分布式分为分布式缓存(Redis)、分布式锁(Redis 或 Zookeeper)、分布式服务(Dubbo 或 SpringCloud)、分布式服务协调(Zookeeper)、分布式消息队列(Kafka 、RabbitMq)、分布式 Session 、分布式事务、分布式搜索(Elasticsearch)等。不可能全部分布式内容都熟悉,必定要在某个领域有所专长。node
分布式理论
问:分布式有哪些理论?
CAP 、BASE。分布式 CAP 理论,任何一个分布式系统都没法同时知足 Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性) 这三个基本需求。最多只能知足其中两项。而 Partition tolerance(分区容错性) 是必须的,所以通常是 CP ,或者 AP。redis
问:你怎么理解分布式一致性?
数据一致性一般指关联数据之间的逻辑关系是否正确和完整。在分布式系统中,数据一致性每每指的是因为数据的复制,不一样数据节点中的数据内容是否完整而且相同。算法
一致性还分为强一致性,弱一致性,还有最终一致性。强一致性就是立刻就保持一致。 最终一致性是指通过一段时间后,能够保持一致。spring
分布式事务
问:你怎么理解分布式事务?分布式事务的协议有哪些?
分布式事务是指会涉及到操做多个数据库的事务。目的是为了保证分布式系统中的数据一致性。分布式事务类型:二阶段提交 2PC ,三阶段提交 3PC。数据库
-
2PC :第一阶段:准备阶段(投票阶段)和第二阶段:提交阶段(执行阶段)。json
-
3PC :三个阶段:CanCommit 、PreCommit 、DoCommit。数组
问:分布式事务的解决方案有哪些?
分布式事务解决方案:补偿机制 TCC 、XA 、消息队列 MQ。浏览器
问:讲一下 TCC。
T(Try)锁资源:锁定某个资源,设置一个预备类的状态,冻结部分数据。缓存
-
好比,订单的支付状态,先把状态修改成"支付中(PAYING)"。安全
-
好比,原本库存数量是 100 ,如今卖出了 2 个,不要直接扣减这个库存。在一个单独的冻结库存的字段,好比 prepare _ remove _ stock 字段,设置一个 2。也就是说,有 2 个库存是给冻结了。
-
积分服务的也是同理,别直接给用户增长会员积分。你能够先在积分表里的一个预增长积分字段加入积分。
-
好比:用户积分本来是 1190 ,如今要增长 10 个积分,别直接 1190 + 10 = 1200 个积分啊!你能够保持积分为 1190 不变,在一个预增长字段里,好比说 prepare _ add _ credit 字段,设置一个 10 ,表示有 10 个积分准备增长。
C(Confirm):在各个服务里引入了一个 TCC 分布式事务的框架,事务管理器能够感知到各个服务的 Try 操做是否都成功了。假如都成功了, TCC 分布式事务框架会控制进入 TCC 下一个阶段,第一个 C 阶段,也就是 Confirm 阶段。此时,须要把 Try 阶段锁住的资源进行处理。
-
好比,把订单的状态设置为“已支付(Payed)”。
-
好比,扣除掉相应的库存。
-
好比,增长用户积分。
C(Cancel):在 Try 阶段,假如某个服务执行出错,好比积分服务执行出错了,那么服务内的 TCC 事务框架是能够感知到的,而后它会决定对整个 TCC 分布式事务进行回滚。
TCC 分布式事务框架只要感知到了任何一个服务的 Try 逻辑失败了,就会跟各个服务内的 TCC 分布式事务框架进行通讯,而后调用各个服务的 Cancel 逻辑。也就是说,会执行各个服务的第二个 C 阶段, Cancel 阶段。
-
好比,订单的支付状态,先把状态修改成" closed "状态。
-
好比,冻结库存的字段, prepare _ remove _ stock 字段,将冻结的库存 2 清零。
-
好比,预增长积分的字段, prepare _ add _ credit 字段,将准备增长的积分 10 清零。
问:事务管理器宕掉了,怎么办?
作冗余,设置多个事务管理器,一个宕掉了,其余的还能够用。
问:怎么保证分布式系统的幂等性?
状态机制。版本号机制。
Redis
问:Redis 有哪些优点?
-
速度快,由于数据存在内存中。
-
支持丰富数据类型,支持 string、list、set 、sorted set、hash。
-
支持事务,操做都是原子性,所谓的原子性就是对数据的更改要么所有执行,要么所有不执行。
-
丰富的特性:可用于缓存,消息,按 key 设置过时时间,过时后将会自动删除。
-
单线程,单进程,采用 IO 多路复用技术。
问:Redis 的存储结构是怎样的?
key-value 键值对。
问:Redis 支持哪些数据结构?
string(字符串), hash(哈希), list(队列), set(集合)及 zset(sorted set 有序集合)。
问:Redis 的数据结构,有哪些应用场景?
-
string:简单地 get / set 缓存。
-
hash:能够缓存用户资料。好比命令:hmset user1 name "lin" sex "male" age "25" ,缓存用户 user1 的资料,姓名为 lin ,性别为男,年龄 25。
-
list:能够作队列。往 list 队列里面 push 数据,而后再 pop 出来。
-
zset:能够用来作排行榜。
问:Redis 的数据结构,底层分别是由什么实现的?
-
Redis 字符串,却不是 C 语言中的字符串(即以空字符 ’\0’ 结尾的字符数组),它是本身构建了一种名为 简单动态字符串(simple dynamic string , SDS)的抽象类型,并将 SDS 做为 Redis 的默认字符串表示。
-
Redi List ,底层是 ZipList ,不知足 ZipList 就使用双向链表。ZipList 是为了节约内存而开发的。和各类语言的数组相似,它是由连续的内存块组成的,这样一来,因为内存是连续的,就减小了不少内存碎片和指针的内存占用,进而节约了内存。
问:Redis 怎么保证可靠性?Redis 的持久化方式有哪些?有哪些优缺点?
一个可靠安全的系统,确定要考虑数据的可靠性,尤为对于内存为主的 Redis ,就要考虑一旦服务器挂掉,启动以后,如何恢复数据的问题,也就是说数据如何持久化的问题。 AOF 就是备份操做记录。AOF 因为是备份操做命令,备份快、恢复慢。 AOF 的优势:AOF 更好保证数据不会被丢失,最多只丢失一秒内的数据。另外重写操做保证了数据的有效性,即便日志文件过大也会进行重写。AOF 的日志文件的记录可读性很是的高。 AOF 的缺点:对于相同数量的数据集而言, AOF 文件一般要大于 RDB 文件。 RDB 就是备份全部数据,使用了快照。RDB 恢复数据比较快。
问:AOF 文件过大,怎么处理?
会进行 AOF 文件重写。
-
随着 AOF 文件愈来愈大,里面会有大部分是重复命令或者能够合并的命令。
-
重写的好处:减小 AOF 日志尺寸,减小内存占用,加快数据库恢复时间。
执行一个 AOF 文件重写操做,重写会建立一个当前 AOF 文件的体积优化版本。
问:讲一下 Redis 的事务。
先以 MULTI 开始一个事务, 而后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的全部命令。若是想放弃这个事务,可使用 DISCARD 命令。
问:Redis 事务没法回滚,那怎么处理? 问:怎么设置 Redis 的 key 过时时间?
key 的的过时时间经过 EXPIRE key seconds 命令来设置数据的过时时间。返回 1 代表设置成功,返回 0 代表 key 不存在或者不能成功设置过时时间。
问:Redis 的过时策略有哪些?
惰性删除:当读/写一个已通过期的 key 时,会触发惰性删除策略,直接删除掉这个过时 key ,并按照 key 不存在去处理。惰性删除,对内存不太好,已通过期的 key 会占用太多的内存。 按期删除:每隔一段时间,就会对 Redis 进行检查,主动删除一批已过时的 key。
问:为何 Redis 不使用定时删除?
定时删除,就是在设置 key 的过时时间的同时,建立一个定时器,让定时器在过时时间来临时,当即执行对 key 的删除操做。 定时删会占用 CPU ,影响服务器的响应时间和性能。
问:Redis 的内存回收机制都有哪些?
当前已用内存超过 maxmemory 限定时,会触发主动清理策略,也就是 Redis 的内存回收策略。 LRU 、TTL。 noeviction :默认策略,不会删除任何数据,拒绝全部写入操做并返回客户端错误信息,此时 Redis 只响应读操做。
-
volatitle - lru :根据 LRU 算法删除设置了超时属性的键,知道腾出足够空间为止。若是没有可删除的键对象,回退到 noeviction 策略。
-
allkeys - lru :根据 LRU 算法删除键,无论数据有没有设置超时属性,直到腾出足够空间为止。
-
allkeys - random :随机删除全部键,知道腾出足够空间为止。
-
volatitle - random :随机删除过时键,知道腾出足够空间为止。
-
volatitle - ttl :根据键值对象的 ttl 属性,删除最近将要过时数据。若是没有,回退到 noeviction 策略。
问:手写一下 LRU 算法。 问:Redis 的搭建有哪些模式?
主从模式、哨兵模式、Cluster(集群)模式。最好是用集群模式。
问:你用过的 Redis 是多主多从的,仍是一主多从的?集群用到了多少节点?用到了多少个哨兵?
集群模式。三主三从。
问:Redis 采用多主多从的集群模式,各个主节点的数据是否一致?
问:Redis 集群有哪些特性
master 和 slaver。主从复制。读写分离。哨兵模式。
问:Redis 是怎么进行水平扩容的? 问:Redis 集群数据分片的原理是什么?
Redis 数据分片原理是哈希槽(hash slot)。
Redis 集群有 16384 个哈希槽。每个 Redis 集群中的节点都承担一个哈希槽的子集。
哈希槽让在集群中添加和移除节点很是容易。例如,若是我想添加一个新节点 D ,我须要从节点 A 、B、C 移动一些哈希槽到节点 D。一样地,若是我想从集群中移除节点 A ,我只须要移动 A 的哈希槽到 B 和 C。当节点 A 变成空的之后,我就能够从集群中完全删除它。由于从一个节点向另外一个节点移动哈希槽并不须要中止操做,因此添加和移除节点,或者改变节点持有的哈希槽百分比,都不须要任何停机时间(downtime)。
问:讲一下一致性 Hash 算法。
一致性 Hash 算法将整个哈希值空间组织成一个虚拟的圆环, 咱们对 key 进行哈希计算,使用哈希后的结果对 2 ^ 32 取模,hash 环上一定有一个点与这个整数对应。依此肯定此数据在环上的位置,今后位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器。 一致性 Hash 算法对于节点的增减都只需重定位环空间中的一小部分数据,具备较好的容错性和可扩展性。 好比,集群有四个节点 Node A 、B 、C 、D ,增长一台节点 Node X。Node X 的位置在 Node B 到 Node C 直接,那么受到影响的仅仅是 Node B 到 Node X 间的数据,它们要从新落到 Node X 上。 因此一致性哈希算法对于容错性和扩展性有很是好的支持。
问:为何 Redis Cluster 分片不使用 Redis 一致性 Hash 算法?
一致性哈希算法也有一个严重的问题,就是数据倾斜。 若是在分片的集群中,节点太少,而且分布不均,一致性哈希算法就会出现部分节点数据太多,部分节点数据太少。也就是说没法控制节点存储数据的分配。
问:集群的拓扑结构有没有了解过?集群是怎么链接的?
无中心结构。Redis-Cluster 采用无中心结构,每一个节点保存数据和整个集群状态,每一个节点都和其余全部节点链接。
问:讲一下 Redis 主从复制的过程。
从机发送 SYNC(同步)命令,主机接收后会执行 BGSAVE(异步保存)命令备份数据。
主机备份后,就会向从机发送备份文件。主机以后还会发送缓冲区内的写命令给从机。 当缓冲区命令发送完成后,主机执行一条写命令,就会往从机发送同步写入命令。
问:讲一下 Redis 哨兵机制。
下面是 Redis 官方文档对于哨兵功能的描述:
-
监控(Monitoring):哨兵会不断地检查主节点和从节点是否运做正常。
-
自动故障转移(Automatic Failover):当主节点不能正常工做时,哨兵会开始自动故障转移操做,它会将失效主节点的其中一个从节点升级为新的主节点,并让其余从节点改成复制新的主节点。
-
配置提供者(Configuration Provider):客户端在初始化时,经过链接哨兵来得到当前 Redis 服务的主节点地址。
-
通知(Notification):哨兵能够将故障转移的结果发送给客户端。
问:讲一下布隆过滤器。
布隆过滤器的主要是由一个很长的二进制向量和若干个(k 个)散列映射函数组成。由于每一个元数据的存储信息值固定,并且总的二进制向量固定。因此在内存占用和查询时间上都远远超过通常的算法。固然存在必定的不许确率(能够控制)和不容易删除样本数据。 布隆过滤器的优势:大批量数据去重,特别的占用内存。可是用布隆过滤器(Bloom Filter)会很是的省内存。 布隆过滤器的特色:当布隆过滤器说某个值存在时,那可能就不存在,若是说某个值不存在时,那确定就是不存在了。 布隆过滤器的应用场景:新闻推送(不重复推送)。解决缓存穿透的问题。
缓存
问:缓存雪崩是什么?
若是缓存数据设置的过时时间是相同的,而且 Redis 刚好将这部分数据所有删光了。这就会致使在这段时间内,这些缓存同时失效,所有请求到数据库中。这就是缓存雪崩。
问:怎么解决缓存雪崩?
解决方法:在缓存的时候给过时时间加上一个随机值,这样就会大幅度的减小缓存在同一时间过时。
问:缓存穿透是什么?
缓存穿透是指查询一个必定不存在的数据。因为缓存不命中,而且出于容错考虑,若是从数据库查不到数据则不写入缓存,这将致使这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。
问:怎么解决缓存穿透?
问:什么是缓存与数据库双写一致问题?
问:如何保证缓存与数据库的一致性?
读的时候,先读缓存,缓存没有的话,就读数据库,而后取出数据后放入缓存,同时返回响应。
先删除缓存,再更新数据库。
问:为何是先删除缓存,而不是先更新缓存?
问:先更新数据库,再删除缓存,会有什么问题?
先更新数据库,再删除缓存。可能出现如下状况:
-
若是更新完数据库, Java 服务提交了事务,而后挂掉了,那 Redis 仍是会执行,这样也会不一致。
-
若是更新数据库成功,删除缓存失败了,那么会致使数据库中是新数据,缓存中是旧数据,数据就出现了不一致。
先删除缓存,再更新数据库。
-
若是删除缓存失败,那就不更新数据库,缓存和数据库的数据都是旧数据,数据是一致的。
-
若是删除缓存成功,而数据库更新失败了,那么数据库中是旧数据,缓存中是空的,数据不会不一致。由于读的时候缓存没有,因此去读了数据库中的旧数据,而后更新到缓存中。
问:先删除缓存,在写数据库成功以前,若是有读请求发生,可能致使旧数据入缓存,引起数据不一致,怎么处理?
分布式锁
问:Redis 如何实现分布式锁?
使用 set key value ex nx 命令。
-
当 key 不存在时,将 key 的值设为 value ,返回 1。若给定的 key 已经存在,则 setnx 不作任何动做,返回 0。
-
当 setnx 返回 1 时,表示获取锁,作完操做之后 del key ,表示释放锁,若是 setnx 返回 0 表示获取锁失败。
详细的命令以下:
set key value [EX seconds] [PX milliseconds] [NX|XX]EX seconds:设置失效时长,单位秒PX milliseconds:设置失效时长,单位毫秒NX:key不存在时设置value,成功返回OK,失败返回(nil)XX:key存在时设置value,成功返回OK,失败返回(nil)。
复制代码
示例以下:
set name fenglin ex 100 nx
复制代码
问:为何不先 set nx ,而后再使用 expire 设置超时时间?
咱们须要保证 setnx 命令和 expire 命令以原子的方式执行,不然若是客户端执行 setnx 得到锁后,这时客户端宕机了,那么这把锁没有设置过时时间,致使其余客户端永远没法得到锁了。
问:使用 Redis 分布式锁, key 和 value 分别设置成什么?
value 可使用 json 格式的字符串,示例:
{ "count":1, "expireAt":147506817232, "jvmPid":22224, "mac":"28-D2-44-0E-0D-9A", "threadId":14}
复制代码
问:Redis 实现的分布式锁,若是某个系统获取锁后,宕机了怎么办?
系统模块宕机的话,能够经过设置过时时间(就是设置缓存失效时间)解决。系统宕机时锁阻塞,过时后锁释放。 问:设置缓存失效时间,那若是前一个线程把这个锁给删除了呢?
问:若是加锁和解锁之间的业务逻辑执行的时间比较长,超过了锁过时的时间,执行完了,又删除了锁,就会把别人的锁给删了。怎么办?
这两个属于锁超时的问题。
能够将锁的 value 设置为 Json 字符串,在其中加入线程的 id 或者请求的 id ,在删除以前, get 一下这个 key ,判断 key 对应的 value 是否是当前线程的。只有是当前线程获取的锁,当前线程才能够删除。
问:Redis 分布式锁,怎么保证可重入性?
能够将锁的 value 设置为 Json 字符串,在其中加入线程的 id 和 count 变量。
-
当 count 变量的值为 0 时,表示当前分布式锁没有被线程占用。
-
若是 count 变量的值大于 0 ,线程 id 不是当前线程,表示当前分布式锁已经被其余线程占用。
-
若是 count 变量的值大于 0 ,线程 id 是当前线程的 id ,表示当前线程已经拿到了锁,没必要阻塞,能够直接重入,并将 count 变量的值加一便可。
这种思路,其实就是参考了 ReentrantLock 可重入锁的机制。
问:Redis 作分布式锁, Redis 作了主从,若是设置锁以后,主机在传输到从机的时候挂掉了,从机尚未加锁信息,如何处理?
可使用开源框架 Redisson ,采用了 redLock。
问:讲一下 Redis 的 redLock。 问:Zookeeper 是怎么实现分布式锁的?
分布式锁:基于 Zookeeper 一致性文件系统,实现锁服务。锁服务分为保存独占及时序控制两类。
-
保存独占:将 Zookeeper 上的一个 znode 看做是一把锁,经过 createznode 的方式来实现。全部客户端都去建立 / distribute _ lock 节点,最终成功建立的那个客户端也即拥有了这把锁。用完删除本身建立的 distribute _ lock 节点就释放锁。
-
时序控制:基于/ distribute _ lock 锁,全部客户端在它下面建立临时顺序编号目录节点,和选 master 同样,编号最小的得到锁,用完删除,依次方便。
更详细的回答以下:
其实基于 Zookeeper ,就是使用它的临时有序节点来实现的分布式锁。
原理就是:当某客户端要进行逻辑的加锁时,就在 Zookeeper 上的某个指定节点的目录下,去生成一个惟一的临时有序节点, 而后判断本身是不是这些有序节点中序号最小的一个,若是是,则算是获取了锁。若是不是,则说明没有获取到锁,那么就须要在序列中找到比本身小的那个节点,并对其调用 exist() 方法,对其注册事件监听,当监听到这个节点被删除了,那就再去判断一次本身当初建立的节点是否变成了序列中最小的。若是是,则获取锁,若是不是,则重复上述步骤。
当释放锁的时候,只需将这个临时节点删除便可。
Zookeeper
问:Zookeeper 的原理是什么?
问:Zookeeper 是怎么保证一致性的?
zab 协议。
zab 协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后, zab 就进入了恢复模式,当领导者被选举出来,且大多数 server 完成了和 leader 的状态同步之后,恢复模式就结束了。状态同步保证了 leader 和 server 具备相同的系统状态。
问:Zookeeper 有哪些应用场景?
Zookeeper 能够做为服务协调的注册中心。还能够作分布式锁(若是没有用过度布式锁就不要说)。
问:Zookeeper 为何能作注册中心?
Zookeeper 的数据模型是树型结构,由不少数据节点组成, zk 将全量数据存储在内存中,可谓是高性能,并且支持集群,可谓高可用。另外支持事件监听(watch 命令)。
Zookeeper 能够做为一个数据发布/订阅系统。
问:Zookeeper 的节点有哪些类型?有什么区别?
临时节点,永久节点。更加细分就是临时有序节点、临时无序节点、永久有序节点、永久无序节点。
临时节点:当建立临时节点的程序停掉以后,这个临时节点就会消失,存储的数据也没有了。
问:Zookeeper 作为注册中心,主要存储哪些数据?存储在哪里?
IP、端口、还有心跳机制。数据存储在 Zookeeper 的节点上面。
问:心跳机制有什么用? 问:Zookeeper 的广播模式有什么缺陷?
广播风暴。
问:讲一下 Zookeeper 的读写机制。
-
Leader 主机负责读和写。
-
Follower 负责读,并将写操做转发给 Leader。Follower 还参与 Leader 选举投票,参与事务请求 Proposal 投票。
-
Observer 充当观察者的角色。Observer 和 Follower 的惟一区别在于:Observer 不参与任何投票。
问:讲一下 Zookeeper 的选举机制。
Leader 不可用时,会从新选举 Leader。超过半数的 Follower 选举投票便可,Observer 不参与投票。
问:大家的 Zookeeper 集群配置了几个节点?
3 个节点。注意:Zookeeper 集群节点,最好是奇数个的。
集群中的 Zookeeper 节点须要超过半数,整个集群对外才可用。
这里所谓的整个集群对外才可用,是指整个集群还能选出一个 Leader 来, Zookeeper 默认采用 quorums 来支持 Leader 的选举。
若是有 2 个 Zookeeper,那么只要有 1 个死了 Zookeeper 就不能用了,由于 1 没有过半,因此 2 个 Zookeeper 的死亡容忍度为 0 ;同理,要是有 3 个 Zookeeper,一个死了,还剩下 2 个正常的,过半了,因此 3 个 Zookeeper 的容忍度为 1 ;同理你多列举几个:2 -> 0 ; 3 -> 1 ; 4 -> 1 ; 5 -> 2 ; 6 -> 2 会发现一个规律, 2n 和 2n - 1 的容忍度是同样的,都是 n - 1 ,因此为了更加高效,何须增长那一个没必要要的 Zookeeper 呢。
问:Zookeeper 的集群节点,若是不是奇数可能会出现什么问题?
可能会出现脑裂。
-
假死:因为心跳超时(网络缘由致使的)认为 master 死了,但其实 master 还存活着。
-
脑裂:因为假死会发起新的 master 选举,选举出一个新的 master ,但旧的 master 网络又通了,致使出现了两个 master ,有的客户端链接到老的 master 有的客户端连接到新的 master。
消息队列
问:为何使用消息队列?消息队列有什么优势和缺点?Kafka 、ActiveMQ 、RabbitMq 、RocketMQ 都有什么优势和缺点?
消息队列解耦,削峰,限流。
问:如何保证消息队列的高可用?(多副本)
问:如何保证消息不被重复消费?(如何保证消息消费的幂等性)
问:如何保证消息的可靠性传输?(如何处理消息丢失的问题)
问:如何保证消息的顺序性?
问:如何解决消息队列的延时以及过时失效问题?消息队列满了之后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
问:若是让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。
Kafka
问:讲一下 Kafka。
Kafka 的简单理解
问:Kafka 相对其余消息队列,有什么特色?
-
持久化:Kafka 的持久化能力比较好,经过磁盘持久化。而 RabbitMQ 是经过内存持久化的。
-
吞吐量:Rocket 的并发量很是高。
-
消息处理:RabbitMQ 的消息不支持批量处理,而 RocketMQ 和 Kafka 支持批量处理。
-
高可用:RabbitMQ 采用主从模式。Kafka 也是主从模式,经过 Zookeeper 管理,选举 Leader ,还有 Replication 副本。
-
事务:RocketMQ 支持事务,而 Kafka 和 RabbitMQ 不支持。
问:Kafka 有哪些模式?
若是一个生产者或者多个生产者产生的消息可以被多个消费者同时消费的状况,这样的消息队列称为"发布订阅模式"的消息队列。
问:Kafka 做为消息队列,有哪些优点?
分布式的消息系统。
高吞吐量。即便存储了许多 TB 的消息,它也保持稳定的性能。 数据保留在磁盘上,所以它是持久的。
问:Kafka 为何处理速度会很快?kafka 的吞吐量为何高?
-
零拷贝:Kafka 实现了"零拷贝"原理来快速移动数据,避免了内核之间的切换。
-
消息压缩、分批发送:Kafka 能够将数据记录分批发送,从生产者到文件系统(Kafka 主题日志)到消费者,能够端到端的查看这些批次的数据。
-
批处理可以进行更有效的数据压缩并减小 I / O 延迟。
-
顺序读写:Kafka 采起顺序写入磁盘的方式,避免了随机磁盘寻址的浪费。
问:讲一下 Kafka 中的零拷贝。
数据的拷贝从内存拷贝到 kafka 服务进程那块,又拷贝到 socket 缓存那块,整个过程耗费的时间比较高, kafka 利用了 Linux 的 sendFile 技术(NIO),省去了进程切换和一次数据拷贝,让性能变得更好。
问:Kafka 的偏移量是什么?
消费者每次消费数据的时候,消费者都会记录消费的物理偏移量(offset)的位置。等到下次消费时,他会接着上次位置继续消费
问:Kafka 的生产者,是如何发送消息的?
-
生产者的消息是先被写入分区中的缓冲区中,而后分批次发送给 Kafka Broker。
-
生产者的消息发送机制,有同步发送和异步发送。
-
同步发送消息都有个问题,那就是同一时间只能有一个消息在发送,这会形成许多消息。
-
没法直接发送,形成消息滞后,没法发挥效益最大化。
-
异步发送消息的同时可以对异常状况进行处理,生产者提供了 Callback 回调。
问:Kafka 生产者发送消息,有哪些分区策略?
Kafka 的分区策略指的就是将生产者发送到哪一个分区的算法。有顺序轮询、随机轮询、key - ordering 策略。
key - ordering 策略:Kafka 中每条消息都会有本身的 key ,一旦消息被定义了 Key ,那么你就能够保证同一个 Key 的全部消息都进入到相同的分区里面,因为每一个分区下的消息处理都是有顺序的,故这个策略被称为按消息键保序策略。
问:Kafka 为何要分区?
实现负载均衡和水平扩展。Kafka 能够将主题(Topic)划分为多个分区(Partition),会根据分区规则选择把消息存储到哪一个分区中,只要若是分区规则设置的合理,那么全部的消息将会被均匀的分布到不一样的分区中,这样就实现了负载均衡和水平扩展。另外,多个订阅者能够从一个或者多个分区中同时消费数据,以支撑海量数据处理能力。
问:Kafka 是如何在 Broker 间分配分区的?
在 broker 间平均分布分区副本。
假设有 6 个 broker ,打算建立一个包含 10 个分区的 Topic ,复制系数为 3 ,那么 Kafka 就会有 30 个分区副本,它能够被分配给这 6 个 broker ,这样的话,每一个 broker 能够有 5 个副本。
要确保每一个分区的每一个副本分布在不一样的 broker 上面:
假设 Leader 分区 0 会在 broker1 上面, Leader 分区 1 会在 broker2 上面, Leder 分区 2 会在 broker3 上面。
接下来会分配跟随者副本。若是分区 0 的第一个 Follower 在 broker2 上面,第二个 Follower 在 broker3 上面。分区 1 的第一个 Follower 在 broker3 上面,第二个 Follower 在 broker4 上面。
问:Kafka 如何保证消息的顺序性?
Kafka 能够保证同一个分区里的消息是有序的。也就是说消息发送到一个 Partition 是有顺序的。
问:Kafka 的消费者群组 Consumer Group 订阅了某个 Topic ,假如这个 Topic 接收到消息并推送,那整个消费者群组能收到消息吗?
Kafka 官网中有这样一句" Consumers label themselves with a consumer group name , and each record published to a topic is delivered to one consumer instance within each subscribing consumer group . "
表示推送到 topic 上的 record ,会被传递到已订阅的消费者群组里面的一个消费者实例。
问:如何提升 Kafka 的消费速度?
问:Kafka 出现消息积压,有哪些缘由?怎么解决?
出现消息积压,多是由于消费的速度太慢。
扩容消费者。之因此消费延迟大,就是消费者处理能力有限,能够增长消费者的数量。 扩大分区。一个分区只能被消费者群组中的一个消费者消费。消费者扩大,分区最好多随之扩大。
问:Kafka 消息消费者宕机了,怎么确认有没有收到消息?
ACK 机制,若是接收方收到消息后,会返回一个确认字符。
问:讲一下 Kafka 的 ACK 机制。
acks 参数指定了要有多少个分区副本接收消息,生产者才认为消息是写入成功的。此参数对消息丢失的影响较大。
若是 acks = 0 ,就表示生产者也不知道本身产生的消息是否被服务器接收了,它才知道它写成功了。若是发送的途中产生了错误,生产者也不知道,它也比较懵逼,由于没有返回任何消息。这就相似于 UDP 的运输层协议,只管发,服务器接受不接受它也不关心。
若是 acks = 1 ,只要集群的 Leader 接收到消息,就会给生产者返回一条消息,告诉它写入成功。若是发送途中形成了网络异常或者 Leader 还没选举出来等其余状况致使消息写入失败,生产者会受到错误消息,这时候生产者每每会再次重发数据。由于消息的发送也分为 同步 和 异步, Kafka 为了保证消息的高效传输会决定是同步发送仍是异步发送。若是让客户端等待服务器的响应(经过调用 Future 中的 get() 方法),显然会增长延迟,若是客户端使用回调,就会解决这个问题。
若是 acks = all ,这种状况下是只有当全部参与复制的节点都收到消息时,生产者才会接收到一个来自服务器的消息。不过,它的延迟比 acks = 1 时更高,由于咱们要等待不仅一个服务器节点接收消息。
问:Kafka 如何避免消息丢失?
一、生产者丢失消息的状况
生产者(Producer) 调用 send 方法发送消息以后,消息可能由于网络问题并无发送过去。
因此,咱们不能默认在调用 send 方法发送消息以后消息消息发送成功了。为了肯定消息是发送成功,咱们要判断消息发送的结果。能够采用为其添加回调函数的形式,获取回调结果。
若是消息发送失败的话,咱们检查失败的缘由以后从新发送便可!能够设置 Producer 的 retries(重试次数)为一个比较合理的值,通常是 3 ,可是为了保证消息不丢失的话通常会设置比较大一点。
设置完成以后,当出现网络问题以后可以自动重试消息发送,避免消息丢失。
二、消费者丢失消息的状况
当消费者拉取到了分区的某个消息以后,消费者会自动提交了 offset。自动提交的话会有一个问题,试想一下,当消费者刚拿到这个消息准备进行真正消费的时候,忽然挂掉了,消息实际上并无被消费,可是 offset 却被自动提交了。手动关闭闭自动提交 offset ,每次在真正消费完消息以后以后再本身手动提交 offset 。
3 、Kafka 丢失消息
a、假如 leader 副本所在的 broker 忽然挂掉,那么就要从 follower 副本从新选出一个 leader ,可是 leader 的数据还有一些没有被 follower 副本的同步的话,就会形成消息丢失。所以能够设置 ack = all。
b、设置 replication . factor >= 3 。为了保证 leader 副本能有 follower 副本能同步消息,咱们通常会为 topic 设置 replication . factor >= 3。这样就能够保证每一个 分区(partition) 至少有 3 个副本。虽然形成了数据冗余,可是带来了数据的安全性。
问:Kafka 怎么保证可靠性?
多副本以及 ISR 机制。
在 Kafka 中主要经过 ISR 机制来保证消息的可靠性。
ISR(in sync replica):是 Kafka 动态维护的一组同步副本,在 ISR 中有成员存活时,只有这个组的成员才能够成为 leader ,内部保存的为每次提交信息时必须同步的副本(acks = all 时),每当 leader 挂掉时,在 ISR 集合中选举出一个 follower 做为 leader 提供服务,当 ISR 中的副本被认为坏掉的时候,会被踢出 ISR ,当从新跟上 leader 的消息数据时,从新进入 ISR。
问:什么是 HW ?
HW(high watermark):副本的高水印值, replica 中 leader 副本和 follower 副本都会有这个值,经过它能够得知副本中已提交或已备份消息的范围, leader 副本中的 HW ,决定了消费者能消费的最新消息能到哪一个 offset。
问:什么是 LEO ?
LEO(log end offset):日志末端位移,表明日志文件中下一条待写入消息的 offset ,这个 offset 上实际是没有消息的。无论是 leader 副本仍是 follower 副本,都有这个值。
问:Kafka 怎么保证一致性?(存疑)
一致性定义:若某条消息对 client 可见,那么即便 Leader 挂了,在新 Leader 上数据依然能够被读到。 HW - HighWaterMark : client 能够从 Leader 读到的最大 msg offset ,即对外可见的最大 offset , HW = max(replica . offset)
对于 Leader 新收到的 msg , client 不能马上消费, Leader 会等待该消息被全部 ISR 中的 replica 同步后,更新 HW ,此时该消息才能被 client 消费,这样就保证了若是 Leader fail ,该消息仍然能够重新选举的 Leader 中获取。 对于来自内部 Broker 的读取请求,没有 HW 的限制。同时, Follower 也会维护一份本身的 HW , Folloer . HW = min(Leader . HW , Follower . offset).
问:Kafka 怎么处理重复消息?怎么避免重复消费?
偏移量 offset :消费者每次消费数据的时候,消费者都会记录消费的物理偏移量(offset)的位置。等到下次消费时,他会接着上次位置继续消费。 通常状况下, Kafka 重复消费都是因为未正常提交 offset 形成的,好比网络异常,消费者宕机之类的。 使用的是 spring-Kafka ,因此把 Kafka 消费者的配置 enable.auto. commit 设为 false ,禁止 Kafka 自动提交 offset ,从而使用 spring-Kafka 提供的 offset 提交策略。
sprin-Kafka 中的 offset 提交策略能够保证一批消息数据没有完成消费的状况下,也能提交 offset ,从而避免了提交失败而致使永远重复消费的问题。
问:怎么避免重复消费?
将消息的惟一标识保存起来,每次消费时判断是否处理过便可。
问:如何保证消息不被重复消费?(如何保证消息消费的幂等性)
怎么保证消息队列消费的幂等性?其实仍是得结合业务来思考,有几个思路: 好比你拿个数据要写库,你先根据主键查一下,若是这数据都有了,你就别插入了, update 一下好吧。 好比你是写 Redis ,那没问题了,反正每次都是 set ,自然幂等性。 若是是复杂一点的业务,那么每条消息加一个全局惟一的 id ,相似订单 id 之类的东西,而后消费到了以后,先根据这个 id 去好比 Redis 里查一下,以前消费过吗? 若是没有消费过,你就处理,而后这个 id 写 Redis。若是消费过了,那你就别处理了,保证别重复处理相同的消息便可。
问:Kafka 消息是采用 pull 模式,仍是 push 模式?
问:pull 模式和 push 模式,各有哪些特色?
pull 模式,准确性?能够较大保证消费者能获取到消息。
push 模式,即时性?能够在 broker 获取消息后立刻送达消费者。
问:Kafka 是如何存储消息的?
Kafka 使用日志文件的方式来保存生产者和发送者的消息,每条消息都有一个 offset 值来表示它在分区中的偏移量。
Kafka 中存储的通常都是海量的消息数据,为了不日志文件过大, 一个分片并非直接对应在一个磁盘上的日志文件,而是对应磁盘上的一个目录。 数据存储设计的特色在于如下几点:
-
Kafka 把主题中一个分区划分红多个分段的小文件段,经过多个小文件段,就容易根据偏移量查找消息、按期清除和删除已经消费完成的数据文件,减小磁盘容量的占用;
-
采用稀疏索引存储的方式构建日志的偏移量索引文件,并将其映射至内存中,提升查找消息的效率,同时减小磁盘 IO 操做;
-
Kafka 将消息追加的操做逻辑变成为日志数据文件的顺序写入,极大的提升了磁盘 IO 的性能;
问:讲一下 Kafka 集群的 Leader 选举机制。
Kafka 在 Zookeeper 上针对每一个 Topic 都维护了一个 ISR(in - sync replica ---已同步的副本)的集合,集合的增减 Kafka 都会更新该记录。若是某分区的 Leader 不可用, Kafka 就从 ISR 集合中选择一个副本做为新的 Leader。
分库分表
问:数据库如何处理海量数据?
分库分表,主从架构,读写分离。
问:数据库分库分表,什么时候分?怎么分?
水平分库/分表,垂直分库/分表。
-
水平分库/表,各个库和表的结构如出一辙。
-
垂直分库/表,各个库和表的结构不同。
问:读写分离怎么作?
主机负责写,从机负责读。
系统设计
一、分布式、高并发场景
遇到高并发场景,可使用 Redis 缓存、Redis 限流、MQ 异步、MQ 削峰等。
问:在实践中,遇到过哪些并发的业务场景?
秒杀。好比抢商品,抢红包。
二、秒杀
问:如何设计一个秒杀/抢券系统?
-
能够经过队列配合异步处理实现秒杀。
-
使用 redis 的 list ,将商品 push 进队列, pop 出队列。
-
异步操做不会阻塞,不会消耗太多时间。
问:如何提升抢券系统的性能?
-
使用多个 list。
-
使用多线程从队列中拉取数据。
-
集群提升可用性。
-
MQ 异步处理,削峰。
问:秒杀怎么避免少卖或超卖?
redis 是单进程单线程的,操做具备原子性,不会致使少卖或者超卖。另外,也能够设置一个版本号 version ,乐观锁机制。
问:考勤打卡,假如高峰期有几万人同时打卡,那么怎么应对这种高并发?
使用 Redis 缓存。员工点击签到,能够在缓存中 set 状态。将工号做为 key ,打卡状态做为 value ,打卡成功为 01 ,未打卡或者打卡失败为 00 ,而后再将数据异步地写入到数据库里面就能够了。
问:如何应对高峰期的超高并发量?
Redis 限流。Redis 能够用计数器限流。使用 INCR 命令,每次都加一,处理完业务逻辑就减一。而后设置一个最大值,当达到最大值后就直接返回,不处理后续的逻辑。
Redis 还能够用令牌桶限流。使用 Redis 队列,每十个数据中 push 一个令牌桶,每一个请求进入后会先从队列中 pop 数据,若是是令牌就能够通行,不是令牌就直接返回。
三、短连接 问:如何将长连接转换成短连接,并发送短信?
短 URL 从生成到使用分为如下几步:
-
有一个服务,将要发送给你的长 URL 对应到一个短 URL 上.例如 www.baidu.com -> www.t.cn/1。
-
把短 url 拼接到短信等的内容上发送。
-
用户点击短 URL ,浏览器用 301 / 302 进行重定向,访问到对应的长 URL。
-
展现对应的内容。
问:长连接和短连接如何互相转换?
思路是创建一个发号器。每次有一个新的长 URL 进来,咱们就增长一。而且将新的数值返回.第一个来的 url 返回"www.x.cn/0",第二个返回"www.x.cn/1". 问:长连接和短连接的对应关系如何存储?
若是数据量小且 QPS 低,直接使用数据库的自增主键就能够实现。 还能够将最近/最热门的对应关系存储在 K-V 数据库中,这样子能够节省空间的同时,加快响应速度。
系统架构与设计
问:如何提升系统的并发能力?
-
使用分布式系统。
-
部署多台服务器,并作负载均衡。
-
使用缓存(Redis)集群。
-
数据库分库分表 + 读写分离。
-
引入消息中间件集群。
问:设计一个红包系统,须要考虑哪些问题,如何解决?(本质上也是秒杀系统)
问:若是让你设计一个消息队列,你会怎么设计?
项目经验及数据量 问:这个项目的亮点、难点在哪里?
问:若是这个模块挂掉了怎么办? 问:大家的项目有多少台机器?
问:大家的项目有多少个实例?
问:大家的系统 QPS(TPS)是多少?
QPS ,每秒查询量。QPS 为几百/几千,已经算是比较高的了。 TPS ,每秒处理事务数。TPS 即每秒处理事务数,包括:”用户请求服务器”、”服务器本身的内部处理”、”服务器返回给用户”,这三个过程,每秒可以完成 N 个这三个过程, TPS 也就是 3。
问:一个接口,多少秒响应才正常?
快的话几毫秒。慢的话 1-2 秒。异常状况可能会 10 几秒;最好保证 99 %以上的请求是正常的。
问:这个接口的请求时间,大概多久?主要耗时在哪里? 问:系统的数据量多少?有没有分库分表?
正常状况下,几百万的数据量没有必要分库分表。只有超过几千万才须要分库分表。
问:插入/更新一条数据要多久?更新十万/百万条数据要多久?
插入/更新一条数据通常要几毫秒;更新十万条数据最好在 10 秒之内; 百万条数据最好在 50-100 秒之内。