面试常见问题

30 阅读1小时+

[TOC]

常用工具整理

微服务

什么是微服务

微服务架构师一种架构模式或者说是一种架构风格,它提倡将单一应用程序划分成一组小的服务,每个服务运行在其独立的自己的继承中,服务之间互相协调,互相配合。

从技术维度理解:微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个服务提供单个业务功能的服务,一个服务做一件事,从技术角度看就是一种小而独立的处理过程,类似进程概念,能够自行单独启动或销毁,拥有自己独立的数据库。

什么是服务熔断或服务降级?

服务熔断一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施,所以很多地方把熔断亦称为过载保护,熔断该服务的所有调用

服务降级是在服务器压力陡增的情况下,利用有限资源,根据当前业务情况,关闭某些服务接口或者页面,以此释放服务器资源以保证核心任务的正常运行。

微服务有哪些特点

解耦(Decoupling)- 系统内的服务很大程度上是分离的。因此整个应用可以被轻松构建、修改和扩展

组件化(Componentization) - 微服务被视为可以被轻松替换和升级的独立组件

业务能力(Business Capabilities) - 微服务非常简单,专注于单一功能

自治(Autonomy) - 开发人员和团队可以相互独立工作,从而提高效率

持续交付(ContinousDelivery) - 允许频繁发版,通过系统自动化完成对软件的创建、测试和审核,

责任(Responsibility) - 微服务不把程序作为项目去关注。相反,他们将程序视为自己负责的产品

分散治理(Decentralized Governance) - 重点是用正确的工具去做正确的事。这意味着没有任何标准化模式或着技术模式。开发人员可以自由选择最合适的工具来解决自己的问题

敏捷性(Agility) - 微服务支持敏捷开发。任何新功能都可以快速开发并被再次丢弃

SOA和微服务架构之间的主要区别是什么?

| SOA (Service-Oriented Architecture) 面向服务的架构 | 微服务架构 |

| ------------------------------------------------------ | :----------------------------------------------- |

| 遵循“ 尽可能多的共享 ”架构方法 | 遵循“ 尽可能少分享 ”的架构方法 |

| 重要性在于 业务功能 重用 | 重要性在于“ 有界背景 ” 的概念 |

| 他们有 共同的 治理 和标准 | 他们专注于 人们的 合作 和其他选择的自由 |

| 使用 企业服务总线(ESB) 进行通信 | 简单的消息系统 |

| 它们支持 多种消息协议 | 他们使用 轻量级协议 ,如 HTTP / REST 等。 |

| 多线程, 有更多的开销来处理I / O | 单线程 通常使用Event Loop功能进行非锁定I / O处理 |

| 最大化应用程序服务可重用性 | 专注于 解耦 |

| 传统的关系数据库 更常用 | 现代 关系数据库 更常用 |

| 系统的变化需要修改整体 | 系统的变化是创造一种新的服务 |

| DevOps / Continuous Delivery正在变得流行,但还不是主流 | 专注于DevOps /持续交付 |

MySQL

MySQL的索引类型

Mysql目前主要有以下几种索引类型:FULLTEXT,HASH,BTREE,RTREE。

1. FULLTEXT

即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。

全文索引并不是和MyISAM一起诞生的,它的出现是为了解决WHERE name LIKE “%word%"这类针对文本的模糊查询效率较低的问题。

2. HASH

由于HASH的唯一(几乎100%的唯一)及类似键值对的形式,很适合作为索引。

HASH索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是,这种高效是有条件的,即只在“=”和“in”条件下高效,对于范围查询、排序及组合索引仍然效率不高。

3. BTREE

BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中(二叉树),每次查询都是从树的入口root开始,依次遍历node,获取leaf。这是MySQL里默认和最常用的索引类型。

4. RTREE(空间数据索引)

myisam支持空间索引,可以用作地理数据存储,R-tree无须前缀索引。空间索引会从所有维度来索引数据。查询时,可以有效地使用任意维度来组合查询

RTREE在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。

相对于BTREE,RTREE的优势在于范围查找。

索引种类

  • 普通索引:仅加速查询

  • 唯一索引(UNIQUE):加速查询 + 列值唯一(可以有null)

  • 主键索引(PRIMARY):加速查询 + 列值唯一(不可以有null)+ 表中只有一个

  • 组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并

  • 全文索引(FULLTEXT):对文本的内容进行分词,进行搜索,主要用来查找文本中的关键字,而不是直接与索引中的值相比较。

fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。fulltext索引配合match against操作使用,而不是一般的where语句加like。它可以在create table,alter table ,create index使用,不过目前只有char、varchar,text 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多。

MySQL存储引擎有哪些

在这里插入图片描述

| 存储引擎 | 描述 |

| :-------- | :----------------------------------------------------------: |

| ARCHIVE | 用于数据存档的引擎,数据被插入后就不能在修改了,且不支持索引。 |

| CSV | 在存储数据时,会以逗号作为数据项之间的分隔符。 |

| BLACKHOLE | 会丢弃写操作,该操作会返回空内容。 |

| FEDERATED | 将数据存储在远程数据库中,用来访问远程表的存储引擎。 |

| InnoDB | 具备外键支持功能的事务处理引擎 |

| MEMORY | 置于内存的表 |

| MERGE | 用来管理由多个 MyISAM 表构成的表集合 |

| MyISAM | 主要的非事务处理存储引擎 |

| NDB | MySQL 集群专用存储引擎 |

存储引擎InnoDB和MyISAM区别

  1. 存储结构

**MyISAM:**每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。 .frm文件存储表定义。数据文件的扩展名为.MYD(MYData)。索引文件的扩展名是.MYI(MYIndex)。

**InnoDB:**所在的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB。

  1. 存储空间

**MyISAM:**可被压缩,存储空间较小。支持三种不同的存储格式:静态表(默认,但是注意数据末尾不能有空格,会被去掉)、动态表、压缩表。

**InnoDB:**需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。

  1. 事物支持

MyISAM: 强调的是性能,每次查询具有原子性,其执行速度比Innodb类型更快,不支持事物

InnoDB: 提供事务支持,外部键等高级数据库功能。具有事务(commit)、回滚(rollback)和崩溃修复能力(crach recovery capabilities)的事务安全(transaction-safe ACID compliant)型表。

  1. 锁机制

**MyISAM:**表级锁

**InnoDB:**行级锁和表级锁

  1. 外键

MyISAM: 不支持。

**InnoDB:**支持。

  1. CURD操作

MyISAM: 如果执行大量的select, MyISAM是更好的选择。(因为没有支持行级锁),在增删的时候需要锁定整个表格,效率会低一些。相关的是innoDB支持行级锁,删除插入的时候只需要锁定该行就行,效率较高。

InnoDB:如果你的数据执行大量的insert或update,出于性能方面的考虑,应该使用InnoDB表。Delete从性能上Innodb更优,但delete from table时,InnoDB不会重新建立表,而是一行一行的删除,在innodb上如果要清空保存有大量数据的表,最好使用truncate table这个命令。

InnoDB和MyISAM的索引的实现方式

MyISAM基于非聚簇索引

索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。

主键索引和辅助索引一致,只是主索引要求key是唯一的,而辅助索引的key可以重复。

InnoDB基于聚簇索引

  1. 主键索引

InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。

  1. 辅助索引

InnoDB的辅助索引data域存储相应记录主键的值而不是数据地址。

聚簇索引与非聚簇索引(二级索引)

  • **聚簇索引:**将数据存储与索引放到了一块,找到索引也就找到了数据

  • **非聚簇索引:**将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行。MyISAM通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因。

事务的四个隔离级别

  • read uncommitted(读未提交)

  • read committed(读已提交)

  • repeatable read(可重复读):InnoDB的默认隔离级别

  • serializable(串行)

img

事务的四个特性(ACID)

(1)**原子性(Atomicity):**即不可分割性,事务要么全部被执行,要么就全部不被执行。

(2)**一致性(Consistency):**事务的执行使得数据库从一种正确状态转换成另一种正确状态

(3)**隔离性(Isolation):**在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务,

(4) **持久性(Durability):**事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。

事务并发一般都会产生哪些问题?

**更新丢失(Lost Update):**当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题,最后的更新覆盖了由其他事务所做的更新。

解决办法:加锁,更新数据加锁排它锁

**脏读(Dirty Reads):**一个事务正在对一条记录做修改,在这个事务没有提交前, 这条记录的数据就一直处于不确定状态; 这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些脏数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这种现象被形象地叫做脏读。

解决方法 : 把数据库的事务隔离级别调整到read commited。

**不可重复读(Non-Repeatable Reads):**一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读” 。

