0%

blockManager介绍.md
map端计算结果缓存解析.md
memoryStore内存控制原理.md
RDD缓存管理cacheManager和磁盘管理DiskBlockManager.md
spark-local模式运行原理.md
sparkContext初始化.md
Spark和mapreducer在shuffle时的区别.md
spark如何做reduce时的数据拉取.md
spark的master、worker、exeuctor容错机制.md
spark的任务构建和提交流程.md
task是怎么分发到各节点上并执行的.md

[toc]

CSDN:Zookeeper 分布式锁 - 图解 - 秒懂


三种选举方式#

  • LeaderElection

  • AuthFastLeaderElection

  • FastLeaderElection (最新默认)

    默认的算法是FastLeaderElection,所以这篇主要分析它的选举机制

选举消息内容#

在投票完成后,需要将投票信息发送给集群中的所有服务器,它包含如下内容。

  • Serverid:服务器ID

    比如有三台服务器,编号分别是1,2,3。

    编号越大在选择算法中的权重越大。

  • Zxid:数据ID

    服务器中存放的最大数据ID.

    值越大说明数据越新,在选举算法中数据越新权重越大。

  • Epoch:逻辑时钟

    或者叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。

  • Server状态:选举状态

    LOOKING,竞选状态。

    FOLLOWING,随从状态,同步leader状态,参与投票。

    OBSERVING,观察状态,同步leader状态,不参与投票。

    LEADING,领导者状态。


选举流程:#

目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:

  1. 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。
  2. 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
  3. 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
  4. 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
  5. 服务器5启动,后面的逻辑同服务器4成为小弟。

选举流程图
选举状态图


选举详细流程#

一、首先开始选举阶段,每个Server读取自身的zxid。

二、发送投票信息

a、首先,每个Server第一轮都会投票给自己。

b、投票信息包含 :所选举leader的Serverid,Zxid,Epoch。Epoch会随着选举轮数的增加而递增。

三、接收投票信息

  1. 如果服务器B接收到服务器A的数据(服务器A处于选举状态(LOOKING 状态)

    1)首先,判断逻辑时钟值:
    a)如果发送过来的逻辑时钟Epoch大于目前的逻辑时钟。首先,更新本逻辑时钟Epoch,同时清空本轮逻辑时钟收集到的来自其他server的选举数据。然后,判断是否需要更新当前自己的选举leader Serverid。判断规则rules judging:保存的zxid最大值和leader Serverid来进行判断的。先看数据zxid,数据zxid大者胜出;其次再判断leader Serverid,leader Serverid大者胜出;然后再将自身最新的选举结果(也就是上面提到的三种数据(leader Serverid,Zxid,Epoch)广播给其他server)
    b)如果发送过来的逻辑时钟Epoch小于目前的逻辑时钟。说明对方server在一个相对较早的Epoch中,这里只需要将本机的三种数据(leader Serverid,Zxid,Epoch)发送过去就行。
    c)如果发送过来的逻辑时钟Epoch等于目前的逻辑时钟。再根据上述判断规则rules judging来选举leader ,然后再将自身最新的选举结果(也就是上面提到的三种数据(leader Serverid,Zxid,Epoch)广播给其他server)。

    2)其次,判断服务器是不是已经收集到了所有服务器的选举状态:若是,根据选举结果设置自己的角色(FOLLOWING还是LEADER),退出选举过程就是了。

最后,若没有收到没有收集到所有服务器的选举状态:也可以判断一下根据以上过程之后最新的选举leader是不是得到了超过半数以上服务器的支持,如果是,那么尝试在200ms内接收一下数据,如果没有新的数据到来,说明大家都已经默认了这个结果,同样也设置角色退出选举过程。

  1. 如果所接收服务器A处在其它状态(FOLLOWING或者LEADING)。

a)逻辑时钟Epoch等于目前的逻辑时钟,将该数据保存到recvset。此时Server已经处于LEADING状态,说明此时这个server已经投票选出结果。若此时这个接收服务器宣称自己是leader, 那么将判断是不是有半数以上的服务器选举它,如果是则设置选举状态退出选举过程。
    b) 否则这是一条与当前逻辑时钟不符合的消息,那么说明在另一个选举过程中已经有了选举结果,于是将该选举结果加入到outofelection集合中,再根据outofelection来判断是否可以结束选举,如果可以也是保存逻辑时钟,设置选举状态,退出选举过程。

[toc]

CSDN:Zookeeper 分布式锁 - 图解 - 秒懂


Q: 如果同时希望加锁的客户端非常多,有一千多个,同时监听一个节点取锁的话会有什么问题#

