基本可用软状态最终一致事务
本用例分两个数据库分别是用户库和交易库,不使用分布式事务,使用基于消息驱动实现基本可用软状态最终一致事务(BASE)。现在说明下事务逻辑演化步骤,尊从CAP原则,即分布式系统不能全部确保一致性、可用性、分区容错性,只能三选二。文章里从一致性模式讨论,例子里每次出售物品时,将一行添加到交易表中,并更新买方和卖方的数量。 使用ACID风格的事务这是强一致性事务,SQL将如图所示。
用户表中的购买量和出售量被认为是事务表的缓存。 在要求效率系统里,可以放宽对一致性的限制。 延迟设定买方和卖方的缓存值,使其持有的余额不会立即反映交易的结果。 这样的做法很常见,为了效率牺牲强一致性,事实上人们经常在交易与查询余额之间遇到这种延迟(例如ATM取款和手机通话)。是否使用弱一致性取决于如何定义运行余额。 我们现在已将更新用户表和事务表解耦。 不使用同一事务操作两个表一致性是不能保证的。 事实上,第一个和第二个事务之间的失败将导致用户表永久不一致,但如果合同规定运行总计是估计值,这不一致是可以接受。如图所示。
如果缓存估值不能接受的话, 然后还是解耦用户和交易更新,这要怎么办呢? 引入持久消息队列解决问题。 使用持久性消息有几种选择,然而实现消息队列最关键的因素是确保消息备份持久化与数据库位于同一资源上。 消息驱动触发机制,添加交易记录与消息排队是同数据源事务,更新用户数据与消息出队是同数据源事务,这叫避免使用2PC提高效率。但是同一资源使系统可用性没有提高。如图所示。
要提高系统可用性就要将交易表和用户表分库处理,但是有一个问题,消息持久化在交易库主机上,避免排队期间的2PC,那消息在涉及用户库主机的事务中出现,我们仍然有一个2PC的情况。消息处理组件中的2PC的一个解决方案是什么都不做,通过将更新事务解耦为单独的后端组件,可以保留面向客户的组件的可用性。 在业务需求中,后端组件消息处理的可用性较低是可以接受的。2PC消息流如图:
Message flow
Coordinator Cohort
QUERY TO COMMIT
-------------------------------->
VOTE YES/NO prepare*/abort*
<-------------------------------
commit*/abort* COMMIT/ROLLBACK
-------------------------------->
ACKNOWLEDGMENT commit*/abort*
<--------------------------------
end
但是假设你的系统性能要求很高绝对不能接受2PC,这个问题怎么解决?首先,你需要了解幂等概念。同一操作应用一次或多次结果都相同就是幂等。 幂等运算是有用的,因为它们允许部分故障,因为重复应用它们不会改变系统的最终状态。例子中寻找幂等操作是有个问题,更新操作很少是幂等的。 多次操作增加余额会导致不正确的余额。 然而即使是简单地设置值更新操作对于操作顺序也不是幂等的。 如果系统不能保证按照接收到的顺序更新,系统的最终状态将不正确。在这种余额更新下,您需要一种方法来跟踪哪些更新已成功,哪些更新尚未完成,这种技术是记录已处理事务标识符的表。查询事务是否在已处理表是幂等的。如果需要,可以使用两个独立的事务完成此操作:一个在消息队列中,一个在用户数据库上。 除非数据库操作成功提交,否则队列操作不会提交。 该算法现在支持部分故障,并且仍然提供事务保证而不诉诸于2PC。
如果不考虑购买排序情况下则有一种更简单的技术来确保幂等更新。添加跟踪用户的最后销售和购买日期,修改用户表如图8。更新最后销售或购买日期小于当前消息日期的记录数据,逻辑如图9。这方案有个问题,假设在短时间内进行两次购买,我们的消息系统不能保证有序入队操作,后购买消息可能先入队,更新操作会丢失先购买消息。您还可以尝试单向递增的交易ID代替最后时间。