解决办法:把数据库的事务隔离级别调整到 REPEATABLE READ , 读取时候不允许其他事务修改该数据,不管数据在事务过程中读取多少次,数据都是一致的。

幻读(Phantom Reads): 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读” 。

如何避免:Repeatable read及以上级别通过间隙锁来防止幻读的出现,即锁定特定数据的前后间隙让数据无法被插入。

MySQL 事务实现原理是什么?

事务的实现是基于数据库的存储引擎,不同的存储引擎对事务的支持程度不一样。MySQL 中支持事务的存储引擎有InnoDB 和 NDB。

InnoDB 是高版本 MySQL 的默认的存储引擎,因此就以 InnoDB 的事务实现为例,==InnoDB 是通过多版本并发控制(MVCC,Multiversion Concurrency Control )解决不可重复读问题,加上间隙锁(也就是并发控制)解决幻读问题==。

因此 InnoDB 的 RR 隔离级别其实实现了串行化级别的效果,而且保留了比较好的并发性能。事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志实现。

如何设置 MySQL 的事务隔离级别?

MySQL 事务隔离级别 MySQL.cnf 文件里设置的(默认目录 /etc/my.cnf),在文件的文末添加配置:transaction-isolation = REPEATABLE-READ

READ-UNCOMMITTED/READ-COMMITTED/REPEATABLE-READ/SERIALIZABLE

或:set global | session tx_isolation=‘READ-COMMITTED’;命令修改隔离级别。

MySQL> set global transaction isolation level read committed; // 设置全局事务隔离级别为 read committed

MySQL> set session transaction isolation level read committed; // 设置当前会话事务隔离级别为 read committed

如何做 MySQL 的性能优化?

(1)设计符合范式的数据库。

(2)选择合适的存储引擎。

(2)SQL语句优化;

(3)索引优化:高分离字段建立索引。

(4)SQL表结构、字段优化,如枚举类型代替字符串类型。

(5)数据库参数优化:IO参数、CPU参数。

(6)延迟加载、设置缓存与缓存参数优化。

(7)分库分表:垂直切分与水平切分。


垂直切分是根据业务来拆分数据库,同一类业务的数据表拆分到一个独立的数据库,另一类的数据表拆分到其他数据库。

水平切分是按照某个字段的某种规则,把数据切分到多张数据表。一张数据表化整为零,拆分成多张数据表,这样就可以起到缩表的效果了。

(8)分区:将表的数据按照特定的规则放在不同的分区,提高磁盘的IO效率,提高数据库的性能。

(9)主从复制与读写分离:三个主要线程与bin-log文件、relay_log文件,主数据库负责写操作,从数据库负责读操作。

(10)负载均衡。

(11)数据库集群。

(12)硬件。

SQL语句怎么优化

  1. 建索引

  2. 减少表之间的关联

  3. 优化 sql,尽量让 sql 很快定位数据,不要让sql 做全表查询,应该走索引,把排除数据量大的条件排在前面

  4. 简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据

  5. 尽量用PreparedStatement 来查询,不要用 Statement

不要在列上使用函数和进行运算,这将导致索引失效而进行全表扫描。

尽量避免使用 != 或 not in或 <> 等否定操作符

尽量避免使用 or 来连接条件

多个单列索引并不是最佳选择,复合索引的最左前缀原则

查询中的某个列有范围查询,则其右边所有列都无法使用索引优化查找。select in 放在最后

索引不会包含有NULL值的列

查询条件左右两侧类型不匹配的时候会发生隐式转换datetimetimestamp),隐式转换带来的影响就是可能导致索引失效而进行全表扫描。

like 语句的索引失效问题

SQL 优化排查步骤

  1. 先运行看看是否真的很慢,注意设置 SQL_NO_CACHE

  2. where条件单表查,锁定最小返回记录表。

这句话意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高

  1. explain查看执行计划,是否与预期一致(从锁定记录较少的表开始查询)

  2. order by limit 形式的sql语句让排序的表优先查

  3. 了解业务方使用场景

  4. 检查索引执行效果

  5. 观察结果,不符合预期继续从0分析

什么是存储过程?用什么来调用?

blog.csdn.net/z1729734271…

存储过程是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它

存储过程是一个预编译的 SQL 语句, 使用存储过程比单纯 SQL 语句执行要快。

调用:

​ 1)可以用一个命令对象来调用存储过程。

​ 2)可以供外部程序调用,比如: java 程序

内连接、自连接、外连接(左、右、全)、交叉连接的区别

内连接:只有两个元素表相匹配的才能在结果集中显示。

外连接:

​ 左外连接:左边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。

​ 右外连接:右边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。

​ 全外连接:连接的表中不匹配的数据全部会显示出来。

交叉连接: 笛卡尔效应,显示的结果是链接表数的乘积。

大表分页查询,10亿行数据,查找第N页数据,怎么优化

  1. 数据分区

  2. 主键设置自增长,根据查询的页数和查询的记录数可以算出查询的 id 的范围,可以使用 id between and 来查询。

线上环境一亿数据量,未分区,如何增加索引

  1. 使用Alter Table Add Index 而不是create index

  2. 利用主从表,分开升级,不影响业务正常使用

  3. shell脚本后台执行,防止超时终止

  4. 计算好需要的存储空间

  5. 做好数据快照

  6. 可以利用创建临时表

B+树和B树区别,优缺点

B树每个节点都存储key和data,所有节点组成这棵树,并且叶子节点指针为null。查询快

B+树只有叶子节点存储data,叶子节点包含了这棵树的所有键值,叶子节点不存储指针,顺序访问指针,也就是每个叶子节点增加一个指向相邻叶子节点的指针。插入及更新快

MySQL为什么选用B+Tree做索引

B树B-树和B+树总结

  1. B+树的磁盘读写代价更低:==B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小==,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。

  2. B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。==所有关键字查询的路径长度相同,导致每一个数据的查询效率相当==。

  3. 由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引

  1. b+树的数据都集中在叶子节点。分支节点 只负责索引。 b树的分支节点也有数据 。 ==b+树的层高 会小于 B树 平均的Io次数会远大于 B+树==。每个结点存储M/2到M个关键字(B-Tree)加上一个叶子节点的双向链表。
  1. b+树更擅长范围查询。叶子节点 数据是按顺序放置的双向链表。 b树范围查询只能中序遍历。
  1. 索引节点没有数据。比较小。b+树可以把索引完全加载至内存中。

MySQL的锁

![mysql][1]

悲观锁和乐观锁

乐观锁( Optimistic Locking):对加锁持有一种乐观的态度,即先进行业务操作,不到最后一步不进行加锁,"乐观"的认为加锁一定会成功的,在最后一步更新数据的时候再进行加锁。应用级别

实现:大多数基于数据版本(Version)记录机制实现

具体可通过给表加一个版本号或时间戳字段实现,当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断当前版本信息与第一次取出来的版本值大小,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据,拒绝更新,让用户重新操作。

update table set x=x+1, version=version+1 where id=#{id} and version=#{version};

CAS操作方式:即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。

悲观锁(Pessimistic Lock):悲观锁对数据加锁持有一种悲观的态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)数据库级别

大多数情况下依靠数据库的锁机制实现

一般使用 select ...for update 对所选择的数据进行加锁处理,例如select * from account where name=”Max” for update, 这条sql 语句锁定了account 表中所有符合检索条件(name=”Max”)的记录。本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。

共享锁(S锁、读锁)/排他锁(X锁、写锁、独占锁、互斥锁)

共享锁是一个事务并发读取某一行记录所需要持有的锁,比如select ... in share mode;

排他锁是一个事务并发更新或删除某一行记录所需要持有的锁,比如select ... for update。

S锁 和 X锁是可以是表锁,也可以是行锁

锁模式

blog.csdn.net/chiqincong8…

记录锁:单行记录上的锁,记录锁是锁住记录,锁住索引记录,而不是真正的数据记录

**间隙锁:**锁定记录之间的范围,但不包含记录本身。比如查询字段区间为1-5,即1-5内的记录行都会被锁住,2、3、4 的数据行的会被阻塞,但是 1 和 5 两条记录行并不会被锁住。

临键锁Next Key Lock: 记录锁+ 间隙锁,锁定一个范围,包含记录本身。

假设某表age字段下有:5,10,15,20

该表中 age列潜在的临键锁有:

(-∞, 5],

(5, 10],

(10, 15],

(15, 20],

(20, +∞],

在事务 A 中修改年龄为5的记录。之后如果在事务 B 中执行插入年龄为8的数据,便会被阻塞。

意向锁( Intention Locks )

​ InnoDB为了支持多粒度(表锁与行锁)的锁并存,引入意向锁。意向锁是表级锁,

