checkpoint
分布式一致性(共识)算法
raft和zab协议
共同点:
- 都是共识算法,写数据时都需要大部分成功才能把日志应用到状态机。
- 都有选主、日志对齐、数据广播的流程。
区别:
- 选主方式:raft比较last_log_index以及last_log_term保证选出的leader已经拥有最完整的数据,zab仅通过节点标识选主,所以需要之后的recovery过程,不过实现中zab也采用了类似于raft的选主方式
- log恢复方向:raft单向,仅从leader到follower补齐log;zab双向,leader需要从follower接收数据来生成initial history
- 处理只读请求:raft的读请求与写请求处理逻辑一样,zab用租约使得可以读任意副本
相关组件:redis哨兵、zookeeper
redis 哨兵集群选举采用了raft协议、zookeeper选举采用了zab协议;
环形缓冲区和双指针设计
redis主从数据同步和mysql的redo log刷盘:
- redis主从支持数据增量复制,是通过redis主库内存中的环形缓存区repl_logback_buffer进行恢复,主从分别记录了自己的offset,主offset追上从offset时候,从库需要执行全量复制(从库同步速度过慢,整块数据重新同步)——双指针:主、从offet
- mysql中存在redo log环形文件,write pos是可以顺序写的指针,check point是文件更新位置的指针。当write pos追上check point时候,会停止对外关于write pos写相关的服务,等待check point擦除、更新到数据文件,以此保证crash-safe。——双指针:write pos和check point
redis 还有一个replicate_buffer是直接通过socket发送给从库的,专门用来传播用户的写命令到从库,保证主从数据一致。replication_buffer 主从断开连接之后就没了,只能靠repl_logback_buffer
CAP
zk写数据时需要大部分从节点同步成功了才响应客户端成功,属于CP
redis从主从选举过程(判断从库的offet)可以判断写从库不是同步的,主库应该是直接响应给client,属于AP
mysql根据同步从库方式分:
- 异步——AP
- 半同步——AP
- 主库binlog写到从库relay log则响应成功ack
- 全同步——CP
- 从库执行relay log中的命令成功后才响应成功ack(此处应该是基于GTID或者位点实现的)
写完主库,立刻响应给客户端而还没来得及同步从库就挂了,此时数据会丢失
- 从库执行relay log中的命令成功后才响应成功ack(此处应该是基于GTID或者位点实现的)
脑裂问题
zk的脑裂
场景1:网络”分区”,集群变成两个子集群进行内部通信,各自leader同时对外提供服务。后期数据合并、冲突无法解决。——leader选举过半机制:N/2+1(快速领导者选举算法)
场景2:旧leader假死,选举出了一个新leader。旧leader向其他follower发出写请求。——zk维护了任期(epoch),全局递增,当收到的epoch小于自己的则拒绝,zk的写也遵循quorum机制,得不到大多数支持的写也是无效的;
zk的epoch和raft算法的term都是任期的意思
redis的脑裂
场景1:旧leader假死,哨兵选举出了一个新的leader,此时存在两个leader。——通过配置min-slaves-to-write(最少从节点个数)以及min-slaves-max-lag(最大延时时长)控制(旧)leader写,不满足则拒绝(旧)leader写
HDFS HA NameNode
高并发写:高吞吐和高可靠性设计(WAL+内存)
兼并两种优势的设计往往是发挥高效的磁盘顺序写代替随机写以及响应更快的内存即:WAL+内存处理
NameNode和SecondaryNameNode(HDFS)
NN管理DN的元数据信息,需要频繁写DN元数据,此时为了高吞吐,采取在内存上进行读写,而不是磁盘上;同时为了保证数据不丢失,WAL写入Edits log中;另外为了保证NN宕机后快速恢复,有FsImage;
SNN是为了解决NN长时间运行导致Edits log过大的问题,定时合并NN的FsImage和Edits Log;
Redis持久化机制AOF
Redis设计相似,基于内存操作,数据可靠性通过AOF和RDB;Redis进行写AOF时候是先执行命令再WAL的(此处不能叫WAL了,好处是能屏蔽掉无效命令,减缓AOF增大速度)
当AOF日志过大时候,不像HDFS有个SNN帮忙合并快照和增量日志,Redis自己fork出一个子进程,压缩增量日志,进行AOF重写(fork过程阻塞主进程),重写过程中:
- 新得请求过来,双写AOF(便于回滚)
- 子进程读取内存数据,以set命令存入到重写AOF中
Mysql持久化Redo log
mysql每次更新和查询如果都走磁盘的话,随机写和读效率都很低,以下仅针对随机写说下mysql的设计。
mysql存在一个WAL的redo log,另外数据写在缓存页上,之后读从该缓存页上读取,只有换页、check point、shut down等操作才会执行强制刷到磁盘——即保证crash safe(数据可靠性)又保证了高吞吐Hbase的HLog和MemStore
Hbase先WAL到HLog,同时也写到MemStore提供对外服务。满足条件(时间、空间)flush到磁盘。这样的好处除了上面提到的高吞吐和高可靠性外,Hbase还有一个额外好处,避免创建多个小文件(store file)Kafka
WAL+零拷贝
写时复制COW
适合场景:对读的性能要求很高,读多写少,弱一致性
Redis在bgsave fork出子进程用于生成快照(RDB)的时候,接收到client修改命令,此时主进程会复制出一份副本,主线程在副本上进行修改,子线程读原来的数据块。
bgsave会fork出一个子进程拷贝主进程的内存页表(如果实例数据很多,页表就也很多,阻塞较慢),页表内地址指向相同的内存空间。只有在写时,才会出现内存分离(cow)
刷盘方式(写回策略)
写流程:buffer -> page cache -> (fsync)disk
数据已经到达操作系统的Page Cache,可以保证的是如果进程挂了,但是操作系统没挂,数据不会丢失
redis的AOF
- appendfsync always:每次写入都刷盘,对性能影响最大,占用磁盘IO比较高,数据安全性最高(总是disk)
- appendfsync everysec:1秒刷一次盘,对性能影响相对较小,节点宕机时最多丢失1秒的数据(每秒disk)
appendfsync no:按照操作系统的机制刷盘,对性能影响最小,数据安全性低,节点宕机丢失数据取决于操作系统刷盘机制(每次都写到page cache,disk由操作系统决定)
mysql的redo log
innoDB 提供了 innodb_flush_log_at_trx_commit 参数,它有三种可能取值:
设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;(空间时间满足到page cache)
- 设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘;(每次都disk)
设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache。(每次都到page cache,disk由操作系统决定)
mysql的binlog
write(写到 page cache)和 fsync 的时机,innoDB 提供了参数 sync_binlog 控制的:
sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync;
- sync_binlog=1 的时候,表示每次提交事务都会执行 fsync;
- sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。
主从数据一致
副本同步策略(ACK应答)
Kafka生产者写应答策略
- ACK=-1:生产者发送消息后,需要等待leader副本将数据同步到ISR中的所有副本后, 返回成功。——可靠性最高
- ACK=0 :生产者发送消息后,无需等待响应,直接返回成功。——吞吐量最高
- ACK=1 :生产者发送消息后,leader写入成功,返回成功。——默认配置,但也存在数据丢失
ZK写应答策略
leader写只有超过半数follower成功才返回ACK——既保证了可靠性也保证了leader假死的脑裂问题Redis
通过配置min-slaves-to-write(最少从节点个数)以及min-slaves-max-lag(最大延时时长)控制(旧)leader写mysql
根据同步从库方式分:
主从切换策略
ZK:ZAB协议
REDIS
- 哨兵集群下,有raft算法
- Cluster