引言:MySQL存储引擎主要分为InnoDB、MyISAM,它们的主要区别是InnoDB支持事物,而MyISAM不支持。生产环境中我们要尽量使用InnoDB来保证数据的完整性,损失一点点性能也没有太大关系,毕竟我们的数据库是需要做分布式。今天我们就来聊一聊如何在分布式数据库当中用好事物。特别强调:数据库的事物是跟着connection走的,通俗点来讲将不同库的SQL操作放在同一个事物当中是没有意义的。(15年刚出来工作的时候踩过这个坑,还一直在纠结明明用了事物为什么两张表的数据不同步,因为这两张表在不同的库。你妹的,没文化,真可怕。)
一、多个数据要同时操作,用事物来保证数据的完整性,以及一致性。
用户下了一个订单,需要修改余额表,订单表,流水表,于是会有类似的伪代码:
start transaction;
CURD table t_account; any Exception rollback;
CURD table t_order; any Exception rollback;
CURD table t_flow; any Exception rollback;
commit;
如果对余额表,订单表,流水表的SQL操作全部成功,则全部提交
如果任何一个出现问题,则全部回滚
事务,以保证数据的完整性以及一致性。
引发的问题:互联网的业务特点,数据量较大,并发量较大,经常使用拆库的方式提升系统的性能。如果进行了拆库,余额、订单、流水可能分布在不同的数据库上,甚至不同的数据库实例上,此时就不能用数据库原生事务来保证数据的一致性了。
二、如何处理好跨库事物?
单库是用这样一个大事务保证一致性:
start transaction;
CURD table t_account; any Exception rollback;
CURD table t_order; any Exception rollback;
CURD table t_flow; any Exception rollback;
commit;
拆分成了多个库后,大事务会变成三个小事务:
start transaction1;
CURD table t_account; any Exception rollback;
commit1; #第一个事物提交
start transaction2;
CURD table t_order; any Exception rollback;
commit2; #第二个事物提交
start transaction3;
CURD table t_flow; any Exception rollback;
commit3; #第三个事物提交
一个事务,分成执行与提交两个阶段:
执行(CURD)的时间很长
提交(commit)的执行很快
第一个事务执行200ms,提交1ms;
第二个事务执行120ms,提交1ms;
第三个事务执行80ms,提交1ms;
1、在什么时候,会出现不一致?
第一个事务成功提交之后,最后一个事务成功提交之前,如果出现问题(例如服务器重启,数据库异常等),都可能导致数据不一致。
2、如何解决这个问题呢?
可以改变事务执行与提交的时序,变成事务先执行,最后一起提交,类似于以下代码。
start transaction1;
CURD table t_account; any Exception rollback;
start transaction2;
CURD table t_order; any Exception rollback;
start transaction3;
CURD table t_flow; any Exception rollback;
commit1; #第一个事物提交
commit2; #第二个事物提交
commit3; #第三个事物提交
3、这样做的好处是什么呢?
1)串行事务方案,总执行时间是303ms,最后202ms内出现异常都可能导致不一致;
2)后置提交优化方案,总执行时间也是303ms,但最后2ms内出现异常才会导致不一致;
3)虽然没有彻底解决数据的一致性问题,但不一致出现的概率大大降低了。
4、后置提交优化,有什么不足,如何尽量避免这个问题?
1)串行事务方案,第一个库事务提交,数据库连接就释放了;
2)后置提交优化方案,所有库的连接,要等到所有事务执行完才释放;
3)这就意味着,数据库连接占用的时间增长了,系统整体的吞吐量降低了。
4)操作比较频繁的数据表尽量放在后面执行,以免因为其他进程抢占导致死锁。死锁目前为止没有更好的解决方案,无非就是代码层面注意,以及分布式数据库处理。
三、什么是补偿事物?
补偿事物博主有研究过,感觉不太好,这里就不细说了,感兴趣的同学可以自行查找相关文档。