A:
会发生羊群效应。
即节点被删除释放锁时, 需要借助watch机制通知所有客户端, 消耗大量资源

  • 如果有1000个客户端watch 一个znode的exists调用,当这个节点被创建的时候,将会有1000个通知被发送。这种由于一个被watch的znode变化,导致大量的通知需要被发送,将会导致在这个通知期间的其他操作提交的延迟

Q: 那么如何避免监听过多?#

A:
znode不止一个, 但是会存在链式关系的锁,有点像是公平排队锁,先到先得。
lock之间会按顺序做监听, 而其他的客户端则单独监听自己需要的那个lock即可。
29492a16312e3133f8973f98d200011a8bb6c589
比如我们按照上述逻辑创建了有三个znodes.
/lock/lock-001,/lock/lock-002,/lock/lock-003.
/lock/lock-001 的这个客户端获得了lock
/lock/lock-002 的客户端watch /lock/lock-001
/lock/lock-003 的客户端watch /lock/lock-002
通过这种方式,每个节点只watch 一个变化

  • 核心逻辑:不需要关心所有的事件,判断自己是否是所有节点中序号最小的。于是,很容易可以联想的到的是,每个节点的创建者只需要关注比自己序号小的那个节点。

    a1fed39d5291cbbfc5fb079d9fc4db9b55c00c16

[toc]


Q: redis的主从复制主要是解决什么问题的?#

A:

  • 故障恢复(主节点挂了, 启动从节点作为主节点)

  • 负载均衡(读写分离, 主库写+读, 从库读)

    17d6bde713c43c334924a7957c9654959aae59f4

  • 高可用(哨兵和集群都依赖主从复制)


redis的主从复制 包含全量复制和增量复制

Q: 全量复制的全过程?#

A:

  • 阶段1:连接建立
  1. 从库调用"replicaof/slaveof ip port"命令,将对应节点设置为自己的主节点。(即调用这个命令的是从库)

  2. 从库给主库发送 psync 请求

    psync请求中包含runId(redis唯一id)和复制进度offset

    当第一次复制,offset设置为-1.

  3. 主库收到 psync 命令后,如果是第一次复制,则会用 FULLRESYNC 响应命令带上两个参数:主库 runID 和主库目前的复制进度 offset。

    fullResync告知对方我会给你全量的数据。

  • 阶段2:数据复制
  1. 主库调用bgsave命令,生成RDB文件, 然后把RDB文件发给从库。
  2. 从库收到RDB文件后,会先清空自己库内的所有数据,然后再根据RDB进行加载。
  • 阶段3:持续同步
  1. 第4步主库生成完RDB文件后, 会生成一个 replication buffer,专门记录这之后收到的所有写命令。

  2. 当RDB文件发送完成后, 主库会把replication buffer中的写命令转发给备redis。

    405422d4dd86cb820c955ab050e1437a74dd24f6


Q: 全量复制后,就不再复制了,对嘛?#

A: 错, 阶段3会持续同步, 一直存在的一个过程。
注意, 这个阶段3的过程不叫“增量复制”!


Q: 那redis的增量复制又是解决什么问题的?#

A:
专门用于解决刚才阶段3中的持续同步时,发生网络短时间断开,以至于主从不一致的问题。
当网络重新建立后, 从节点不希望全量复制,因此会借助增量复制机制,让主节点把特定offset后的写数据发给自己。(之前的psync中的offset就是用在这里的)


Q: 主节点怎么实现的增量复制?#

A:
主节点自身有一个repl_backlog_buffer, 写命令都会缓存到这里,每个命令都有个offset。
这个buffer是一个环形链表,因此有长度限制,过于老的写命令会被清理掉。
当从节点重新用psync连上,并告知自己的offset时, 主节点会检查一下,看看offset在repl_backlog_buffer中是否存在

  • 如果存在,说明来得及,于是从offset处发送写命令给从节点。
  • 如果offset不存在,说明时间太久了,offset到现在的数据之间已经有很多丢失了,需要重新全量复制了

Q: 主节点不做持久化的话,会有什么问题?#

A:
当主节点重启时,从节点正好继续增量复制,然后都继续了空的全量复制,并清空了原先的数据。


Q: 为什么全量复制时,要用RDB而不是AOF?#

A:
1、RDB文件内容是经过压缩的二进制数据(不同数据类型数据做了针对性优化),文件很小。而AOF文件记录的是每一次写操作的命令,写操作越多文件会变得很大。
\2. RDB文件直接加载key, 而AOF需要处理写命令。
\3. RDB只有在需要定时备份和主从全量复制数据时才会触发生成一次快照。而在很多丢失数据不敏感的业务场景,其实是不需要开启AOF的。


