统一信息资源库秘笈(一):如何解决事务一致性问题?
2018-09-10 10:51:25 来源: 开普云 作者:

在很多业务系统中,都存在事务一致性方面的需求。传统的软件系统大多是以单体应用形式存在的,只操作单个数据库。我们通常只需借助开发平台中特有数据访问技术和框架(如Spring、JDBC、ADO.NET),结合关系型数据库自带的事务管理机制就可以实现事务性的需求。统一信息资源库是一款基于微服务架构的分布式系统,一个看起来简单的功能,内部可能就需要调用多个“服务”并操作多个数据库或分片来实现,事务一致性的实现技术就会复杂很多。


CAP理论

业界著名的CAP理论告诉我们,在一个分布式系统中,一致性(Consistency:分布式环境下多个节点的数据是否强一致)、可用性(Availability:分布式服务能一直保证可用状态,当用户发出一个请求后,服务能在有限时间内返回结果)和分区容忍性(Partition Tolerance:特指对网络分区的容忍性)这3 个要素最多只能同时满足两个,不可兼得。其中,分区容忍性又是不可或缺的。

分布式系统中的事务一致性,是指多个节点中存放的相同数据内容是否一致。主要是由于分布式系统写入和读取数据可能不在同一台机器上,而这必然导致存在一个“不一致时间窗口”,会有一段时间不同机器上所存的数据不一致的情况。根据时间窗口特性,存在三种一致性模型。


一致性模型

数据的一致性模型,一般可以分成以下三种类型:

1、强一致性

当更新操作完成之后,任何后续进程和线程的访问都会获取到最新更新过的值。这种对用户是最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据 CAP 理论,这种实现需要牺牲可用性。

2、弱一致性

系统并不保证后续进程和线程的访问都会获取到最新更新过的值。系统在数据写入成功之后,不承诺立即可以读到最新写入的值,也不会具体地承诺多久之后可以读到。

3、最终一致性

弱一致性的特定形式。系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。在没有故障发生的前提下,不一致窗口的时间主要受通信延迟,系统负载和复制副本的个数影响。

传统的分布式事务实现,是实现两阶段提交协议或三阶段提交协议。但是两阶段提交、三阶段提交存在如下的局限性,并不适合在统一信息资源库中使用:

  • 所有的操作必须是事务性资源(比如数据库、消息队列、EJB组件等);
  • 存在使用局限性,不支持HTTP方式的调用,比较适合传统的单体应用;
  • 由于是强一致性,资源需要在事务内部等待,性能影响较大,吞吐率不高,不适合高并发与高性能的业务场景。

在统一信息资源库中,为了保障系统的高可用性和高性能,我们将强一致性需求转换成最终一致性的需求,实现了基于消息队列方式的数据最终一致性。核心思想是将需要分布式处理的任务通过消息队列方式来异步执行,再通过业务规则进行失败重试,同时要求各服务的接口是幂等的,从而保证了系统数据的最终一致性。


基于消息队列的最终一致性

我们以统一信息资源库中的存储信息变更为例来说明基于消息队列的事务一致性处理过程。

信息资源的存储信息变更,需要通知到缓存系统、关系型数据库系统、noSQL系统以及分布式文件系统,由各个存储子系统根据消息内容,处理各自的业务。存储中心作为消息队列的生产者,添加通知到消息队列中。各存储子系统订阅消息,根据变更消息的内容处理本系统的业务数据,并记录业务处理信息到日志系统。

消息出列后,消费者对应的业务操作要执行成功。如果业务执行失败,消息不能失效或者丢失,需要保证消息消费与业务操作的结果一致。

为了解决消息被重复消费,我们通过一个“消费状态表”来记录消费状态。在执行消费操作之前,检测下该消息(提供标识)是否已经消费过,消费完成后,通过本地事务控制来更新这个“消费状态表”。这样子就避免重复消费的问题。

分布式服务的事务一致性对衍生的配套系统要求比较多,需要考虑消息的积压、消费情况、监控、报警等。


小结

分布式系统的事务一致性本身是一个技术难题,目前没有一种很简单很完美的方案能够应对所有场景。分布式系统的一个难点就是因为“网络通信的不可靠”,只能通过“确认机制”、“重试机制”、“补偿机制”等各方面来解决一些问题。在综合考虑应用场景、可用性、性能、实现复杂度等各方面的情况上,我们选择了基于消息队列的最终一致性方案,很好地解决了统一信息资源库的分布式事务一致性问题,满足了海量数据的一致性存储和管理的需求。