​ IS: 意向共享锁

​ IX: 意向排他锁

插入意向锁(Insert Intention Lock)

插入意向锁,也是间隙锁的一种,在插入记录前,插入操作会先在所插入的索引范围内设置一个插入意向锁。插入意向锁和插入意向锁之间是兼容的,只要插入的键值不同,就不会相互阻塞,其目的就是为了使在同一个gap中不同事务插入不同键值的数据时,不会相互阻塞。但是如果一个区间已经获取了间隙锁或临键锁,会阻塞插入意向锁的获取。

​ Gap Lock中存在一种插入意向锁,在insert操作时产生。

​ 有两个作用:

​ 和next-key互斥,阻塞next-key 锁,防止插入数据,这样就不会幻读。

​ 插入意向锁互相是兼容的,允许相同间隙、不同数据的并发插入

锁粒度

1) 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。适用于频繁读取

2) 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

3) 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般 。

MyISAM和MEMORY存储引擎采用的是表级锁(table-level-locking);BDB存储引擎采用的是页面锁(page-level-locking),同时也支持表级锁;InnoDB存储引擎既支持行级锁,也支持表级锁,默认情况下是采用行级锁

数据库范式

第一范式

属性不可拆分 或 无重复的列

第二范式

就是有一个唯一主键,并且非主属性对候选键是完全依赖。

第三范式

消除传递依赖,数据库中的非主属性仅能依赖于候选键,不存在与其他非主属性的关联

BC范式

其定义是:在关系模式中每一个决定因素都包含候选键,也就是说,==只要属性或属性组A能够决定任何一个属性B,则A的子集中必须有候选键==。BCNF范式排除了任何属性(不光是非主属性,2NF和3NF所限制的都是非主属性)对候选键的传递依赖与部分依赖。

候选键存在多个属性时,多个主属性直接要消除传递依赖关系

(1)所有非主属性对每一个码都是完全函数依赖;

(2)所有的主属性对于每一个不包含它的码,也是完全函数依赖;

(3)没有任何属性完全函数依赖于非码的任意一个组合。

MongoDB

什么是NoSQL数据库

NoSQL是非关系型数据库,NoSQL = Not Only SQL。

关系型数据库采用的结构化的数据,NoSQL采用的是键值对的方式存储数据。

在处理非结构化/半结构化的大数据时;在水平方向上进行扩展时;随时应对动态增加的数据项时可以优先考虑使用NoSQL数据库。

mongodb成为最好nosql数据库的原因是什么?

面向文件的 高性能 高可用性 易扩展性 丰富的查询语言

MongoDB介绍

MongoDB是一个文档数据库,提供好的性能,领先的非关系型数据库。采用BSON存储文档数据。

BSON()是一种类json的一种二进制形式的存储格式,简称Binary JSON.

相对于json多了date类型和二进制数组。

MongoDB适用场景

文档管理、事件的记录,内容管理或者博客平台等等。

MongoDB和MySQL的区别

| 数据库 | MongoDB | MySQL |

| ------------ | ---------------------------------------------------- | ---------------------------- |

| 数据库模型 | 非关系型 | 关系型 |

| 存储方式 | 以类JSON的文档的格式存储 | 不同引擎有不同的存储方式 |

| 查询语句 | MongoDB查询方式(类似JavaScript的函数) | SQL语句 |

| 数据处理方式 | 基于内存,将热数据存放在物理内存中,从而达到高速读写 | 不同引擎有自己的特点 |

| 成熟度 | 新兴数据库,成熟度较低 | 成熟度高 |

| 广泛度 | NoSQL数据库中,比较完善且开源,使用人数在不断增长 | 开源数据库,市场份额不断增长 |

| 事务性 | 仅支持单文档(事务)操作,弱一致性,不支持事务 | 支持事务操作 |

| 占用空间 | 占用空间大 | 占用空间小 |

| join操作 | MongoDB没有join | MySQL支持join |

MongoDB优势与劣势

优势:

1、在适量级的内存的MongoDB的性能是非常迅速的,它将==热数据存储在物理内存中,使得热数据的读写变得十分快==。

2、MongoDB的高可用和集群架构拥有十分==高的扩展性==。

3、在副本集中,当主库遇到问题,无法继续提供服务的时候,副本集将选举一个新的主库继续提供服务。

4、MongoDB的Bson和JSon格式的数据十分适合文档格式的存储与查询。

  • 面向文档的存储:以 JSON 格式的文档保存数据。

  • 任何属性都可以建立索引。

  • 复制以及高可扩展性。

  • 自动分片。

  • 丰富的查询功能。

  • 快速的即时更新。

劣势:

1、 不支持事务操作。MongoDB本身没有自带事务机制,若需要在MongoDB中实现事务机制,需通过一个额外的表,从逻辑上自行实现事务。

2、 应用经验少,由于NoSQL兴起时间短,应用经验相比关系型数据库较少。

3、MongoDB占用空间过大。

Monogodb 中的分片什么意思

分片是将数据水平切分到不同的物理节点。当应用数据越来越大的时候,数据量也会越来越大。当数据量增长

时,单台机器有可能无法存储数据或可接受的读取写入吞吐量。利用分片技术可以添加更多的机器来应对数据量增加

以及读写操作的要求。

为什么要在MongoDB中使用分析器

mongodb中包括了一个可以显示数据库中每个操作性能特点的数据库分析器.通过这个分析器你可以找到比预期慢

的查询(或写操作);利用这一信息,比如,可以确定是否需要添加索引.

MongoDB支持哪些数据类型

  • String

  • Integer

  • Double

  • Boolean

  • Object

  • Object ID:用于存储文档id,一共有四部分组成:时间戳、客户端ID、客户进程ID、三个字节的增量计数器

  • Arrays

  • Min/Max Keys

  • Datetime

  • Code:"Code"类型用于在文档中存储 JavaScript 代码。

  • Regular Expression:类型用于在文档中存储正则表达式

在MongoDB中什么是副本集(避免单点故障)

在MongoDB中副本集由一组MongoDB实例组成,包括一个主节点多个次节点,MongoDB客户端的所有数据都

写入主节点(Primary),副节点从主节点同步写入数据,以保持所有复制集内存储相同的数据,提高数据可用性。

如何理解MongoDB中的GridFS机制,MongoDB为何使用GridFS来存储文件?

GridFS是一种将大型文件存储在MongoDB中的文件规范。使用GridFS可以将大文件分隔成多个小文档存放,这样我们能够有效的保存大文档,而且解决了BSON对象有限制的问题。

更新一个正在被迁移的块(chunk)上的文档时会发生什么?

更新操作会立即发生在旧的分片(shard)上,然后更改才会在所有权转移(ownership transfers)前复制到新的分片上.

数据在什么时候才会扩展到多个分片(shard)里?

mongodb 分片是基于区域(range)的.所以一个集合(collection)中的所有的对象都被存放到一个块(chunk)中.只有当存在多余一个块的时候,才会有多个分片获取数据的选项.现在,每个默认块的大小是 64mb,所以你需要至少 64 mb 空间才可以实施一个迁移.

如果在一个分片(shard)停止或者很慢的时候,我发起一个查询会怎样?

如果一个分片(shard)停止了,除非查询设置了“partial”选项,否则查询会返回一个错误.如果一个分片(shard)响应很慢,mongodb则会等待它的响应.

如果块移动操作(movechunk)失败了,我需要手动清除部分转移的文档吗?

不需要,移动操作是一致(consistent)并且是确定性的(deterministic);一次失败后,移动操作会不断重试;当完成后,数据只会出现在新的分片里(shard).

可以把movechunk目录里的旧文件删除吗?

没问题,这些文件是在分片(shard)进行均衡操作(balancing)的时候产生的临时文件.一旦这些操作已经完成,相关的临时文件也应该被删除掉.但目前清理工作是需要手动的,所以请小心地考虑再释放这些文件的空间.

Redis

Redis应用场景

token保存、session共享、分布式锁、自增id、验证码、订阅发布、HyperLog基数统计、GEO地理位置信息存储等

Redis数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

Redis有序集合的底层实现

ziplist编码

ziplist编码的有序集合对象底层实现是压缩列表,其结构与哈希对象类似,不同的是两个紧密相连的压缩列表节点,第一个保存元素的成员,第二个保存元素的分值,而且分值小的靠近表头,大的靠近表尾

skiplist编码

skiplist编码的有序集合对象底层使用 zet 结构作为底层实现,一个 zset 结构同时包含一个字典和一个跳表;

字典的键保存元素的值,字典的值则保存元素的分值;

