同步与异步
同步复制与异步复制
复制系统的一个重要细节是:复制是同步(synchronously)发生还是异步(asynchronously)发生,在关系型数据库中这通常是一个配置项,其他系统通常硬编码为其中一个。主从之间可以是异步复制,也可以是同步复制。例如 MySQL,在默认情况下采用异步复制。
譬如网站的用户更新他们的个人头像。在某个时间点,客户向主库发送更新请求;不久之后主库就收到了请求。在某个时刻,主库又会将数据变更转发给自己的从库。最后,主库通知客户更新成功。下图显示了系统各个组件之间的通信:用户客户端,主库和两个从库。时间从左到右流动。请求或响应消息用粗箭头表示。
上图中,从库 1 的复制是同步的:在向用户报告写入成功,并使结果对其他用户可见之前,主库需要等待从库 1 的确认,确保从库 1 已经收到写入操作。以及在使写入对其他客户端可见之前接收到写入。跟随者 2 的复制是异步的:主库发送消息,但不等待从库的响应。从库 2 处理消息前存在一个显著的延迟。通常情况下,复制的速度相当快:大多数数据库系统能在一秒向从库应用变更,但它们不能提供复制用时的保证。有些情况下,从库可能落后主库几分钟或更久;例如:从库正在从故障中恢复,系统在最大容量附近运行,或者如果节点间存在网络问题。
同步与异步的对比
同步复制的优点是,从库保证有与主库一致的最新数据副本。异步复制容易引起数据丢失。比如主从结构中,主节点的写入请求还没有复制到从节点就挂了,当从节点被选为新的主节点之后,在这之前写入没有同步的数据就会被丢失。虽然即便采用了同步复制,也只能提供相对较弱的基本保障。其他情况譬如主接收写入请求然后发到从节点,从节点写入成功后并发送确认给主,如果此时主节点正准备发送确认信息给客户端时挂了,那么客户端就会认为提交失败,可是从节点已经提交成功了,如果这是从节点被提升为主,那么就出现问题了。
同步复制的缺点是如果同步从库没有响应(比如它已经崩溃,或者出现网络故障,或其它任何原因),主库就无法处理写入操作。主库必须阻止所有写入,并等待同步副本再次可用。因此,将所有从库都设置为同步的是不切实际的:任何一个节点的中断都会导致整个系统停滞不前。实际上,如果在数据库上启用同步复制,通常意味着其中一个跟随者是同步的,而其他的则是异步的。如果同步从库变得不可用或缓慢,则使一个异步从库同步。这保证你至少在两个节点上拥有最新的数据副本:主库和同步从库。这种配置有时也被称为半同步(semi-synchronous)。
在主从复制结构里,异步复制相比同步复制具备更高的吞吐量和更低延迟,因此,结合同步和异步复制是一个常见选项。比如 Kafka,根据它的声称,这是一个 CA 系统,也就是同时达到数据一致和高可用。Kafka 的复制设计同时包含异步复制和同步复制,同步复制节点组成的集合称为 ISR(In-Sync Replicas),只有 ISR 内的所有节点都对写入确认之后,才算做写成功。当一个节点失效,Leader 会通过 ZooKeeper 感知并把它从 ISR 中移除。不过 Kafka 有一个问题,因为它声称 F 个节点可以容忍 F-1 个节点失效,这跟其他系统不同,通常类似的设计只能容忍 F/2-1 个节点失效,也就是说要确保大多数节点都能正常运行,而 Kafka 这把这个条件弱化成为只有 Leader 运行也可以。这样做是有问题的:假设 ISR 只剩下一个 Leader 在运行,如果此时 Leader 跟 ZooKeeper 的网络连接中断,就会产生一次选举,让 ISR 之外的节点(那些异步复制节点)加入 ISR,通常它会落后此前的 Leader 不少。当原先的 Leader 跟 ZooKeeper 网络连接恢复后,系统就产生脑裂,需要对 2 个 Leader 的数据做 Merge 或者舍弃,后者则会导致数据丢失。
最后,通常情况下,基于领导者的复制都配置为完全异步在这种情况下,如果主库失效且不可恢复,则任何尚未复制给从库的写入都会丢失这意味着即使已经向客户端确认成功,写入也不能保证 持久(Durable)然而,一个完全异步的配置也有优点:即使所有的从库都落后了,主库也可以继续处理写入。