这一章主要介绍了备份.
备份是指把同样的信息存在多个机器上. 主要有以下好处
- 让数据在地理位置上更近.
- 即使一部分系统down了, 系统仍然可以正常工作.
- Scale! 有多个备份可以服务更多的读请求.
本章的假设是dataset足够小所以每台机器都可以单独的存下一部分.
如果说dataset相对时间不变的话, 那就相对很简单. 难点在于dataset会变, 如何解决dataset更新?
有三种常见的方法来解决变化的dataset.
- 单个领导(Leader)
- 多个领导
- 没有领导
需要分别讨论
在解决数据同步的时候有一些需要考虑的点: 比如是使用同步复制还是异步复制; 如何处理失败的备份; 最终一致性(eventual consistency); 读你的write( read-your-writes); 单调读(monotonic reads)
领导 和 追随者 (Leaders and followers)
基本概念: Replica; 同步复制, 异步复制
在多个replica之间如何保持数据的一致性, 最常见的解决方案是 leader-based replication. 有时叫做主从备份(master slave), 主动被动备份(active passive).
其中一个备份被指定做为领导.所有的写请求都通过它; 其他的备份做为追随者. 当领导改变的时候, 会通知追随者也改变; 当客户端读的时候, 可以从任意一个备份去读, 但写的话只能通过领导去写.
很多关系型数据库比如MySQL, PostgreSQL都用这种备份模式. 一些非关系型的数据库也会用. 一些message broker比如Kafka, RabbitMQ也会用.
在复制的时候有同步复制和异步复制的说法.
同步复制是写leader的时候一起把追随者也给写了. 等到所有追随者都复制的时候, 才告诉client说我写好了.
优点显而异见, 缺点是这样慢. 一般情况也不会慢很多, 但无法控制下限.
异步复制是写leader的时候只要leader写好就算成功了. follower去慢慢复制. 优点为是快, 缺点是如果leader挂了, 可能有些数据就丢了.
还有一种叫半同步, 其中一个follower是同步的,其他follower是异步的,这样可以保证至少有一个备份.
设制新的追随者
一般是先copy一个snapshot, 再从把snapshot之后的data change 慢慢追上来.
处理Node挂掉的情况
Node经常会挂掉
- 如果是follower挂掉了
这比较简单. 从last transaction慢慢追就可以了 - 如果是leader挂掉了, 就会比较麻烦
需要把一个备胎promote成leader; 需要让客户端知道这样写请求才能到新的leader上来; 需要让其他同僚知道.
如何确定leader挂了? 常用timeout
如何确定新的leader? 可以选举,也可以指定.
Request routing 让客户端把请求发给新的leader.
一些常见的头疼的问题: 新的leader可能没收到之前所有的数据, 经常的处理是直接丢掉了, 但是这样不好呀, 会出事; 如果没处理好fail over的问题, 可能会出现两个leader,这时要选择一个来shut down; 认定leader挂了的timeout设多久? 太长的话追起来太慢, 太短的话经常虚假failover. 这些问题都没有简单的solution.
Implementation of Replication logs.
有几种办法
- Statement-based: INSERT/UPDATE/DELETE这种statement, 缺点是可能不确定,如果有Now()这种statement
- WAL: 偏底层实现. 取决于数据的存储结构.
- Row based: 数据库是按row来存储的.replication log也一次处理一个整个的row. 也可以支持transaction. MySQL就是这样
- Triggered: 不是所有的replica都处理,只有影响到application level的返回结果时才处理. Overhead比较大,但很flexible.
备份延迟问题
Leader based replication对读请求很容易扩展, 但也有如下问题.
Read-your-write consistency 读你自己的写的一致性.
客户写了一个数据之后, 经常需要回读确认. 但是写是写到leader, 读是任意一个follower都可以读, 所以会产生不一致性让客户不满意.
解决办法可以是如果客户访问自已可以修改的资料的时候, 永远走leader; 或者记一个user last operate的time stamp, 让读的服务器确保能跟到这个timestamp之时
单调读 Monotonic Reads
不能让用户看到聊天记录有时间倒流的错觉. 看到了一条聊天记录, 然后它又消失了.
一致前缀读 Consistent Prefix Reads
比如先看到了B的回复然后才看到A的提问.
一个对话存在同一个partition里面.
Replication lag的解决方案
不能假装备份是同步的, 其实它并不是.