跳跃表节点的 object 属性保存元素的成员,跳跃表节点的 score 属性保存元素的分值。

为何skiplist编码要同时使用跳跃表和字典实现?
  • 跳跃表优点是有序,但是查询分值复杂度为O(logn);字典查询分值复杂度为O(1) 但是无序,所以结合连两个结构的优点进行实现。

  • 虽然采用两个结构但是集合的元素成员和分值是共享的,两种结构通过指针指向同一地址,不会浪费内存。

单线程的redis为什么这么快 / 为什么单线程就能hold住几万qps

(一)纯内存操作

(二)单线程操作,避免了频繁的上下文切换

(三)采用了非阻塞I/O多路复用机制

I/O复用,Reactor 设计模式

blog.csdn.net/seujava_er/…

Redis 为什么是单线程的

Redis利用队列技术将并发访问变为串行访问

1)绝大部分请求是纯粹的内存操作(非常快速)

2)采用单线程,避免了不必要的上下文切换和竞争条件

3)非阻塞IO优点:

  1. 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
  1. 支持丰富数据类型,支持string,list,set,sorted set,hash
  1. 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
  1. 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除如何解决redis的并发竞争key问

Redis 持久化机制

RDB是根据指定的规则定时将内存中的数据备份到硬盘上,

AOF是在每次执行命令后命令本身记录下来,所以RDB的备份文件是一个二进制文件,而AOF的备份文件是一个文本文件

Redis 内部结构

  • dict 本质上是为了解决算法中的查找问题(Searching)是一个用于维护key和value映射关系的数据结构,与很多语言中的Map或dictionary类似。 本质上是为了解决算法中的查找问题(Searching)

  • sds sds就等同于char * 它可以存储任意二进制数据,不能像C语言字符串那样以字符’\0’来标识字符串的结 束,因此它必然有个长度字段。

  • skiplist (跳跃表) 跳表是一种实现起来很简单,单层多指针的链表,它查找效率很高,堪比优化过的二叉平衡树,且比平衡树的实现,

  • quicklist

  • ziplist 压缩表 ziplist是一个编码后的列表,是由一系列特殊编码的连续内存块组成的顺序型数据结构,

redis的过期策略以及内存淘汰机制

redis采用的是定期删除+惰性删除策略,定期随机抽取清理+访问判断清理,最后采用缓存清理机制(默认最近最少使用LRU)

但是这种方式不能彻底清理,所以需要设置==内存淘汰机制,一般设置LRU(volatile-lru(有过期时间缓存的清理)allkeys-lru(全部缓存的清理))==

为什么不用定时删除策略?

定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.

定期删除+惰性删除是如何工作的呢?

定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。

于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。

采用定期删除+惰性删除就没其他问题了么?

不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。

在redis.conf中有一行配置


maxmemory-policy allkeys-lru

该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据,新写入操作会报错

ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

Redis 集群方案

www.jianshu.com/p/53a9f9897…

www.cnblogs.com/kaleidoscop…

  1. 客户端分片

分区的逻辑在客户端实现,由客户端自己选择请求到哪个节点。方案可参考一致性哈希,这种方案通常适用于用户对客户端的行为有完全控制能力的场景。

  1. 代理分片

分片工作交给代理服务完成

  1. Twemproxy代理

大概概念是,它类似于一个代理方式, 使用时在本需要连接 redis 的地方改为连接 twemproxy, 它会以一个代理的身份接收请求并使用一致性 hash 算法,将请求转接到具体 redis,将结果再返回 twemproxy。

缺点: twemproxy 自身单端口实例的压力,使用一致性 hash 后,对 redis 节点数量改变时候的计算值的改变,数据无法自动移动到新的节点。

  1. Sentinel哨兵模式

由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器以及这些主服务器下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。

  1. codis

目前用的最多的集群方案,基本和 twemproxy 一致的效果,但它支持在节点数量改变情况下,旧节点数据可恢复到新 hash 节点

  1. redis cluster3.0 自带的集群

特点在于他的分布式算法不是一致性 hash,而是 hash 槽的概念,以及自身支持节点设置从节点。

codis 类似于cluster 也是利用一致性hash及solt分片概念实现

有没有尝试进行多机redis 的部署?如何保证数据一致的?

主从复制,读写分离

一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。

Redis线程模型

文件事件处理器包括分别是套接字、 I/O 多路复用程序、 文件事件分派器(dispatcher)、 以及事件处理器。使用 I/O 多路复用程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字

Redis事务

redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的

Redis会将一个事务中的所有命令序列化,然后按顺序执行。

1.redis 不支持回滚“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。

2.如果在一个事务中的命令出现错误,那么所有的命令都不会执行;

3.如果在一个事务中出现运行错误,那么正确的命令会被执行。

注:redis的discard只是结束本次事务,正确命令造成的影响仍然存在.

1)MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。

2)EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。

3)通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。

4)WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。

pipline用来干嘛

pipeline的作用是将一批命令进行打包,然后发送给服务器,服务器执行完按顺序打包返回。

Memcache与Redis的区别都有哪些?

  1. 存储方式

Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。

Redis有部份存在硬盘上,redis可以持久化其数据

灾难恢复–memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复

存储数据安全–memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化)

不过memcache还可用于缓存其他东西,例如图片、视频等等;

  1. 数据支持类型

memcached所有的值均是简单的字符串,

redis作为其替代者,支持更为丰富的数据类型 ,提供list,set,zset,hash等数据结构的存储

  1. 使用底层模型不同

它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。

Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

  1. value 值大小不同

Redis 最大可以达到 512M;

memcache 只有 1mb。

  1. Redis支持数据的备份,即master-slave模式的数据备份。

  2. 应用场景不一样:

Redis出来作为NoSQL数据库使用外,还能用做消息队列、数据堆栈和数据缓存等;

Memcached适合于缓存SQL语句、数据集、用户临时性数据、延迟查询数据和session等。

Memcached

1、分布式应用。由于memcached本身基于分布式的系统,所以尤其适合大型的分布式系统。

2、数据库前段缓存。

3、服务器间数据共享。

  1. 过期策略

memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10;

为什么Redis没有彻底干掉Memcache

  1. 项目中使用缓存存储时,只会对String数据结构进行存储,但redis在存储String类型时,会耗费更多的内存,我们需要对数据转换为dict来进行存储的压缩,从而减低内存损耗,在这种情况下,我会优先考虑memcache。

  2. redis只支持单线程,其性能受限于CPU性能,即取决于数据结构、数据大小以及服务器硬件性能,其在日常环节中的QPS高峰约为1-2w,而memcache具有多核优势,其单实例的吞吐量极高,性能主要取决于存储的key及value的字节大小以及服务器硬件性能,其在日常环节中的QPS高峰约为4-6w。(但有意思的时,即使如此,memcache的性能比起redis,在实际业务中并没有好多少,但如果看过源码的人会发现,redis的源码及其精致!而memcache的源码则稍显臃肿,可能这一块也会有一定的影响呢?)

Redis为什么是key,value的,为什么不是支持SQL的?

因为redis的内存模型是一个hashtable,不使用表来存储数据,也不会预定义或强制要求用户对redis储存的不同数据进行关联。

Redis是多线程还是单线程?

redis中io多路复用器模块是单线程执行,事件处理器也是单线程执行,两个线程不一样。所以实际redis应该是单进程多线程,只是不同的模块都用的单线程实现。

两个维度来举例:

(1)若是client发送命令到server的话,server处理命令是单线程逐条进行的。

(2)server内部可以是多线程的,比如aof持久化,假设策略每秒,那就是再单独开启一个线程去执行aof文件持久化操作,这就是多线程了。

Redis如果做集群该如何规划?AKF/CAP如何实现和设计?

参考redis各种部署方式的优缺点来决定。

如果希望快速部署,那么可以考虑单节点部署方式。

如果只需要考虑可靠性,那么可以考虑主从复制模式。

如果想要保证高可用,不需要考虑储存成本可以考虑哨兵模式。

如果想提高集群的扩展性和可用性,不要求保证数据的强一致性,且没有批量操作,那么可以考虑集群模式。

Redis 的持久化开启了RDB和AOF下,重启服务是如何加载的

1) AOF持久化开启且存在AOF文件时,优先加载AOF文件,

2) AOF关闭或者AOF文件不存在时,加载RDB文件,

3) 加载AOF/RDB文件成功后,Redis启动成功。

4) AOF/RDB文件存在错误时,Redis启动失败并打印错误信息

10万用户一年365天的登录情况如何用redis存储,并快速检索任意时间窗内的活跃用户?

bitmap+集群的方式,直接看下文会大彻大悟。

