多台机器保存相同副本:使数据在地理位置上更接近用户,降低延迟,提高吞吐量、可用性。
概要:1主从(同/异步):配新从、处理失效节点、复制日志
2复制滞后:读自己写、单调读、前缀一致读
3多主:适用场景、处理写冲突(特定中心、唯一id/时间戳、应用层解决)
4无主:读修复反熵、w+r>n、宽松Quorum、跨中心、5、检测并发写
一、主与从节点
全同步很少,全异步:使用相对广泛,提高吞吐量,但会滞后,用于节点多,分布地域广场景
半同步:一个从节点同步,其他从异步,同步从节点性能下降或不可用,其他从升级为同步模式。ps:主节点 + 同步从节点有最新副本
1、配置新从节点
1)某时间点对主节点副本产生一致性快照,避免长时间锁db
2)将快照拷贝到新从节点
3)从连到主节点,并请求快照点后的更改日志(mysql binlog coordinates)
4)获得日志,追赶,继续处理主节点新变化,重复1-4
2、处理失效节点
从节点失效:崩溃后又重启,追赶主节点
主节点失效:切换节点,切换过程中问题:
1)如用异步复制,失效前新主 没收到 原主 所有 数据,原主重新上线,写冲突。丢弃原主解决
2)如其他系统用db内容,丢弃特别危险:从提升为主,新原主没完全同步,db用自增计数器,重新用原主分配的主键,恰好这些主键被redis引用。导致redis mysql不一致?如何解决
3)脑裂,都认为自己是主,强制关闭一个或都关
4) 设置何时的超时时间,来检测主节点失效,避免必要的切换
3、复制日志
1、预写日志wal:追加写上去,1)对于日志结构存储引擎(sstable、lsm-tree),日志是主要存储方式,日志段在后台压缩 并 支持垃圾回收 2)对于覆盖写磁盘的btree,修改预先写日志,崩溃通过索引更新方式恢复。3)除了写日志,也可发给从节点(如数据格式不一样,从无法运行)
2、行的逻辑日志:binlog,复制与存储逻辑剥离,如一条事务更新多行,多条日志,后面一行记录,该事务已提交
3、触发器复制:不涉及任何代码,写事务,触发器记录到单独表中,开销高,更容易出错,高度灵活时用。
二、复制滞后问题
完全同步实际反而非常不可靠,正常主从延迟不足1秒,实践不会太大影响
2.1读自己写
用户查看自己提交的,可能读从时新没到达从,影响用户体验。方案:
1.只从主节点读自己(从节点读其他用户),可能被修改数据(需有方法知道被修改信息)如:社交网络用户首页信息,但大多数用户都修改时,并不好。
2.跟踪更新时间,一分钟内,读主节点,并监控从节点滞后程度,避免滞后时间>1分钟的读从
3.客户端请求中包含时间戳(或日志序列号),保证读时都包含该时间戳,没有,则交给副本处理。
ps:如多设备访问(web、手机),不能用时间戳方法(无法知道其他设备),要1)元数据全局共享 2)不同设备路由到,同一数据中心
2.2 单调读
A向主节点写,B第一次读的从已写,第二次读的从没写,仿佛回滚了。
解决:用户读固定从,不随机
2.3 前缀一致读
分区数据,经多副本复制后,出现不同程度滞后,观察者先看到果,后看到因
解决:因果关系写入,一个分区完成
三、多主节点复制
3.1、适用场景
1)多数据中心:为容灾和近用户考虑(就近写)。中心内主从。中心间,主主交换(异步)。缺点:会有写冲突(不同数据中心,修改相同数据)
2)离线客户端操作:离线时,用户对本地数据任意更改(相当于主节点),再次上线,与其他设备同步(多主复制)。
例:协作编辑:减小可编辑的粒度,会导致多主复制
3.2、处理写冲突
(1)避免冲突:总是通过同一个主节点。如个人信息特定数据中心修改,对于用户相当于主从复制。
(2)收敛于一致状态:多主节点,没有绝对一致的写入顺序,副本按它看到的写入顺序执行,最终不一致。解决:
1)基于时间戳/用户id确定,最后写入/id最高 获胜,易丢数据
2)副本唯一id,定优先级,易丢数据
3)写入的值合并
(3)自定义解决(靠应用层)
1)写入时执行:复制变更日志时检测到冲突,调用应用层冲突处理
2)读取时执行:写入值全部暂存。读时返回多版本,提示用户或自动解决
四、无主节点复制
去中心复制,放弃主节点。节点失效时写入db,不需切换。重新上线读到过期数据。
解决:客户端读多个副本,版本号确定哪个值更新
1、修复失效节点:
读修复:新旧值都读到,更新值写入落后节点。缺点:很少读的数据,无法检测到
反熵过程:后台进程不断找副本间差异
2、Quorum确定读成功
n个副本,写要w个节点确认,读至少查询r个节点
要w+r>n。读取的节点一定包含最新值。w和r可以置,多写少w=n,r=1
3、宽松Quorum
响应节点数>w认为写成功,但不一定能从r个节点读到新
4、跨数据中心操作:
1)向所有副本发送,只等本地中心确认
2)只和本地交互,中心间多主复制
5、检测并发写
多副本收敛于相同的问题:
1、最后写入胜。写请求附加时间戳,丢弃早的数据,牺牲持久性。
2、因果和并发关系:Happens-Before关系。互相不需知道,就是并发关系
解决:
1、确认前后关系:
1)主键版本号,写入递增
2)客户端读主键,服务器返回所,有主键值和服务器版本号
3)客户端写主键时包含读到版本号,和新值合并用此版本号返回服务器。
4)服务器收到写入时,覆盖版本号和更低版本所有值
2、合并同时写入的值:合并版本号,去掉重复部分。删除要标记,并在合并时剔除
3、版本矢量 :多副本中,保存副本和主键的版本号。可一个副本读,另一个副本写,db决定应该覆盖还是保留并发值