多库多事务降低数据不一致概率

一、案例缘起

我们经常使用事务来保证数据库层面数据的ACID特性。

举个栗子,用户下了一个订单,需要修改余额表,订单表,流水表,于是会有类似的伪代码:

start transaction;

CURDtable t_account;  any Exception rollback;

CURDtable t_order;       any Exceptionrollback;

CURDtable t_flow;         any Exceptionrollback;

commit;

如果对余额表,订单表,流水表的SQL操作全部成功,则全部提交,如果任何一个出现问题,则全部回滚,以保证数据的一致性。

互联网的业务特点,数据量较大,并发量较大,经常使用拆库的方式提升系统的性能。如果进行了拆库,余额、订单、流水可能分布在不同的数据库上,甚至不同的数据库实例上,此时就不能用事务来保证数据的一致性了。这种情况下如何保证数据的一致性,是今天要讨论的话题。

二、补偿事务

补偿事务是一种在业务端实施业务逆向操作事务,来保证业务数据一致性的方式。

举个栗子,修改余额表事务为

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){

}

同理,订单表操作为

Do_OrderT,新增一个订单

Compensate_OrderT,删除一个订单

要保重余额与订单的一致性,可能要写这样的代码:

// 执行第一个事务

int flag = Do_AccountT();

if(flag=YES){

//第一个事务成功,则执行第二个事务

flag= Do_OrderT();

if(flag=YES){

// 第二个事务成功,则成功

returnYES;

}

else{

// 第二个事务失败,执行第一个事务的补偿事务

Compensate_AccountT();

}

}

该方案的不足是:

(1)不同的业务要写不同的补偿事务,不具备通用性

(2)没有考虑补偿事务的失败

(3)如果业务流程很复杂,if/else会嵌套非常多层

例如,如果上面的例子加上流水表的修改,加上Do_FlowT和Compensate_FlowT,可能会变成一个这样的if/else:

// 执行第一个事务

int flag = Do_AccountT();

if(flag=YES){

//第一个事务成功,则执行第二个事务

flag= Do_OrderT();

if(flag=YES){

// 第二个事务成功,则执行第三个事务

flag= Do_FlowT();

if(flag=YES){

//第三个事务成功,则成功

returnYES;

}

else{

// 第三个事务失败,则执行第二、第一个事务的补偿事务

flag =Compensate_OrderT();

if … else … // 补偿事务执行失败?

flag= Compensate_AccountT();

if … else … // 补偿事务执行失败?

}

}

else{

// 第二个事务失败,执行第一个事务的补偿事务

Compensate_AccountT();

if … else … // 补偿事务执行失败?

}

}

三、事务拆分分析与后置提交优化

单库是用这样一个大事务保证一致性:

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;

一个事务,分成执行与提交两个阶段,执行的时间其实是很长的,而commit的执行其实是很快的,于是整个执行过程的时间轴如下:


第一个事务执行200ms,提交1ms;

第二个事务执行120ms,提交1ms;

第三个事务执行80ms,提交1ms;

那在什么时候系统出现问题,会出现不一致呢?

回答:第一个事务成功提交之后,最后一个事务成功提交之前,如果出现问题(例如服务器重启,数据库异常等),都可能导致数据不一致。

如果改变事务执行与提交的时序,变成事务先执行,最后一起提交,情况会变成什么样呢:


第一个事务执行200ms;

第二个事务执行120ms;

第三个事务执行80ms;

第一个事务执行1ms;

第二个事务执行1ms;

第三个事务执行1ms;

那在什么时候系统出现问题,会出现不一致呢?

问题的答案与之前相同:第一个事务成功提交之后,最后一个事务成功提交之前,如果出现问题(例如服务器重启,数据库异常等),都可能导致数据不一致。

这个变化的意义是什么呢?

方案一总执行时间是303ms,最后202ms内出现异常都可能导致不一致;

方案二总执行时间也是303ms,但最后2ms内出现异常才会导致不一致;

虽然没有彻底解决数据的一致性问题,但不一致出现的概率大大降低了!

事务提交后置降低了数据不一致的出现概率,会带来什么副作用呢?

回答:事务提交时会释放数据库的连接,第一种方案,第一个库事务提交,数据库连接就释放了,后置事务提交的方案,所有库的连接,要等到所有事务执行完才释放。这就意味着,数据库连接占用的时间增长了,系统整体的吞吐量降低了。

四、总结

trx1.exec();

trx1.commit();

trx2.exec();

trx2.commit();

trx3.exec();

trx3.commit();

优化为:

trx1.exec();

trx2.exec();

trx3.exec();

trx1.commit();

trx2.commit();

trx3.commit();

这个小小的改动(改动成本极低),不能彻底解决多库分布式事务数据一致性问题,但能大大降低数据不一致的概率,带来的副作用是数据库连接占用时间会增长,吞吐量会降低。对于一致性与吞吐量的折衷,还需要业务架构师谨慎权衡折衷