Redis的bitmap从基础到业务

Redis的5种Value类型实现原理

说下原理吧

string:int、raw、embstr

list:ziplist、linkedlist

hash:ziplist、hashtable

set:intset、hashtable

sorted set:ziplist、skiplist+dict

100万并发4G数据,10万并发400G数据,如何设计Redis存储方式?

我肤浅的理解为:

前者用主从+哨兵进行高可用,加快读请求的速度,减轻单节点的压力。

一文掌握Redis的主从复制原理到实战

一文掌握Redis的哨兵Sentinel原理到实战

后者用集群来均分这400G数据。 但是感觉不是面试官想要的答案。

缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题

缓存雪崩

我们可以简单的理解为:由于原有缓存失效,新缓存未加载

(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

解决方案

大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开。

缓存穿透

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。

解决方案

最常见的则是采用布隆过滤器,==将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。==

另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

5TB的硬盘上放满了数据,请写一个算法将这些数据进行排重。如果这些数据是一些32bit大小的数据该如何解决?如果是64bit的呢?对于空间的利用到达了一种极致,那就是Bitmap和布隆过滤器(Bloom Filter)。

Bitmap: 典型的就是哈希表

缺点是,Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的空间、时间来完成了。

布隆过滤器(推荐)

就是引入了k(k>1)k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。

它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。

Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。

Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。

缓存击穿

是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据。

解决方案

在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。

缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决思路:

  1. 直接写个缓存刷新页面,上线时手工操作下;

  2. 数据量不大,可以在项目启动的时候自动进行加载;

  3. 定时刷新缓存;

缓存更新

除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

(1)定时去清理过期的缓存;

(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。

缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

以参考日志级别设置预案:

  1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;

  2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;

  3. 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;

  4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

缓存雪崩、缓存穿透与缓存击穿的区别

缓存雪崩:原有缓存失效或者过期,但是新缓存未加载,导致大批量查询数据库

缓存穿透:缓存和持久化数据库中都没有,而用户多次请求

缓存击穿:缓存中没有但是持久化数据库中存在,用户多次请求需要访问数据库

Kafka

image.png

image.png

介绍

Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。

主要应用场景是:日志收集系统和消息系统。

常见面试知识点

Kafka面试整理

Kafka常见的面试题

Kafka Leader选举机制

Kafka 名词解释

broker

Kafka 集群包含一个或多个服务器,服务器节点称为broker。

Topic

每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。类似于数据库的表名

Partition

topic中的数据分割为一个或多个partition。每个topic至少有一个partition。每个partition中的数据使用多个segment文件存储。partition中的数据是有序的,不同partition间的数据丢失了数据的顺序。如果topic有多个partition,消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1。

Leader

每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。

Follower

Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower。

为什么选择Kafka,Kafka的特点

**高吞吐量:**Kafka 每秒可以生产约 25 万消息(50 MB),每秒处理 55 万消息(110 MB)

**持久化数据存储:**可进行持久化操作。将消息持久化到磁盘,因此可用于批量消费,例如 ETL,以及实时应用程序。通过将数据持久化到 硬盘以及replication 防止数据丢失。

**分布式系统易于扩展:**所有的 producer、broker 和 consumer 都会有多个,均为分布式的。无需停机即可扩展机器。

**客户端状态维护:**消息被处理的状态是在 consumer 端维护,而不是由 server 端维护。当失败时能自动平衡。

为什么要使用消息队列

**缓冲和削峰:**上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka在中间可以起到一个缓冲的作用,把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。

**解耦和扩展性:**项目开始的时候,并不能确定具体需求。消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守约定,针对数据编程即可获取扩展能力。

**冗余:**可以采用一对多的方式,一个生产者发布消息,可以被多个订阅topic的服务消费到,供多个毫无关联的业务使用。

**健壮性:**消息队列可以堆积请求,所以消费端业务即使短时间死掉,也不会影响主要业务的正常进行。

**异步通信:**很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么

ISR: In-Sync Replicas 副本同步队列

AR: Assigned Replicas 所有副本

ISR是由leader维护,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度, 当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。

kafka中的broker 是干什么的

broker 是消息的代理,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉取指定Topic的消息,然后进行业务处理,broker在中间起到一个代理保存消息的中转站。

什么情况下一个 broker 会从 isr中踢出去

leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护 ,如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除 。

kafka 为什么那么快

  • Cache Filesystem Cache PageCache缓存
  • 顺序写 由于现代的操作系统提供了预读和写技术,磁盘的顺序写大多数情况下比随机写内存还要快。
  • Zero-copy 零拷技术减少拷贝次数
  • Batching of Messages 批量量处理。合并小的请求,然后以流的方式进行交互,直顶网络上限。
  • Pull 拉模式 使用拉模式进行消息的获取消费,与消费端处理能力相符。

1)顺序读写

kafka的消息是不断追加到文件中的,这个特性使kafka可以充分利用磁盘的顺序读写性能,顺序读写不需要硬盘磁头的寻道时间,只需很少的扇区旋转时间,所以速度远快于随机读写。

2)零拷贝

利用Linux kernel"零拷贝(zero-copy)"系统调用机制,就是跳过“用户缓冲区”的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到“用户态缓冲区”。

3)分区

kafka中的topic中的内容可以被分为多分区存在,每个分区又分为多个段,所以每次操作都是针对一小部分做操作,很轻便,并且增加并行操作的能力。

4)批量发送

kafka允许进行批量发送消息,producter发送消息的时候,可以将消息缓存在本地,等到了固定条件发送到kafka等消息条数到固定条数,一段时间发送一次。

5)数据压缩

Kafka还支持对消息集合进行压缩,Producer可以通过GZIP或Snappy格式对消息集合进行压缩

压缩的好处就是减少传输的数据量,减轻对网络传输的压力

kafka producer如何优化打入速度

  • 增加线程
  • 提高 batch.size
  • 增加更多 producer 实例
  • 增加 partition 数
  • 设置 acks=-1 时,如果延迟增大:可以增大 num.replica.fetchers(follower 同步数据的线程数)来调解;
  • 跨数据中心的传输:增加 socket 缓冲区设置以及 OS tcp 缓冲区设置。

为什么Kafka不支持读写分离?

在 Kafka 中,生产者写入消息、消费者读取消息的操作都是与 leader 副本进行交互的,从 而实现的是一种主写主读的生产消费模型。

Kafka 并不支持主写从读,因为主写从读有 2 个很明 显的缺点:

  • (1)数据一致性问题。数据从主节点转到从节点必然会有一个延时的时间窗口,这个时间 窗口会导致主从节点之间的数据不一致。某一时刻,在主节点和从节点中 A 数据的值都为 X, 之后将主节点中 A 的值修改为 Y,那么在这个变更通知到从节点之前,应用读取从节点中的 A 数据的值并不为最新的 Y,由此便产生了数据不一致的问题。
  • (2)延时问题。类似 Redis 这种组件,数据从写入主节点到同步至从节点中的过程需要经 历网络→主节点内存→网络→从节点内存这几个阶段,整个过程会耗费一定的时间。而在 Kafka 中,主从同步会比 Redis 更加耗时,它需要经历网络→主节点内存→主节点磁盘→网络→从节 点内存→从节点磁盘这几个阶段。对延时敏感的应用而言,主写从读的功能并不太适用。

如何保证消息队列的顺序

mp.weixin.qq.com/s/sZQ7z_ZCN…

消息队列对比

| 特性 | ActiveMQ | RabbitMQ | Kafka | RocketMQ |

| ----------------- | -------------- | ---------- | ---------------- | -------------- |

| PRODUCER-COMSUMER | 支持 | 支持 | 支持 | 支持 |

| PUBLISH-SUBSCRIBE | 支持 | 支持 | 支持 | 支持 |

| REQUEST-REPLY | 支持 | 支持 | - | 支持 |

| API完备性 | 高 | 高 | 高 | 低(静态配置) |

| 多语言支持 | 支持,JAVA优先 | 语言无关 | 支持,JAVA优先 | 支持 |

| 单机呑吐量 | 万级 | 万级 | 十万级 | 单机万级 |

| 消息延迟 | - | 微秒级 | 毫秒级 | - |

| 可用性 | 高(主从) | 高(主从) | 非常高(分布式) | 高 |

| 消息丢失 | - | 低 | 理论上不会丢失 | - |

| 消息重复 | - | 可控制 | 理论上会有重复 | - |

| 文档的完备性 | 高 | 高 | 高 | 中 |

| 提供快速入门 | 有 | 有 | 有 | 无 |

| 首次部署难度 | - | 低 | 中 | 高 |

Docker

什么是Docker?