Q: 如果我的redis磁盘性能比较差,做主从全量复制时,每次从磁盘加载RDB的开销很大怎么办?#

A:
使用repl-diskless-sync配置开启无磁盘复制
master创建一个新进程直接dump RDB到slave的socket,不经过主进程,不经过硬盘。适用于disk较慢,并且网络较快的时候。


Q: 从库太多了, 主库复制的压力很大怎么办?#

A: 改成主——从——从的模式。
即从库2把从库1认做主库, 从库3把从库2认作主库。
f5078c8505ad5dec005f8268d0171aec5805e5d0


Q: redis主库和从库继续读操作时,数据可能会不一致, 但我的服务对一致性要求很高,不允许被人同时看到2个不一样的数据,怎么办?#

A:
(1)业务可以接受,系统不优化
(2)强制读主,高可用主库,用缓存提高读性能
(3)在cache里记录哪些记录发生过写请求,来路由读主还是读从

  • 优化主从节点之间的网络环境, 将延时控制在不可感知的时间范围内
  • 监控主从的当前offset或者网络延时,如果发现误差过大,则停止从从节点读,使用延时更低的集群扩展进行读写负载扩容。
  • 从节点数据正在做同步时,不允许响应客户端命令,避免不一致。

Q: 全量复制时,从节点读取到了过期的数据怎么办?#

A:
Redis 3.2中,从节点在读取数据时,增加了对数据是否过期的判断:如果该数据已过期,则不返回给客户端;将Redis升级到3.2可以解决数据过期问题。

[toc]


redis持久化是做什么的?就是为了做备份, 避免redis重启后要重新从数据库里拿缓存。
redis当前有两种持久化机制: RDB和AOC


RDB(Redis DataBase) redis快照#

Q: RDB有哪两种触发方式?#

A:

  • 手动触发。 调用save或者bgsave命令进行快照和备份。

    也可以配置redis.conf,做一个周期性快照,但本质上是使用上面这2个命令

  • 自动触发。m秒内有n次修改,或者主从复制时会触发。


Q: save命令和bgsave命令有什么区别?#

A:

  • save命令会阻塞当前redis线程,导致缓存暂时不可用(stopwolrd), 只能在开发环境或者测试环境使用。

  • bgsave 会fork创建一个子进程,在子进程中做RDB操作。

    RDB快照存盘完成后,会通知主进程,主进程更新统计信息。


Q: 如果一次执行好几个bgsave命令会怎么样?#

A:
当检测到有子进程时, 后面几个bgsave命令会直接返回,不会做创建。


Q: bgsave的过程中,如果主进程收到新的写数据怎么办?#

A:
主进程会把写数据生成一个副本,交给bgsave子进程, 子进程会把这个修改作为最新修改写入RDB文件。


Q: 这个同步副本给子进程的时候进程崩溃怎么办?导致没有落盘到RDB#

A:
RDB完全写入成功,才算成功。
写一半的RDB文件,被认为无效文件,因此会用上一次的有效RDB文件进行恢复。


Q: 每秒做一次bgsave会有什么问题?如果数据量比较大的话#

A:

  1. 磁盘压力比较大,频繁竞争磁盘通过(注意每次快照的文件都是不同的文件,但磁盘写入器会共用)
  2. 创建fork子线程的操作,本身会阻塞一定的主进程时间。

Q: 如何解决上面的问题, 又能尽可能保证备份的实时可靠性?#

A:
做增量快照(只备份新增的修改),而增量快照的话RDB无法支持。


Q: RDB的优缺点?#

A:
优点:

  • 使用了LZF算法做了压缩,数据量小于内存大小,很适合备份、全量复制。

  • 备份恢复速度比AOF快。

    缺点:

  • 效率不高,如果数据量太大的话。

  • fork子进程操作比较重

  • 文件属于二进制文件,可读性差。

AOF(append only file)持久化方式#

Q: AOF采用 ”先更新内存缓存, 再写备份日志“, 和”mysql日志先行“的思想不同,为什么?#

A:

  • 错误命令得调用redisobject方法的时候才能感知,写日志时不方便做检查。

    如果写了错误命令的日志,再执行命令,就不好恢复了(那mysql为什么没有这个顾虑?)

  • 尽快加速写操作的响应

    风险就是正巧写的时候挂了,那日志也丢了。


Q: AOF记录时的3个步骤是怎样的?#

A:

  1. 命令追加

    把写命令写道aof_buf缓冲区

  2. 文件写入和同步

    什么时候写入文件,有3种策略

  • always:每次写入缓冲区后,都马上写入文件
  • everySec:每秒把缓冲区里的数据写一次到文件(可能丢失1秒)
  • no:由操作系统控制合何时写文件