多库多事务降低数据不一致概率相关推荐

  1. 揭露数据不一致的利器 —— 实时核对系统

    本文首发于"Shopee技术团队"微信公众号. 摘要 随着企业业务发展,以及微服务化大趋势下单体服务的拆分,服务间的通信交互越来越多.与单体服务不同,微服务间的数据往往需要通过额外 ...

  2. onbeforeedit和onbeginedit数据不一致_Redis缓存与数据库产生不一致的问题该如何解决?...

    不一致产生的原因 我们在使用redis过程中,通常会这样做:先读取缓存,如果缓存不存在,则读取数据库.伪代码如下: Object stuObj = new Object();public Stu ge ...

  3. mysql256次利用_【案例】【MySQL】一次复杂的主从库数据不一致修复

    修复操作之前,已写好了修复操作方案.回滚方案和规避建议.但是修复过程中,发生了一些意料之外的状况,根据实际情况不断修改方案,直到完成预定目标. 问题描述: 主库已从2b切换到2a,在新主库2a上查询不 ...

  4. Redis学习总结(数据类型、持久化、事务、数据删除策略、主从复制、哨兵、缓存雪崩等)

    Redis学习总结 1.Redis是什么 1.概念 2.特点 3.应用场景 2.Linux环境安装redis 3.Redis的数据存储格式 1.String类型 1.String类型的常用操作 2.S ...

  5. onbeforeedit和onbeginedit数据不一致_深度解读,奔溃一致性、应用一致性的区别

    通常在对数据进行备份时,存在以下三种数据保护的一致性: 1.不一致备份(In-consistent backup),表示备份的数据是正在改变的数据: 2.崩溃一致备份(Crash-consistent ...

  6. MySQL主从数据不一致,怎么办?

    先给大家说个身边的故事. 小伙伴二狗最近面宇宙厂,前面被问MySQL索引.锁.主从复制原理时答的都很开心. 当面试官问到 :"你们遇到主从不一致的问题怎么解决呢?你有什么更好的方案吗?&qu ...

  7. mysql缓存淘汰机制_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....

    在实际的工作项目中, 缓存成为高并发.高性能架构的关键组件 ,那么Redis为什么可以作为缓存使用呢?首先可以作为缓存的两个主要特征: 在分层系统中处于内存/CPU具有访问性能良好, 缓存数据饱和,有 ...

  8. 三年之久的 etcd3 数据不一致 bug 分析

    etcd 作为 Kubernetes 集群的元数据存储,是被业界广泛使用的强一致性 KV 存储,但近日被挖掘出一个存在 3 年之久的数据不一致 bug--client 写入后无法在异常节点读取到数据, ...

  9. redis 查询缓存_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....

    在实际的工作项目中, 缓存成为高并发.高性能架构的关键组件 ,那么Redis为什么可以作为缓存使用呢?首先可以作为缓存的两个主要特征: 在分层系统中处于内存/CPU具有访问性能良好, 缓存数据饱和,有 ...

最新文章

  1. python损失函数实现_pytorch 实现cross entropy损失函数计算方式
  2. 杀毒软件原理后续阶段
  3. 从 2018 年 Nacos 开源说起
  4. uglifyjs报错 webpack_基于vue2.X的webpack基本配置,教你手动撸一个webpack4的配置
  5. 读者来信(1)——项目经理,不要迷信制度!
  6. 体验AJAX Toolkit新控件:AutoCompleteExtender
  7. 第十五章:交互式界面(十一)
  8. Ubuntu下升级安装gcc-7.5.0教程
  9. c语言写法方法加_void,c语言加法程序怎么写
  10. 无线通信数字调制技术
  11. 产品读书.心理学《九型人格》
  12. powerdns 安装部署备忘
  13. 2022年docker面试题大全(持续更新中)
  14. python全栈示例_Python全栈之路--Django ORM详解
  15. 用爬虫批量采集淘宝宝贝评论
  16. 《C语言入门指南》合集版,学习c语言有这一篇就够了?
  17. 音速索尼克 怪人_优势演员评论家方法简介:让我们玩刺猬索尼克!
  18. 论文中 c.f. i.e. s.t. e.g. w.r.t. et al. etc英文缩写是什么意思
  19. python画二维温度云图_lammps温度云图
  20. 基于matlab的单相pwm逆变电路的仿真研究,基于Matlab的单相双极性spwm逆变电路仿真报告...

热门文章

  1. 写一个楼房盘类java语言编写_Java开发每日复盘2018_0514
  2. 数字图像处理(作业四)——边缘表达
  3. 嵌入式Linux系统编程学习之七gdb调试器
  4. 手机ufs测试软件,EFTech eMMC 5.1,UFS 3.0测试工具
  5. 【LeetCode】剑指 Offer 63. 股票的最大利润
  6. ubuntu server 下使用subversion的总结【原创】
  7. jQuery检查某个元素在页面上是否存在
  8. sql server 事务与try catch
  9. 使用三级缓存解决内存溢出
  10. 阅读构建之法10、11、12章