复制
领导者与追随者
单领导者
同步复制与异步复制
- 同步
- 异步
- 半同步:一个副节点同步,其余副节点异步
设置新从库
- 全量镜象+增量
处理节点宕机
-
从节点
- 追赶恢复
-
主节点
-
流程
- 由剩余从节点或控制节点进行选主流程
- 通常选举有最新副本的从节点做为主节点
- 旧主节点上线后作为从节点
-
存在问题
- 新主库缺失旧主库部分写入,如果其收到冲突的写入(如主键冲突),常见方法是丢掉旧主库未同步的写入,但影响了写入的持久性
- 如果丢掉的写入与其他数据库相关联,则有可能会出现数据不一致的问题
- 脑裂
- 主库判定失效超时时间设置困难,太短,容易受网络抖动延迟误操作;太长,选主延迟加大,丢失的数据也可能越多
-
复制日志的实现
-
基于语句的复制
-
问题
- 非确定语句运行会出现主从不一致的问题,如NOW(),RAND()
- 如果使用了自增列或依赖于库中现有的数据,则从节占的执行顺序必须严格按主节点的执行顺序
- 现MYSQL会将含有不确定的语句转换为基于行的复制
-
-
基于WAL,PostgreSQL与Oracle使用该类复制
- 问题:日志记录过于底层,记录了磁盘块上哪些字节有了更改,对主从所采用的存储引擎、数据库版本的一致性要求高
-
基于逻辑日志(基于行)
- 记录与存储引擎相分离,因此主从可采用不同的存储格式,数据库也可不同
-
基于触发器
- 由于事务需要等触发器完成其操作才能提交,所以事务处理延迟会增加。
复制延迟问题
解决方法
-
读己之写
-
从业务代码上入手
-
对于和自身相关的信息,都从主库读取,其它用户的从从库读取
-
跟踪上次更新时间,如果小于一定时间(如一分钟),则从主库读
-
客户端保存最近一次写入的时间戳,所读的从库确保最新的数据时间大于等于该时间(访问需要系统额外判断从库数据的时间信息)
- 如果同一用户在不同终端同时登录并做了写入操作,此时不同客户端所保存的时间戳不一致,也会出现所看到的数据不一致问题,在多数据中心的情况下情况更复杂
-
-
-
单调读
- 为防止两次读取从库可能出现的时间倒流情况(第一次读较新的库,第二次读了较旧的库),可使用户只在一个从库上读,确保数据单调一致性。(如根据用户ID进行HASH)
-
一致前缀读
- 为防止具有先后因果的数据所写入不同分区,而“因”数据的读入延迟大于“果”的读入延迟,在客户端侧看来会出现因果倒置于的现象,需要将具有前后因果的数据写入同一分区解决
多主复制
优势
-
性能:写操作可以在本地数据中心进行处理,写网络延迟可能更低
-
可用性
-
容忍单个数据中心停机
-
数据中心间采用异步复制,因此中心间的网络通信故障不会导致写入失败
- 需要考虑的问题:写冲突
-
-
其它适用的场景
-
协同编辑
- 如石墨文档
-
离线场景
- 考虑手机,笔记本电脑和其他设备上的日历应用。无论设备目前是否有互联网连接,你需要能随时查看你的会议(发出读取请求),输入新的会议(发出写入请求)。如果在离线状态下进行任何更改,则设备下次上线时,需要与服务器和其他设备同步
-
专为多主设计的数据库CouchDB
- CouchDB支持双向数据复制,冲突检查。其适用于文中的离线操作场景主要是受益于CouchDB的支持MVCC(多版本并发控制)特性,详见第7章。
-
写冲突
-
如果采用同步冲突检测,即等待所有主节点写完毕告诉用户写成功,会失去各主节点可以独立写的优点
-
避免冲突
- 如同一用户所接入的写数据中心只能是同一个,如果允许同一用户的写请求同时接入多个数据中心,则需要在业务层面处理冲突
-
冲突合并
-
给每个写入分配一个ID(如时间戳),具有最新的ID胜出,丢弃其它写请求。
- 易造成数据丢失
-
给每个副本分配一个副本ID,具有最高ID的优先级高
- 易造成数据丢失
-
增加业务处理冲突代码
-
写时执行
- 检测到写冲突就调用用户自定义的处理代码。如Bucardo可编写Perl代码解决冲突
-
读时执行
- 写入时,所有冲突均会记录,待用户读时,返回多版本给应用,用应用决定处理
-
-
-
复制拓扑
-
环形
-
星形
-
互联(All to All)
-
问题:如果出现了因果数据写入,则可能会出现类似于分片时延不同而导致的数据乱序问题
- 解决方法:使用“版本向量”技术控制
-
-
无主复制
定义:由客户端同时向多个副本写入数据,由客户端决定写入是否成功,读取时根据各副本的版本号投票决定读取的结果
故障时写入数据库
-
例子:假设3副本的环境,有一个副本不可用,2个副本可以正常写入
-
读修复和反熵
- 读修复:在客户端读取时向老版本的副本库中写入新的数据
- 反熵:数据库后台检测旧版本,并对其数据进行修复
-
读写的法定人数
检测并发写入
- 单副本:使用基于版本号的乐观锁进行写入
- 多副本:基于版本向量