我们看一个跨库事务一致性的问题,这是一个简单的场景:有新老两个系统。相应新老两套数据库。新数据库採用分库分表的设计。考虑到项目公布之后可能存在风险,採取了新老系统的并行方案。这个系统的业务比較简单:接收来自外部的数据。然后对数据进行核对处理。为了保证新老系统可以并行。在接收数据的时候必须实现双写方案,从而导致了跨库事务的一致性问题。
以下一幅图展示这一简单的场景
这里面会存在一个小问题,就是可能存在写入老库成功,可是写入新库失败的场景。
我们假设出现这样的概率的情况是百万分之中的一个,在系统公布的情况下,这样的概率可能更高。从眼下我们的数据量来看,一天大概5000W。那么出现不一致的数据量在500条。
考虑到这个是数据核算系统,不能有一条丢失的情况,否则两边比对结果可能会不一致。所以须要保证一致性。
这样的问题,有以下几种解决方式
1 考虑使用JTA等支持分布式事务的事务管理器
这样的方案的优势就是直接有现成的解决方式,一般的j2eeserver都提供了JTA的相关的实现。比較明显的问题就是解决方式太重量级。一般JTA除了server要支持,相应的数据库服务厂商一般也要提供相应的商业支持。主要是提供基于 XAResource JDBC驱动,这一些商业上的支持,部分是须要付费的。
并且使用XA 数据库驱动,本身可能导致一些潜在的问题,尤其是基于不同的数据库厂商的时候。而XA是基于两阶段提交协议。事务管理器为了完毕一个事务,须要多次和数据库通信,效率上比較低。
2 考虑使用数据库自身的数据同步机制
假设新老库的结构基本一样,这样的方案还是比較靠谱的。
也是比較简单的方案。
这样的方案的局限性也再次。在本项目中。新库不是一个物理库。而是多个物理库,而老库是一个物理库。
假设要用数据库自身的同步机制,涉及到多个库和一个库之间的数据复制。同一时候因为分表的方案也不一样,导致两边做一个映射的配置,而这个须要在数据库层面进行,逻辑相当的复杂。解决方式成本也比較高。
相当于把重要的分库分表的逻辑在数据库这一层又一次实现了一份。
事实上这个也带来一个维护问题。一旦我们认为新系统已经足够稳定。
应用程序可以之间在写入库进行切换,把老库的逻辑切掉,从而实现了仅仅写新库的需求。整个过程也不须要进行再次公布。而数据库的方案则须要停掉脚本,在多个地方进行配置。
3 在old库存放同样的两张模型表。一张表用于old库的持久化表。另外一张作为暂时表,主要是作为须要同步到到新库的数据。
假设已经同步到新库。就删除。假设没有同步到新库就同步到新库。这个过程採用定时机制,每分钟定时提取暂时表一定数据量的数据。批量导入到新库。
通过努力重试,来保证一致性。而新库则须要保证幂等性。保证数据仅仅会同步过一次。普通情况下,则是通过数据特征标识符来识别,这个一般都是数据的唯一性主键。
以下是简单的实现:
这三种方案的主要思想就是 採取重试机制,这个仅仅是分布式事务里面的一种模型,相应的还有两阶段提交,异常恢复补偿等机制。