现象
数据字段status
mysql类型tinyint
存储value=2
- 更新语句更新表中的其他字段并且覆盖写入status=2(该字段本身value=2)
- 运行到其他服务的业务出现错误,全局事务回滚
- 其他字段回滚正常,status数据回滚成value=1
排查
测试接口断点卡主, 查看seata的undolog表的数据
// 示例数据(Base64 编码的 Java 序列化数据) 手动去掉0x前缀
String base64Data = "7B22406";
byte[] bytes = DatatypeConverter.parseHexBinary(base64Data);
// 反序列化
String jsonString = new String(bytes, StandardCharsets.UTF_8);
System.out.println(object);
{
"@class": "io.seata.rm.datasource.undo.BranchUndoLog",
"branchId": 2090276239587073061,
"sqlUndoLogs": [
"java.util.ArrayList",
[
{
"@class": "io.seata.rm.datasource.undo.SQLUndoLog",
"sqlType": "UPDATE",
"tableName": "xxxxx",
"beforeImage": {
"@class": "io.seata.rm.datasource.sql.struct.TableRecords",
"tableName": "xxxx",
"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": "status",
"keyType": "NULL",
"type": -7,
"value": true
},
{
"@class": "io.seata.rm.datasource.sql.struct.Field",
"name": "update_time",
"keyType": "NULL",
"type": 93,
"value": [
"java.time.LocalDateTime",
1739256077000
]
}
]
]
}
]
]
},
"afterImage": {
"@class": "io.seata.rm.datasource.sql.struct.TableRecords",
"tableName": "account_member_bank_info",
"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": "status",
"keyType": "NULL",
"type": -7,
"value": true
},
{
"@class": "io.seata.rm.datasource.sql.struct.Field",
"name": "update_time",
"keyType": "NULL",
"type": 93,
"value": [
"java.time.LocalDateTime",
1739256179000
]
}
]
]
}
]
]
}
}
]
]
}
理解
Seata在生成undolog的rollback_info时,转换status的时候把tinyint类型的数据("type": -7) 当做Boolean进行处理了,作为true在保存到rollback_info,当回滚时【value=true】反序列化到【value=1】写入
解决办法
- 状态字段不使用tinyint 使用int或者varchar等其他字段
- 在更新字段时,将status字段置为null,不更新该字段,则不会回滚该字段