Q: 随着写入操作越来越多, AOF文件会越来越大,怎么办?#

A:
当大到一定成都时, redis会创建一个新的AOF文件, 对老文件里的命令进行重写,去除一些冗余命令,只保留每个键的最后状态。
重写操作也是fork了一个子线程进行重写


Q: 重写时,由新的数据写入怎么办?#

A:
同步2份写操作, 老AOF文件写一份, 新AOF文件的缓冲区同步一份。

  1. 主线程fork出子进程重写aof日志

  2. 子进程重写日志完成后,主线程追加aof日志缓冲

  3. 替换日志文件

    860816c7e75a0e58e1850249253f77bdd72ff327


Q: 如果重写的时候 redis重启了怎么办?#

A:
没写完的AOF文件默认失效, 使用老的AOF文件。


Q: 主进程如何拷贝数据给子进程?#

A:
使用操作系统的copy on write
子进程时会拷贝父进程的页表,即虚实映射关系(虚拟内存和物理内存的映射索引表),而不会拷贝物理内存
36b01fbc65c4c5b78e1f61f26f11766dc5a56407


一般都采用RDB和AOF混合的方式, 先全量快照, 然后一段时间内增量AOF。


Q: 恢复时, 如果同时有RDB文件和AOF文件,怎么选择?#

A:
优先选择AOF文件,AOF不存在再RDB。
219bd9996098a234c7a2534fa4e5d24c2a0525c2


Q: 那么为什么会优先加载AOF呢?#

A:
因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。


Q: 线上高压环境如何进行备份机制的调优,减少阻塞?#

A:

  • 关闭非敏感、重要性数据的备份
  • 通过一些其他补数据机制进行恢复,例如自己写一些缓存重新载入的补数据功能。
  • redis集群实现滚动备份,避免全部同时在备份。
  • redis主从复制时, 让从节点做备份,主节点不做部分。

[toc]


笔记来自
Redis进阶 - 事件:Redis事件机制详解


Q: redis的高吞吐率具体指的是什么?#

A:
在网络 IO 操作中能并发处理大量的客户端请求,并建立很多连接,却不会那么那么容易阻塞


Q: redis所说的单线程指的是哪些部分?哪些部分不是单线程?#

A:
单线程主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程
需要额外线程执行的:

  • 持久化
  • 异步删除
  • 集群数据同步

Q: 讲一下一次 Redis 客户端与服务器进行连接并且发送命令的过程?#

A:

  1. 客户端向服务端发起建立 socket 连接的请求,那么监听套接字将产生 AE_READABLE 事件,触发连接应答处理器执行。

  2. 进行应答,然后创建客户端套接字,以及客户端状态,并将客户端套接字的 AE_READABLE 事件与命令请求处理器关联。

  3. 客户端建立连接后,向服务器发送命令,那么客户端套接字将产生 AE_READABLE 事件,触发命令请求处理器执行,处理器读取客户端命令,然后传递给相关程序去执行。

  4. 执行命令获得相应的命令回复。

    为了将命令回复传递给客户端,服务器将客户端套接字的 AE_WRITEABLE 事件与命令回复处理器关联。

    当客户端试图读取命令回复时,客户端套接字产生 AE_WRITEABLE 事件,触发命令回复处理器将命令回复全部写入到套接字中。


Q: 上面的过程中, 如果没有IO多路复用,会有什么问题?#

A:
服务端将会有一个线程一直阻塞等待客户端的命令以及回复读取。占用CPU资源。


Q: 那改成IO多路复用后又是怎么优化的?#

A:
调用 epoll 机制,让内核监听这些套接字。
此时,Redis 线程不会阻塞在某一个特定的监听或已连接套接字上,也就是说,不会阻塞在某一个特定的客户端请求处理上。
正因为此,Redis 可以同时和多个客户端连接并处理请求,从而提升并发性。


Q: redis的多路复用机制只有epoll吗?#

A:

  • Redis 基于的底层 I/O 多路复用库有多套。包括select、epoll、evport和kqueue等。

  • 每个IO多路复用函数库在 Redis 源码中都对应一个单独的文件,比如ae_select.c,ae_epoll.c, ae_kqueue.c等。

  • Redis 会根据不同的操作系统,按照不同的优先级选择多路复用技术。事件响应框架一般都采用该架构,比如 netty 和 libevent。

    1bfbfd888006a50755f022d83af679ca0d670240


Q: redis有哪些时间事件?#

A:

  • 定时事件:让一段程序在指定的时间之后执行一次,执行完就不再执行了。
  • 周期性事件:让一段程序每隔指定时间就执行一次。