今天,看了58沈剑的一篇文章《冗余数据一致性,到底如何保证?》,觉得写得非常的不错,因此做了一些实验,然后把内容分享出来。
在什么情况下,需要数据冗余
当我们在设计数据库的时候,因为某些数据库中的数据量非常大,所以我们常常会选择进行数据库的切割,而针对数据量的这种情况,水平切割是一种常用的方式。
当使用水平切割时,我们通常会选择一个切割的维度,也就是选择一个patition key。我现在在做一个B2B的平台,每天会有大量的订单,作为店铺,会有一个ShopId,作为买家,会有一个UserId。
当我们查询店铺订单时,如果ShopId是我们的patition key,那查询就比较简单,但,这是如果我们想要查询买家的订单时,就需要查询多库。反之,如果UserId是patition key,那么买家查询方便了,但是店铺查询就麻烦了。
针对这种情况,我们就可以把同一个数据冗余两份,一个使用UserId分库,一个使用ShopId分库。
如何有效的进行数据冗余呢?
1)服务同步双写
如果我们需要数据冗余,那么可以在服务执行时,同时向两个数据库写入数据。
这种解决方案的优点就是:
- 简单,代码中直接向两个数据库插入数据就行了;
- 数据一致性相对较高,因为两次插入完成后才回返回。
当然,缺点也比较明显:
- 需要同时进行两次写操作,时间花费多。(实际测试中,其实人多一次写还是两次写的时间上并不敏感);
- 可能存在数据不一致,例如第一次写成功后,服务断开。
假设,我们需要提高处理的时间,那么我们可以使用下面这种解决方案,也是一种比较常用的解决方案。
2)服务异步双写
在这个方案里,通过引入了消息总线,和数据同步中心,让数据不再是通过服务来一次完成双写了。服务会异步向消息总线发出一个消息,然后通知数据同步中心来完成冗余数据的写入。
这个方案的优点我们已经说过了,就是:
- 处理时间快。由于只请求一次数据库,其他都异步的完成。
当然,缺点也应运而生:
- 系统复杂性增加了。因为多了一个消息总线和数据同步中心服务。(对于已经使用消息总线的小伙伴就相对简单许多);
- 数据极短时间内无法一致。由于第二次写入是异步的,可能在请求返回时,异步数据写入还没有完成,因此,会有短暂的数据不一致。(绝大多数业务场景中,这一点点时间的不一致大多都可以忽略);
- 消息总线可能丢失消息,那么数据就会出现不一致。(这种情况极少出现)。
当然,沈剑还介绍了第三种方案,这种我没有试验过,说是能够实现冗余数据的解耦。
3)线下异步双写
这种方式类似于数据库的订阅同步一样,冗余数据的写入不再是通过服务来完成的了,而是通过一个线下的服务或者任务来完成的。
线下的服务或者任务通过读取日志记录来完成冗余数据的写入。
这种方式的优点:
- 将冗余数据的写入和业务完全解耦,怎么去写冗余数据和业务服务以及无关了;
- 处理时间快。(原因同第二种方式)。
缺点当然也是有的:
- 数据不一致性,和第二种方式一样,这也是异步插入冗余数据,那就会有短暂的时间出现数据不一直;
- 线下服务必须可靠,不然数据一致性难保证。
不管我们使用哪种方式,系统总有可能出现这样那样的异常,特别是在高并发的情况下。当这些异常出现时,我们就需要一个手段,来将不一致的数据最终实现一致。
因此,就必须完成:异步检测、异步修复。
如何保证数据的最终一致性
沈剑在他的文章里介绍了三种方法,这里,我也就将这三种方法分享给大家。
1)线下全量数据扫描
线下启动一个离线的扫描工具,不停的扫描对比T1表和T2表的数据,一旦发现数据不一致,就进行修复。
这样做的优点:
- 简单,开发的方便;
- 无需修改线上服务,离线扫描工具与线上业务完全解耦。
缺点也非常明显:
- 扫描效率低,大部分时间,都在扫描已经一致的数据;
- 扫描量大,因此同步数据的时间周期比较长。
有没有办法不用每次都扫描全部数据,只是有针对性的进行扫描呢?
2)线下增量数据扫描
我们修改线上的服务,让它在每次写入数据的时候,同时写入一个日志。然后,我们同样需要一个离线的扫描工具,只是这个扫描工具每次只是增量的扫描日志,如果发现日志数据不一致,就进行修复。
这样做以后:
- 虽然增加了一些步骤,但依旧非常简单,开发的难度不高;
- 数据扫描效率提高了,只需要扫描增量数据。
但:
- 我们需要对线上服务进行调整(只是多写两次日志,非常简单);
- 虽然扫描效率提高了,但是实时性还是不高,主要是取决于扫描增量数据的周期时间。
有没有一个能够实时监测的方式呢?
3)线上实时监测“消息对”法
这次,我们将服务进行改造,不在写入日志,改成向总线发送一个消息。在数据写入成功后,就发送一个消息,两次写入,就发送了两次消息。这时,我们不再需要一个扫描工具了,而是需要一个实时订阅消息的服务,不停的去收这些消息,当收到第一次写入成功的消息后一定时间内,没有收到第二次消息,就去检测数据的一致性,如果发现不一直,就进行修复。
这样做以后:
- 实时性高;
- 效率高。
但也有缺点:
- 方案比较复杂,需要使用到消息总线;
- 需要一个订阅总线的检测服务。
具体我们需要使用哪种解决方案,就需要根据我们实际的业务需求来判断了。技术本来就没有一个绝对的方法,我们需要考虑投入和产出,因此,不一定缺点明显的方案就不是好方案。
閱讀更多 會技術的葛大爺 的文章