Docker是一个容器化平台,它以容器的形式将您的应用程序及其所有依赖项打包在一起,以确保您的应用程序在任何环境中无缝运行。

Docker核心解决的问题是利用LXC来实现类似VM的功能,从而利用更加节省的硬件资源提供给用户更多的计算资源。同VM的方式不同, LXC 其并不是一套硬件虚拟化方法 - 无法归属到全虚拟化、部分虚拟化和半虚拟化中的任意一个,而是一个操作系统级虚拟化方法, 理解起来可能并不像VM那样直观。

Docker实现原理

docker使用一种叫做namespace的技术来提供名为container的隔离工作区,当你开始运行一个容器,docker会为该容器创建一组命名空间.

这些命名空间提供了一个隔离层,容器的各个面都被限制在单独的命名空间中运行,并且其访问边界也被限制在相应的命名空间中.

Docker Engine在linux中使用如下的命名空间:

  • pid namespace: 进程隔离(PID: Process ID).

  • net namespace: 管理网络接口,网络隔离

  • ipc namespace: 管理对ipc资源的访问 (IPC: 进程间通信).

  • mnt namespace: 管理文件系统挂载点(MNT:mount)

  • uts namespace: 隔离内核和版本标识符(UTS: Unix Timesharing System).which defines the system name and domain

CI(持续集成)服务器的功能是什么?

CI功能就是在每次提交之后不断地集成所有提交到存储库的代码,并编译检查错误

什么是Docker镜像?

Docker镜像是Docker容器的源代码,Docker镜像用于创建容器。使用build命令创建镜像

什么是Docker容器?

Docker容器包括应用程序及其所有依赖项,作为操作系统的独立进程运行

Docker容器有几种状态?

Docker容器可以有四种状态:

运行

已暂停

重新启动

已退出

Docker使用流程

1)创建Dockerfile后,您可以构建它以创建容器的镜像

2)推送或拉取镜像。

Docker file中最常见的指令是什么?

Docker file中的一些常用指令如下:

FROM:指定基础镜像

LABEL:为镜像指定标签

RUN:运行指定的命令

CMD:容器启动时要运行的命令

Docker file中的命令COPY和ADD命令有什么区别?

COPY与ADD的区别COPY的只能是本地文件,其他用法一致

docker常用命令?

docker pull 拉取或者更新指定镜像

docker push 将镜像推送至远程仓库

docker rm 删除容器

docker rmi 删除镜像

docker images 列出所有镜像

docker ps 列出所有容器

Linux

IO模型

