本文主要聊一聊主流开源产品的replication方式。
replication和partition/sharding是分布式系统必备的两种能力。具体详见复制、分片和路由. 对于海量数据来说,replication一方面可以增加冗余,保证系统可用性,一方面还可以提升读取的效率。 本文主要聚焦于replication,即假设每个node都足以存下整个副本。
按照有无leader以及leader数目可以分为:
具体详见副本更新策略,主要有如下几种
无中心的复制,可以分为三种拓扑结构,环形、星/树型、网状拓扑
主要分为以下几种
WAL
)
WAL是数据库中一种高效的日志算法,对于非内存数据库而言,磁盘I/O操作是数据库效率的一大瓶颈。在相同的数据量下,采用WAL日志的数据库系统在事务提交时,磁盘写操作只有传统的回滚日志的一半左右,大大提高了数据库磁盘I/O操作的效率,从而提高了数据库的性能。
PG使用的就是这种。logical log
)
WAL跟数据库存储引擎是耦合的,而row-based-log也称作logical log,是跟存储引擎无关的,采用的是change data capture的方式,这个就很方便异构数据源的数据同步。一般replication增加冗余常用来做master的的热备(支持查询)/温备(不支持查询)
读取的话,涉及读己所写,因果读(
针对操作有序
)、单调读(不读到旧数据
)
假设某份数据需要复制到3个节点,为了保证强一致性,不需要所有节点都确认写入操作,只需要其中两个节点(也就是超半数节点)确认就可以了。在这种情况下,如果发生两个相互冲突的写入操作,那么只有其中一个操作能为超过半数的节点所认可,这就是写入仲裁(write quorum),如果用稍微正规一点的方式说,那就是W>N/2,这个不等式的意思是参与写入操作的节点数W,必须超过副本节点数N的一半,副本节点数又称为复制因子(replication factor)。
读取仲裁(read quorum),也就是说想保证能够读到最新的数据,必须与多少个节点联系才行。假设写入操作需要两个节点来确认(W=2),那么我们至少得联系两个节点,才能保证获取到最新数据。然而,假如某些写入操作只被一个节点所确认(W=1),那么我们就必须3个节点都通信一遍,才能确保获取到的数据是最新的。一个情况下,由于写入操作没有获得足够的节点支持率,所以可能会产生更新冲突。但是,只要从足够数量的节点中读出数据,就一定能侦测出此类冲突。因此,即使在写入操作不具备强一致性的情况下,也可以实现除具有强一致性的读取操作来。
这三者之间的关系,可以用一个不等式来表述,即只有当R+W>N的时候,才能保证读取操作的强一致性。
产品 | 复制方式 | 实现方式 | 其他 |
---|---|---|---|
mysql | 主从半同步 | MySQL 5.0及之前的版本仅支持statement-based的复制,5.1+版本,当有不确定语句时,就切换为row-based log replication | 主从延迟处理 |
kafka | 主从ISR半同步 | leader写入消息并复制到所有follower,ISR中的副本写入成功返回ack给leader才算commit成功 | 生产者可以选择是否等待ISR的ack |
elasticsearch | 主从半同步,默认replication=sync | consistency可选的值有quorum、one和all。默认的设置为quorum | tradelog及fsync以及refresh |
pg | 主从异步复制 | 基于Write-ahead log | archive及stream方式 |
redis | 主从异步复制 | 增量Redis Protocol(全量\增量\长连接) | Sentinel failover |
mongo | 主从异步,Replica set模式 | 持久化的ring-buffer local.oplog.rs(initial_sync,steady-sync) | Arbiter选主 |
可以看见一些对一致性要求高的,可以采用半同步的机制,一般是基于quorum机制,像es就是基于这种机制,而kafka是采用ISR机制,二者都可以配置 其他的基本是异步复制,对于新加入的node以及recovery node的同步来说,采用不同的同步方式,新加入的一般采用全量同步,而处于正常状态的node,一般是增量同步
不同产品的replication细节不尽相同,但是大的理论是一致的,对于replication除了关注上述的replication相关方式外,还需要额外关注replication相关异常场景,才能做到成熟应用。