AT 模式时基于XA事务模型研究而来
在业务流程中进行操作数据库时,Seata会给予数据源代理对原执行的SQL进行解析。
然后将业务数据在更新前后保存到 undo_log 日志表中,利用本地事务的ACID特性,把业务数据的更新和回滚日志写入同一个本地事务中进行提交
我把 rollackInfo 字段保存的值转成字符进行查看
{
"@class":"io.seata.rm.datasource.undo.BranchUndoLog",
"xid":"192.168.137.1:8091:3080761116565270529",
"branchId":3080761116565270532,
"sqlUndoLogs":[
"java.util.ArrayList",
[
{
"@class":"io.seata.rm.datasource.undo.SQLUndoLog",
"sqlType":"INSERT",
"tableName":"sys_order",
"beforeImage":{
"@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords",
"tableName":"sys_order",
"rows":[
"java.util.ArrayList",
[
]
]
},
"afterImage":{
"@class":"io.seata.rm.datasource.sql.struct.TableRecords",
"tableName":"sys_order",
"rows":[
"java.util.ArrayList",
[
{
"@class":"io.seata.rm.datasource.sql.struct.Row",
"fields":[
"java.util.ArrayList",
[
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"id",
"keyType":"PRIMARY_KEY",
"type":-5,
"value":[
"java.lang.Long",
80
]
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"order_no",
"keyType":"NULL",
"type":12,
"value":"c8f8f9d9-c130-4efd-bc8d-c41b092d607c"
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"price",
"keyType":"NULL",
"type":3,
"value":[
"java.math.BigDecimal",
10
]
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"sku_code",
"keyType":"NULL",
"type":12,
"value":"00001"
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"sku_name",
"keyType":"NULL",
"type":12,
"value":"肘å"
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"total",
"keyType":"NULL",
"type":3,
"value":[
"java.math.BigDecimal",
10
]
},
{
"@class":"io.seata.rm.datasource.sql.struct.Field",
"name":"user_id",
"keyType":"NULL",
"type":-5,
"value":[
"java.lang.Long",
1
]
}
]
]
}
]
]
}
}
]
]
}
beforeImage 和 afterImage 保存了操作前和操作后数据用于发生异常数据回滚,
在这里可以看出,AT模式与XA模式最大的不同点,本地事务执行完成立刻提交马上释放资源,根据保存到数据库中undo_log进行异常补偿操作。AT模式实际上降低了锁的范围,从而提升了分布式事务的处理效率。
TC接收到了所有分支事务的执行结果,决定对全局事务进行提交或者回滚操作
如果决定全局提交,说明此时所有分支事务都已经完成了提交,所以只需要清除undo_log就可以了。
如果决定回滚,则根据undo_log中的记录进行补偿,如果全局事务回滚成功,数据一致性就得到了保证
在AT模式中,当多个全局事务操作同一张表时,他的事务隔离型保证时通过全局锁来实现的。
我们假设
两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。
在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted) 。
如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。
SELECT FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是 已提交 的,才返回。
出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。