互联网一致性架构设计 -- 事务一致性
按业务区分
- 单库事务
- 多库事务
例子:用户下了一个订单,需要修改余额表,订单表,流水表
单库事务
start transaction; CURDtable t_account; any Exception rollback; CURDtable t_order; any Exceptionrollback; CURDtable t_flow; any Exceptionrollback; commit;
多库事务:将缘分一起执行的事务,拆分成多个小的
start transaction1; //第一个库事务执行 CURDtable t_account; any Exception rollback; … // 第一个库事务提交 commit1; start transaction2; //第二个库事务执行 CURDtable t_order; any Exceptionrollback; … // 第二个库事务提交 commit2; start transaction3; //第三个库事务执行 CURDtable t_flow; any Exceptionrollback; … // 第三个库事务提交 commit3;
结果:分库后分成了多个小的事务,就不能实现原子性,无法达到事务的效果。
优化方案
- 补偿事务
- 先执行完,最后一起提交
补偿事务
补偿事务是一种在业务端实施业务逆向操作事务,来保证业务数据一致性的方式。
例如:
举个栗子,修改余额表事务为 int Do_AccountT(uid, money){ start transaction; //余额改变money这么多 CURDtable t_account with money; anyException rollback return NO; commit; return YES; } 那么补偿事务可以是: int Compensate_AccountT(uid, money){ //做一个money的反向操作 returnDo_AccountT(uid, -1*money){ }
缺点:
- 不同的业务要写不同的补偿事务,不具备通用性
- 没有考虑补偿事务的失败
- 如果业务流程很复杂,if/else会嵌套非常多层
先执行完,最后一起提交
依然是这个拆分后的几个小事务
start transaction1; //第一个库事务执行 CURDtable t_account; any Exception rollback; … // 第一个库事务提交 commit1; start transaction2; //第二个库事务执行 CURDtable t_order; any Exceptionrollback; … // 第二个库事务提交 commit2; start transaction3; //第三个库事务执行 CURDtable t_flow; any Exceptionrollback; … // 第三个库事务提交 commit3;
优化前
trx1.exec(); trx1.commit(); trx2.exec(); trx2.commit(); trx3.exec(); trx3.commit(); 第一个事务执行200ms,提交1ms; 第二个事务执行120ms,提交1ms; 第三个事务执行80ms,提交1ms;
优化后
trx1.exec(); trx2.exec(); trx3.exec(); trx1.commit(); trx2.commit(); trx3.commit(); 第一个事务执行200ms; 第二个事务执行120ms; 第三个事务执行80ms; 第一个事务执行1ms; 第二个事务执行1ms; 第三个事务执行1ms;
为什么这么做?
优化前:第一个事务成功提交之后,最后一个事务成功提交之前,如果出现问题(例如服务器重启,数据库异常等),都可能导致数据不一致。这个时间比较长,共403ms。
优化后:第一个事务提交后的,最后一个事务提交前,这个时间比较短,共3ms,这样就能尽可能减少出现数据不一致的概率。而且还通用,减少编码量。
缺点:还是存在不一致的情况,但是已经大大降低。
两种优化方案的比较
事务提交时会释放数据库的连接,第一种方案,第一个库事务提交,数据库连接就释放了,后置事务提交的方案,所有库的连接,要等到所有事务执行完才释放。这就意味着,数据库连接占用的时间增长了,系统整体的吞吐量降低了。