Seata

2019年1月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案

Simple Extensible Autonomous Transaction Architecture,简单可扩展自治事务框架

2020起初,参加工作后用1.0以后的版本

再看TC/TM/RM三大组件

1610803445722.png

TC: seata服务器

TM: 事务的发起方,标有 @GlobalTransactional注解的方法

RM: 事务的参与者,与数据库打交道

分布式事务的执行流程

  1. TM开启分布式事务(TM向TC注册全局事务记录)
  2. 换业务场景,编排数据库,服务等事务内资源(RM向TC汇报资源准备状态)
  3. TM结束分布式事务,事务一阶段结束(TM通知TC提交/回滚分布式事务)
  4. TC汇总事务信息,决定分布式事务是提交还是回滚
  5. TC通知所有RM提交/回滚资源,事务二阶段结束。

AT模式如何做到对业务的无侵入

是什么

1610803447023.png

一阶段加载

业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源

在一阶段,Seata会拦截“业务SQL”,
1、解析SQL语义,找到“业务SQL”要更新的业务数据,在业务数据被更新前,将其保存成”before image”
2、执行“业务SQL”更新业务数据,在业务数据更新之后,
3、其保存成”after image” ,最后生成行锁。
以上操作全部在一个数据库事务内完成, 这样保证了一阶段操作的原子性。

1610803447141.png

二阶段提交:提交异步化,非常快速地完成

二阶段如果顺利提交的话,因为“业务 SQL”在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉, 完成数据清理即可。

1610803447230.png

二阶段回滚:回滚通过一阶段的回滚日志进行反向补偿

阶段如果是回滚的话,Seata就需要回滚一阶段已经执行的“业务SQL” ,还原业务数据。
回滚方式便是用”before image” 还原业务数据;但在还原前要首先要校验脏写,对比”数据库当前业务数据”和“after image”如果两份数据完全一 致就说明没有脏写,可以还原业务数据,如果不一 致就说明有脏写,出现脏写就需要转人工处理。

1610803447356.png

整体流程:

1610803447481.png

debug

在账户余额服务中打断点

1610803447516.png

浏览器访问: http://localhost:2001/order/create?userId=1&productId=1&count=2&money=100

服务端表情况

1610803447607.png

1610803447634.png

1610803447666.png

客户端表情况

1610803447712.png

1610803447743.png

1610803447787.png

看看rollback_info字段内容

包含前置镜像和后置镜像

1610803447826.png

  1. {
  2. "@class": "io.seata.rm.datasource.undo.BranchUndoLog",
  3. "xid": "192.168.1.252:8091:2019296988",
  4. "branchId": 2019296997,
  5. "sqlUndoLogs": [
  6. "java.util.ArrayList",
  7. [
  8. {
  9. "@class": "io.seata.rm.datasource.undo.SQLUndoLog",
  10. "sqlType": "UPDATE",
  11. "tableName": "t_account",
  12. "beforeImage": {
  13. "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
  14. "tableName": "t_account",
  15. "rows": [
  16. "java.util.ArrayList",
  17. [
  18. {
  19. "@class": "io.seata.rm.datasource.sql.struct.Row",
  20. "fields": [
  21. "java.util.ArrayList",
  22. [
  23. {
  24. "@class": "io.seata.rm.datasource.sql.struct.Field",
  25. "name": "id",
  26. "keyType": "PRIMARY_KEY",
  27. "type": -5,
  28. "value": [
  29. "java.lang.Long",
  30. 1
  31. ]
  32. },
  33. {
  34. "@class": "io.seata.rm.datasource.sql.struct.Field",
  35. "name": "used",
  36. "keyType": "NULL",
  37. "type": 3,
  38. "value": [
  39. "java.math.BigDecimal",
  40. 0
  41. ]
  42. },
  43. {
  44. "@class": "io.seata.rm.datasource.sql.struct.Field",
  45. "name": "residue",
  46. "keyType": "NULL",
  47. "type": 3,
  48. "value": [
  49. "java.math.BigDecimal",
  50. 1000
  51. ]
  52. }
  53. ]
  54. ]
  55. }
  56. ]
  57. ]
  58. },
  59. "afterImage": {
  60. "@class": "io.seata.rm.datasource.sql.struct.TableRecords",
  61. "tableName": "t_account",
  62. "rows": [
  63. "java.util.ArrayList",
  64. [
  65. {
  66. "@class": "io.seata.rm.datasource.sql.struct.Row",
  67. "fields": [
  68. "java.util.ArrayList",
  69. [
  70. {
  71. "@class": "io.seata.rm.datasource.sql.struct.Field",
  72. "name": "id",
  73. "keyType": "PRIMARY_KEY",
  74. "type": -5,
  75. "value": [
  76. "java.lang.Long",
  77. 1
  78. ]
  79. },
  80. {
  81. "@class": "io.seata.rm.datasource.sql.struct.Field",
  82. "name": "used",
  83. "keyType": "NULL",
  84. "type": 3,
  85. "value": [
  86. "java.math.BigDecimal",
  87. 100
  88. ]
  89. },
  90. {
  91. "@class": "io.seata.rm.datasource.sql.struct.Field",
  92. "name": "residue",
  93. "keyType": "NULL",
  94. "type": 3,
  95. "value": [
  96. "java.math.BigDecimal",
  97. 900
  98. ]
  99. }
  100. ]
  101. ]
  102. }
  103. ]
  104. ]
  105. }
  106. }
  107. ]
  108. ]
  109. }

事务的回滚和恢复就是根据 afterImage 和 beforeImage来完成的