数据库很多都支持嵌套事务,还可以通过savepoint功能设置部分回滚功能,但是这个功能我没有用过,不过遇到一些坑需要记录一下。
业务复杂的时候,代码嵌套比较多, 事务里面开事务,除了业务计算逻辑,也可能存在其他比如RPC请求,redis加锁、发送消息等逻辑。
1. redis解锁:
嵌套事务里面进行了redis加锁和解锁,或者说一段包含redis加锁和解锁的函数被外面的代码在事务里面调用了,redis解锁时机是在函数执行完成后,但是外面的事物还没有提交。相当于锁提前释放了,可能对业务有影响。
尽量将redis锁往前提,放在事务外。
2.事务消息:
为了保证发送异步消息(mq)的可靠性,先将消息和业务数据在同一个事务写入数据库,然后在aftercommit的callback里异步执行发送异步任务的功能,如果消息发送失败然后通过周期任务定时扫描消息表中的未发送成功的数据。在嵌套事务场景下会存在严重问题。
-
内部事务成功,执行aftercommit,发送消息,标记数据库里面的消息已发送,但是主事务还没有提交成功,会提示找不到记录,后期定时任务扫描会重复扫到数据,导致数据重复发送。
-
内部事务成功,执行aftercommit,发送消息,但是最终主事务没有提交成功,rollback了,但是消息已经发出去了,业务逻辑出错了。
这个需要自己封一个额外的包,保证事务消息只主事务提交以后才发送。
- 事务消息发送流程是:待发送消息的数据写消息表后,执行发送逻辑,标记消息表数据删除。但是定时任务在写消息表后,标记删除前这个间隔执行了,所以定时任务可以看到未发送成功的消息,但是此时其实已经正在执行发送了。
可以发送和删除前都用select for update 但是没有必要,目前采用的方法是,定时任务只扫描当前时间前10s的,这个10s远超过发送业务消息的消耗时间。
网友评论