复制的拓扑结构描述了写请求从一个节点传播到另一个节点的通信路径。若有两个主节点,如图-7,只有一个合理拓扑结构:M1必须把他所有的写同步到M2,反之亦然。当有两个以上M,各种不同拓扑都可能的。如图-8说明了一些例子。
最普遍的拓扑是全部到全部,即图-8 ©,每个M将其写入同步到其他所有M。
但也会使用更多受限制的拓扑:例如,MySQL仅支持环形拓扑(circular topology),其中每个节点接收来自前一个节点的写入,并将这些写入(加上自己的写入)转发给后序节点。
另一流行结构是星形形状1。一个指定的根节点将写入转发给所有其他节点。星型拓扑可以推广到树。
环形、星形拓扑
写请求需通过多个节点才能到达所有副本,即中间节点需要转发从其他节点收到的数据更改。为避免无限循环,每个节点需赋予一个唯一标识符,在复制日志中的每个写请求都标记了所有已经过的节点的标识符。当某节点收到用自己的标识符标记的数据更改时,该数据更改将被忽略,避免重复转发。
问题
若某节点故障,则可能会中断其他节点之间的复制消息流,导致它们无法通信,直到节点修复。拓扑结构可以重新配置为在发生故障的节点上工作,但在大多数部署中,这种重新配置必须手动完成。更密集连接的拓扑结构(例如全部到全部)的容错性更好,因为它允许消息沿着不同的路径传播,避免单点故障。
全部到全部的拓扑也可能问题。特别当一些网络链接可能比其他网络链接更快(网络拥塞),结果一些复制消息可能“超过”其他复制消息,如图-9。
客户端A向L1的表中插入一行,B在L3更新该行。然而,L2能以不同顺序接收写入:可先接收更新(从它的角度来看,是对数据库中不存在的行的更新),之后接收L1的插入日志(本该在更新日志之前到达)。
这是个因果关系问题,类似“一致前缀读”中的:更新依赖先前完成的插入,所以需确保所有节点先接收插入,再处理更新。在每次写日志里添加一个时间戳还不够,主要因为无法确保时钟完全同步,因而无法在L2上正确排序所收到的日志。
为正确排序日志消息,可使用版本向量。冲突检测技术在很多主节点复制系统中实现不够完善。如PostgreSQL BDR不提供写入的因果排序,Tungsten Replicator for MySQL甚至不尝试检测冲突。