要做到全球异地多活, 一定要在数据层支持多机房写入, 并且对大多数业务场景提供最终一致性的解决方案。原因如下:

* 跨洲的网络延迟在100ms的数量级,如果只有单点写, 对于用户体验是种灾难
* 对于高频操作来说, 如果做强一致性,那么任然受限于网络延迟, 对于用户体验是种灾难
既然决定要选择最终一致性, 那么随之而来就有两个问题需要解决:

* 跨机房的数据同步
* 多点写入时的数据冲突处理
<>一 、数据同步

数据的同步有几个核心问题需要考虑:

* 获取数据变更以及重放
* 不丢不重不乱序
* 避免数据回环同步
<>获取数据变更以及重放

这个问题比较好解决, 可以通过存储提供的增量日志(比如mysql的binlog,redis的AOF)来获取本机房的数据变更。
如果用到的存储没有提供这个功能, 也可以考虑在业务层做类似的事情。 比如写入成功后, 把变更操作发到消息队列。(但是对于数据一致性要求比较高的场景,
要考虑到[变更存储+发消息]这个操作的事务性,很麻烦)。

一般来说,我们用的存储都会提供主从方案,所以思路都是通过fake成主存储的一个slave来获取数据变更。
获取到变更之后,可以写入消息队列再mirror到别的DC(比如Kafka MirrorMaker), 在别的DC重放这些变更。

<>不丢不重不乱序

这时候引入了新问题, 消息的写入和消费,是否需要exactly once和消息的有序?

这个需要具体问题具体分析,比如mysql的binlog会带上数据before和after的镜像,因此我们可以接受消息的重复,只需要保证at least
once即可; 而对redis而言, set操作可以接受重复, 但是incr等就不能接受。至于如何做到exactly once,不在本文讨论的范围内,
有兴趣的同学可以参考:
https://www.confluent.io/blog/exactly-once-semantics-are-possible-heres-how-apache-kafka-does-it/

<https://www.confluent.io/blog/exactly-once-semantics-are-possible-heres-how-apache-kafka-does-it/>

但是一般来说, 消息的有序都是需要的。我们无法容忍[set a=v1, set a=v2]的序列被处理成[set a=v2, set a=v1]。
这个问题相对比较简单,假设我们用kafka做消息队列, 那么只要用key做partitioner即可做到这一点。

<>避免数据回环同步

按我们目前所讨论的方案, 获取变更日志之后再重放, 那么一条变更就会在多个机房之间来回同步, 也就是产生了回环问题。

那么如何解决回环问题呢? 其实思路很简单, 就是提供一个机制,让我们在解析重放日志时, 可以判断这个变更是否来自本机房。

以mysql为例, 我们可以利用mysql的事务机制, 在事务的开头和结尾插入同步标志, 在解析时,发现有这个同步标志, 就过滤掉, 不同到别的机房。
落实到具体实现的话, 则是在同步的数据库中创建辅助表, 每次从对端机房同步过来的数据, 都在事务的开头和结尾对辅助表进行相关标志的update操作,
这样就可以在binlog中做区分了。

<>二、冲突处理

既然我们允许多个数据中心对一条数据进行写入, 那么必然会产生这么一种情况: 在数据中心A对数据X进行变更,V从v0->v1 ,
当我们把这个变更同步到数据中心B时, 我们期望update X from v0 to v1, 结果发现在数据中心B,X的值已经是v1’了, 这时候,
就产生了数据冲突。 为了多机房数据的一致性,我们需要处理这种冲突。

<>LWW策略

比较经典的策略是Last Write Wins, 具体的说, 就是为数据的变更加上时间戳, 在同步到别的机房时,如果发现有冲突, 则比较时间戳,
选取时间戳大的那个版本。

这种策略需要注意的问题是,本地机器的时间是不准确的, 各个机器生成的时间戳的大小可能和真实世界中的变更顺序不一致。

Google Spanner利用原子钟和GPS提供了TrueTime API(可以理解为一个全局时钟),
全球各个数据中心的spanserver产生的时间戳都是基于同一参考系,是单调递增的。(不过也不是完全准确,它保证误差在一个范围之内,详细内容可见:
https://ai.google/research/pubs/pub39966
<https://ai.google/research/pubs/pub39966> )

<>逻辑主DC

有了LWW就够了吗? 当然不是。 即使概率很低, 仍然可能发生两个数据的变更时间戳完全一致的情况。 因此我们需要在多个DC中指定一个逻辑主DC,
发生时间戳完全一致的情况时, 用逻辑主DC的数据覆盖别的DC。

<>冲突报告订阅

对于一些敏感数据的变更(比如facebook需要下线一些恐怖主义的帖子),当发生数据冲突时,则不能简单的通过时间戳来决定选择哪个数据版本。
否则,可能会发生被审核人员下线的帖子, 又被重新放出的情况。

这时候,我们需要提供数据冲突报告, 比如在发生数据冲突时,把冲突情况发送到消息队列, 下游根据具体的业务逻辑来处理这种冲突, 决定选用哪个版本的数据。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信