服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:

  1. 同步阻塞IO(Blocking IO):即传统的IO模型。

  2. 同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。

  3. IO多路复用(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO

  4. 异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。

同步和异步的概念描述的是用户线程与内核的交互方式:

同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;

而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。

阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:

阻塞是指IO操作需要彻底完成后才返回到用户空间;

而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。

常用命令行

IP分割

~#>echo "192.168.0.1"|awk -F. '{ print $1" "$2" "$3" "$4 }'

端口占用


~#>lsof -i:端口号

~#>netstat -tunlp|grep 端口号

查看cup负载load


~#>w

~#>top

~#>uptime

查看内存

~#>free

查看磁盘使用量

~#>df -h

查看文件大小

~#>du -h

追踪网络数据包

~#>traceroute[参数][主机]

Linux 运维常用命令Linux 运维常用命令

网络编程

图片描述

img

常见的网络协议

网络层:

ARP(Address Resolution Protocol )地址解析协议,网络层

RARP(Reverse Address Resolution Protocol )逆向地址解析协议,网络层

ICMP(Internet Control Message Protocol) 网络控制报文协议,网络层

应用层:

SMTP( Simple Mail transfer Protocol)邮件传输协议,应用层

NFS(Network File Server)网络文件服务器,应用层

FTP(File Transfer Protocol):远程文件传输协议,应用层

Telnet(baiRemote Login):提供远程登录功能,应用层

HTTP

HTTPS

RPC

传输层:

SCTP (Stream Control Transmission Protocol)流控制传输协议,传输层

UDP(User Datagram Protocol):用户数据包协议,传输层

TCP(Transmission Control Protocol):传输控制协议,传输层

http能不能一次连接多次请求,不等后端返回

http本质上是使用socket连接,因此发送请求,接写入tcp缓冲,是可以多次进行的,这也是http是无状态的原因

http keep-alive

client发出的HTTP请求头需要增加Connection:keep-alive字段

Web-Server端要能识别Connection:keep-alive字段,并且在http的response里指定Connection:keep-alive字段,告诉client,我能提供keep-alive服务,并且"应允"client我暂时不会关闭socket连接

TCP协议中有长连接和短连接之分。短连接在数据包发送完毕后就会自己断开,长连接在发包完毕后,会在一定的时间内保持连接,即通常所说的keepalive功能。

当tcp检测到对端socket不再可用时(不能发出探测包,或探测包没有收到ACK的响应包),select会返回socket可读,并且在recv时返回-1,

同时置上errno为ETIMEDOUT。此时TCP的状态是断开的

connect会阻塞,怎么解决?

connect调用的时候,当发起主动连接的时候,如果服务端关闭,则进程会被connect阻塞,等待较长时间返回。假如直接将connect直接定义为非阻塞的,则无法确定connect是否连接成功。

  1. 建立socket

  2. 将该socket设置为非阻塞模式

  3. 调用connect()

  4. 使用select()检查该socket描述符是否可写

  5. 根据select()返回的结果判断connect()结果

  6. 将socket设置为阻塞模式

滑动窗口协议

即在传输过程中接收方告诉发送方在某一时刻能送多少包(称窗口尺寸)

滑动窗口概念不仅存在于数据链路层,也存在于传输层,两者有不同的协议,但基本原理是相近的。其中一个重要区别是,一个是针对于帧的传送,另一个是字节数据的传送。

滑动窗口(Sliding window)是一种流量控制技术。早期的网络通信中,通信双方不会考虑网络的拥挤情况直接发送数据。由于大家不知道网络拥塞状况,同时发送数据,导致中间节点阻塞掉包,谁也发不了数据,所以就有了滑动窗口机制来解决此问题。参见滑动窗口如何根据网络拥塞发送数据仿真视频。

滑动窗口协议是用来改善吞吐量的一种技术,即容许发送方在接收任何应答之前传送附加的包。接收方告诉发送方在某一时刻能送多少包(称窗口尺寸)。

CP中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。当滑动窗口为0时,发送方一般不能再发送数据报,但有两种情况除外,一种情况是可以发送紧急数据,例如,允许用户终止在远端机上的运行进程。另一种情况是发送方可以发送一个1字节的数据报来通知接收方重新声明它希望接收的下一字节及发送方的滑动窗口大小

什么是回退N帧协议

数据传输出现错误,会回退到出错的帧重新进行传输。

慢开始算法

发送的数据为了避免拥塞,由小到大的增大发送窗口。

TCP与UDP区别

| | TCP | UDP |

| ---------- | ---------------------------- | ----------------------------------------------- |

| 是否连接 | 面向连接 | 面向非连接 |

| 传输可靠性 | 可靠,保证数据正确性及顺序性 | 不可靠,可能丢包,无法保证顺序 |

| 信道类型 | 全双工信道 | 单工信道 |

| 应用场合 | 少量数据 | 传输大量数据 |

| 速度 | 慢 | 快 |

| 数据类型 | TCP面向字节流 | UDP面向报文 |

| 链接类型 | 点到点 | UDP支持一对一,一对多,多对一和多对多的交互通信 |

TCP的优点: 可靠,稳定

TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。 TCP的缺点: 慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。 而且,因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。

UDP的优点: 快

相比TCP,UDP稍安全 。没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。但UDP也是无法避免攻击的,比如:UDP Flood攻击…… UDP的缺点: 不可靠,不稳定 因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。 基于上面的优缺点,那么: 什么时候应该使用TCP: 当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。 在日常生活中,常见使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输 ………… 什么时候应该使用UDP: 当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。 比如,日常生活中,常见使用UDP协议的应用如下: QQ语音 QQ视频 TFTP ……

有些应用场景对可靠性要求不高会用到UPD,比如长视频,要求速率

TCP/IP的三次握手与四次挥手

三次握手

  1. 建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

  2. 服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

  3. 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

四次挥手

  1. 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

  2. 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。

  3. 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。

  4. 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手

为什么需要三次握手?挥手为什么需要四次

三次握手:为了防止已失效链接的请求报文突然又传送到服务端,而产生数据错误

四次挥手:为了防止与下一链接消息重复,及保障已发送的数据正常的接收。

TCP建立连接和断开连接的各种过程中的状态转换细节

客户端:主动打开SYN_SENT--->ESTABLISHED--->主动关闭FIN_WAIT_1--->FIN_WAIT_2--->TIME_WAIT

服务器端:LISTEN(被动打开)--->SYN_RCVD--->ESTABLISHED--->CLOSE_WAIT(被动关闭)--->LAST_ACK--->CLOSED

为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。

所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

  1. 保证TCP协议的链接能够安全可靠的关闭

  2. 保证这次链接的重复数据段从网络中消失。

TCP有几种状态?

tcp有11种状态

CLOSED:初始状态,表示TCP连接是“关闭着的”或“未打开的”。

LISTEN :表示服务器端的某个SOCKET处于监听状态,可以接受客户端的连接。

SYN_RCVD :表示服务器接收到了来自客户端请求连接的SYN报文。在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat很难看到这种状态,除非故意写一个监测程序,将三次TCP握手过程中最后一个ACK报文不予发送。当TCP连接处于此状态时,再收到客户端的ACK报文,它就会进入到ESTABLISHED 状态。

SYN_SENT :这个状态与SYN_RCVD 状态相呼应,当客户端SOCKET执行connect()进行连接时,它首先发送SYN报文,然后随即进入到SYN_SENT 状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT 状态表示客户端已发送SYN报文。

ESTABLISHED :表示TCP连接已经成功建立。

FIN_WAIT_1 :这个状态得好好解释一下,其实FIN_WAIT_1 和FIN_WAIT_2 两种状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:==FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET进入到FIN_WAIT_1 状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2 状态。==当然在实际的正常情况下,无论对方处于任何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1 状态一般是比较难见到的,而FIN_WAIT_2 状态有时仍可以用netstat看到。

FIN_WAIT_2 :上面已经解释了这种状态的由来,实际上FIN_WAIT_2状态下的SOCKET表示半连接,即有一方调用close()主动要求关闭连接。注意:FIN_WAIT_2 是没有超时的(不像TIME_WAIT 状态),这种状态下如果对方不关闭(不配合完成4次挥手过程),那这个 FIN_WAIT_2 状态将一直保持到系统重启,越来越多的FIN_WAIT_2 状态会导致内核crash。

TIME_WAIT :表示收到了对方的FIN报文,并发送出了ACK报文。 TIME_WAIT状态下的TCP连接会等待2*MSL(Max Segment Lifetime,最大分段生存期,指一个TCP报文在Internet上的最长生存时间。每个具体的TCP协议实现都必须选择一个确定的MSL值,RFC 1122建议是2分钟,但BSD传统实现采用了30秒,Linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本机的这个值),然后即可回到CLOSED 可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。(这种情况应该就是四次挥手变成三次挥手的那种情况)

CLOSING :这种状态在实际情况中应该很少见,属于一种比较罕见的例外状态。正常情况下,当一方发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING 状态表示一方发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?那就是当双方几乎在同时close()一个SOCKET的话,就出现了双方同时发送FIN报文的情况,这是就会出现CLOSING 状态,表示双方都正在关闭SOCKET连接。

CLOSE_WAIT :表示正在等待关闭。怎么理解呢?当对方close()一个SOCKET后发送FIN报文给自己,你的系统毫无疑问地将会回应一个ACK报文给对方,此时TCP连接则进入到CLOSE_WAIT状态。接下来呢,你需要检查自己是否还有数据要发送给对方,如果没有的话,那你也就可以close()这个SOCKET并发送FIN报文给对方,即关闭自己到对方这个方向的连接。有数据的话则看程序的策略,继续发送或丢弃。简单地说,当你处于CLOSE_WAIT 状态下,需要完成的事情是等待你去关闭连接。

LAST_ACK :当被动关闭的一方在发送FIN报文后,等待对方的ACK报文的时候,就处于LAST_ACK 状态。当收到对方的ACK报文后,也就可以进入到CLOSED 可用状态了。

CLOSING状态:产生的原因是客户端和服务端同时关闭

TCP头多少字节?哪些字段?

tcp头有20字节,包括选项时会增大,最大不超过60字节。包括:16位源端口号、16位目的端口号、32位的序号、32位确认号、4位头部长度标志位、16位窗口大小、16位校验和、16位紧急指针。

UDP调用connect有什么作用?

  1. 因为UDP可以是一对一,多对一,一对多,或者多对多的通信,所以每次调用sendto()/recvfrom()时都必须指定目标IP和端口号。

通过调用connect()建立一个端到端的连接,就可以和TCP一样使用send()/recv()传递数据,而不需要每次都指定目标IP和端口号。

但是它和TCP不同的是它没有三次握手的过程。

  1. 还可以通过在建立连接的UDP套接字,再次调用connect()实现以下功能:

a. 指定新的IP地址和端口号

b. 断开连接

进程间通讯的方式有哪些,各有什么优缺点

进程间通信

Linux 进程间通信(IPC)以下以几部分发展而来:

早期UNIX进程间通信、基于System V进程间通信、基于Socket进程间通信和POSIX进程间通信。

UNIX进程间通信方式包括:管道、FIFO、信号。

System V进程间通信方式包括:System V消息队列、System V信号灯、System V共享内存、

POSIX进程间通信包括:posix消息队列、posix信号灯、posix共享内存。

现在linux使用的进程间通信方式:

==(1)管道(pipe):==管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程之间使用。进程的亲缘关系通常是指父子进程关系。

==(2 )有名管道(FIFO):==有名管道也是半双工的通信方式,但是允许在没有亲缘关系的进程之间使用,管道是先进先出的通信方式。

==(3)信号(signal):==信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

==(4)消息队列:==消息队列是有消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

==(5)共享内存:== 共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

==(6)信号量:== 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

==(7)套接字(socket):== 套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

epoll与select的区别

  1. 数据大小不同

select在一个进程中打开的最大fd是有限制的,由FD_SETSIZE设置,默认值是2048。

不过 epoll则没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,一般来说内存越大,fd上限越大,1G内存都能达到大约10w左右。

  1. 轮询机制不同

select的轮询机制是系统会去查找每个fd是否数据已准备好,当fd很多的时候,效率当然就直线下降了,

epoll采用基于事件的通知方式,一旦某个fd数据就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,而不需要不断的去轮询查找就绪的描述符,这就是epoll高效最本质的原因。

无论是select还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的,而select则做了不必要的拷贝

epoll中et和lt的区别与实现原理

LT:水平触发,效率会低于ET触发,尤其在大并发,大流量的情况下。但是LT对代码编写要求比较低,不容易出现问题。LT模式服务编写上的表现是:只要有数据没有被获取,内核就不断通知你,因此不用担心事件丢失的情况。

ET:边缘触发,效率非常高,在并发,大流量的情况下,会比LT少很多epoll的系统调用,因此效率高。但是对编程要求高,需要细致的处理每个请求,否则容易发生丢失事件的情况

connect方法会阻塞,请问有什么方法可以避免其长时间阻塞?

最通常的方法最有效的是加定时器;也可以采用非阻塞模式。

Socket是什么?

socket是应用层与传输层的一个抽象,将复杂的TCP/IP协议隐藏在Socket接口之后,只对应用层暴露简单的接口

socket是一种特殊的文件,它也有文件描述符,进程可以打开一个socket,并且像处理文件一样对它进行read()和write()操作,而不必关心数据是怎么在网络上传输的

socket是一个tcp连接的两端

Socket如何唯一标识一个进程?

socket基于tcp协议实现,网络层的ip地址唯一标识一台主机,而传输层的协议+端口号可以唯一标识绑定到这个端口的进程

通信双方如何进行端口绑定?

通常服务端启动时会绑定一个端口提供服务,而客户端在发起连接请求时会被随机分配一个端口号

Socket属于网络的哪一层?

Socket不算是一个协议,它是应用层与传输层间的一个抽象层。它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用,以实现进程在网络中通信

Socket是全双工的吗?

基于TCP协议,是全双工的

HTTP协议是全双工的吗?

HTTP 协议设计的初衷本身就是请求/响应模式,这是规范决定的。不过在技术上是可以利用下层的 TCP 来进行全双工通信的。

Socket与WebSocket的区别

Socket是应用层与传输层的一个抽象,将复杂的TCP/IP协议隐藏在Socket接口之后,只对用户暴露简单的接口

而WebScoket是应用层协议,它也是基于TCP实现,同时借助了HTTP协议建立连接

Socket什么情况下可读什么情况下可写

socket准备好读:
  1. socket接收缓冲区中已经接收的数据的字节数大于等于socket接收缓冲区低潮限度的当前值;

对这样的socket的读操作不会阻塞,并返回一个大于0的值(即:准备好读入的数据的字节数).

我们可以用socket选项SO_RCVLOWAT来设置此低潮限度,对于TCP和UDP socket,其缺省值为1;

  1. 连接的读READ这一半关闭(即:接收到对方发过来的FIN的TCP连接).对于这样的socket的读操作将不阻塞,

并且返回0(即:文件结束符,FIN包体长度为0字节);

  1. 一个用于监听的socket,并且已经完成的连接数为非0.这样的soocket处于可读状态,

是因为socket收到了对方的connect请求,执行了三次握手的第一步:对方发送SYN请求过来,使监听socket处于可读状态;正常情况下,

这样的socket上的accept操作不会阻塞;

  1. 有一个socket有异常错误条件待处理.对于这样的socket的读操作将不会阻塞,并且返回一个错误(-1),errno则设置成明确的.这些待处理的错误也可通过指定socket选项SO_ERROR调用getsockopt来取得并清除;
socket准备好写 :
  1. socket发送缓冲区中的可用空间字节数大于等于socket发送缓冲区低潮限度的当前值,且(i):socket已连接(TCP socket),

或者(ii):socket不要求连接(如:UDP socket).这意味着,如果我们将这样的socket设置为非阻塞模式,写操作将不会阻塞,并且返回一个正值

(如:由传输层接收的字节数).我们可以用socket选项SO_SNDLOWAT来设置此低潮限度,对于TCP和UDP socket,其缺省值一般是2048Bytes;

  1. 连接的写WRITE这一半关闭.对于这样的socket的的写操作将产生信号SIGPIPE;

  2. 有一个socket异常错误条件待处理.对于这样的socket的写操作将不会阻塞并且返回一个错误(-1),errno则设置成明确的错误条件.这些待处理的错误也可以通过指定socket选项SO_ERROR调用getsockopt函数来取得并清除;

如果select返回可读,结果只读到0字节,什么情况?

select 返回0代表超时。select出错返回-1。

select到读事件,但是读到的数据量为0,说明对方已经关闭了socket的读端。本端关闭读即可。

当select出错时,会将接口置为可读又可写。这时就要通过判断select的返回值为-1来区分。

==socket一直返回可读,导致任务死循环。这个问题最后的结果是因为socket发生错误,但是没有读取错误码导致 socket 一直可读。==

什么是粘包? socket 中造成粘包的原因是什么? 哪些情况会发生粘包现象?

**粘包:**多个数据包被连续存储于连续的缓存中,在对数据包进行读取时==由于无法确定发送方的发送边界,而采用某一估测值大小来进行数据读出, 若双方的size不一致时就会使指发送方发送的若干包数据到接收方接收时粘成一包==,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。

发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率, ==发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去, 这样接收方就收到了粘包数据==。

==接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象==。这是因为接收方先把收到的数据放在系统接收缓冲区, 用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后, 而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。

分包是指在出现粘包的时候我们的接收方要进行分包处理。 (在长连接中都会出现) 数据包的边界发生错位,导致读出错误的数据分包,进而曲解原始数据含义。

  1. 当短连接的情况下,不用考虑粘包的情况 。

  2. 如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包

  3. 如果双方建立连接,需要在连接后一段时间内发送不同结构数据 处理方式: 接收方创建一预处理线程,对接收到的数据包进行预处理,将粘连的包分开

注:粘包情况有两种,一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的包。

如何解决粘包

有以下三种情况可以解决粘包现象:

  • 一是约定数据包长度,即发送端和接收端约定一样的发送和接收的数据包长度,这样可以清晰的获取到我们需要的数据;

  • 二是使用分隔符,比如smtp协议就是在发送时,使用\r\n为分隔符,但如果我们要发送的数据中也有\r\n呢,就容易搞混淆,所以不是特别推荐;

  • 三是在每个数据包的开头利用2个或者4个字节填充整个数据包的长度,这样接收端可以先接收2个或者4个字节,就可以准确的知道真正的数据包是多长,从而正确获取需要的数据;

  • 四根据数据类型拆包

socket和http的区别

socket连接

socket不属于协议范畴,而是一个调用接口(API),是对TCP/IP协议的封装。实现服务器与客户端之间的物理连接,并进行数据传输。Socket处于网络协议的传输层,主要有TCP/UDP两个协议。

socket连接是长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会是连接断开,比如:服务器端或客户端主机宕机了、网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。所以当一个socket连接中没有数据的传输,那么为了维持连接需要发送心跳消息。

socket传输的数据可自定义,为字节级,数据量小,可以加密,数据安全性高,适合Client/Server之间信息实时交互。

http连接

HTTP是基于TCP/IP协议的应用层协议,定义的是传输数据的内容的规范。

HTTP是基于请求-响应形式并且是短连接,即客户端向服务器端发送一次请求,服务器端响应后连接即会断掉。

HTTP是无状态的协议,针对其无状态特性,在实际应用中又需要有状态的形式,因此一般会通过session/cookie技术来解决此问题。

HTTP的传输速度慢,数据包大,数据传输安全性差,如实现实时交互,服务器性能压力大。

TTL是什么?有什么用处,通常那些工具会用到它?(ping? traceroute? ifconfig? netstat?)

简: TTL是Time To Live,一般是hup count,每经过一个路由就会被减去一,如果它变成0,包会被丢掉。它的主要目的是防止包在有回路的网络上死转,浪费网络资源。ping和traceroute用到它。

详: TTL是Time To Live,目前是hup count,当包每经过一个路由器它就会被减去一,如果它变成0,路由器就会把包丢掉。IP网络往往带有环(loop),比如子网A和子网B有两个路由器相连,它就是一个loop。TTL的主要目的是防止包在有回路的网络上死转,因为包的TTL最终后变成0而使得此包从网上消失(此时往往路由器会送一个ICMP包回来,traceroute就是根据这个做的)。ping会送包出去,所以里面有它,但是ping不一定非要不可它。traceroute则是完全因为有它才能成的。ifconfig是用来配置网卡的,netstat -rn 是用来列路由表的,所以都用不着它

HTTPS的原理

blog.csdn.net/weixin_4447…

zhuanlan.zhihu.com/p/27395037

浏览器向服务器发送HTTPS服务请求,在传输数据之前,进行一次握手

握手的目的:

  • 确立双方加密传输的密码信息
  • 做一次传输测试(利用随机生成的随机数)

TSL/SSL使用了非对称加密、对称加密、Hash算法

握手过程

  1. 浏览器请求https服务,并将自己的一套加密规则发送给网站

  2. 服务器从中选择一组加密算法,并将自己的身份信息以证书的形式回给浏览器

证书中包含:身份信息、公钥、证书颁发机构……

  1. 浏览器收到证书后
  • 验证证书合法性(颁发机构是否合法、网站地址正确与否……)

若证书受信,浏览器地址栏会出现一个锁头

  • 证书受信后,浏览器随机生成一串随机数(用于测试),并使用证书中的公钥对随机数加密,将其发送给服务器
  1. 服务器利用自己的私钥进行解密,完成一次测试

访问一个网页的过程是怎样的

整个过程可以概括为几下几个部分:

  1. 域名解析成IP地址;

  2. 封装HTTP请求数据包

  3. 与目的主机进行TCP连接(三次握手);

  4. 发送与收取数据(浏览器与目的主机开始HTTP访问过程);

  5. 与目的主机断开TCP连接(四次挥手);

DNS、HTTP、TCP、OSPF、IP、ARP

HTTP1.1与HTTP1.0的区别

HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而HTTP1.1则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议。 主要区别主要体现在:

  1. 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。

  2. 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。

  3. 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

  4. Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。

  5. 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。

什么是虚拟内存

虚拟内存的一个重要意义:定义了一个连续的虚拟地址空间,并将内存扩展到了硬盘空间

虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。

对虚拟内存的定义是基于对地址空间的重定义的,即把地址空间定义为「连续的虚拟内存地址」,以借此「欺骗」程序,使它们以为自己正在使用一大块的「连续」地址。

也就是说虚拟内存能提供一大块连续的地址空间,对程序来说它是连续的,完整的,实际上虚拟内存是映射在多个物理内存碎片上,还有部分映射到了外部磁盘存储器上。

虚拟内存有以下两个优点:

​ 1.虚拟内存地址空间是连续的,没有碎片

​ 2.虚拟内存的最大空间就是cup的最大寻址空间,不受内存大小的限制,能提供比内存更大的地址空间

虚拟内存作用:

  • 使得大型程序编写更容易

  • 对真正的物理内存的使用更加高效

进程有几种状态

就绪、运行、阻塞

Windows内存管理方式

段式存储、页式存储、段页式存储