2018年11月28日 有两个客户在两个渠道购买了同一产品每人各买2笔 系统应在29日做成交处理, 成交结束后, 更新一张记录表, 记录表根据产品代码和渠道代码作为Unique. 成交使用已客户为维度的多线程成交. // 方法名为虚拟捏造, 并非实际使用方法名 成交方法 chengjiao() 为独立事务; chengjiao() 方法内使用多线程的嵌套事务 NESTED doChengjiao() 伪代码
// 独立事务
chengjiao() {
// 根据客户查出交易
List<Trade> lists = getTradeList();
new Thread ... (list);
}
// 嵌套事务
doChengjiao();假如数据为 渠道 001 产品 002 渠道 002 产品 002 那更新的记录两条线程都要取更新表里面更新 001&002记录 和 002&002; 但是问题出在线程的执行顺序; 两个客户每个人在不同渠道买了一笔, 一共四笔交易记录; 线程A先去更新了 001 & 002 这条记录 线程B先去更新了 002 & 002 这条记录 之后 线程B又去更新 001 & 002 这条记录; (问题在这已经出现) 线程A去更新 002 & 002 这条记录;
后续的线程B在更新的时候, 在等待这条记录之前的UPDATE事务提交或回滚, 而在占用这条记录的线程A想要提交需要等待002 & 002 这条记录提交或回滚, 而002 & 002这条记录正好被B线程占用, 由此造成了互相等待, 将更新表锁住.后续交易无法进行.后经人为干预(数小时后发现), 杀掉其中一条会话, 导致该会话数据回滚, 而另一个会话因为数据库等待时间过长, 数据也没有进行提交, 最后导致4笔交易全部回滚. 如果问题出在这也就没什么. 问题是每天这几笔交易都恰巧同时执行.就一直卡死. 最后在12月3号, 4笔交易成交了. 4个工作日.问题影响… 客户是拒绝的… 不过好在客户大度, 没有计较. … …
至此将问题从生产日志取下, 分析, 复现, 解决,重新上线 共计 2周+, 期间对spring事务感悟颇深. 遂总结此文章. 整理, 学习.
隔离级别 | 脏读 | 不可重读 | 幻读 |
|---|---|---|---|
读操作未提交 | 可能 | 可能 | 可能 |
读操作已提交 | 不可能 | 可能 | 可能 |
可重读 | 不可能 | 不可能 | 可能 |
串行化 | 不可能 | 不可能 | 不可能 |
画重点
spring的事务管理中, 让我们容易出现问题的几个传播约定
1是默认传播机制, 2是嵌套传播机制; REQUIRED 如果你没有, 我就自己管自己, 如果有, 就用你的; NESTED 如果有, 我就听你的, 如果没有, 我就按照默认的走;
举例说明: fun1() 方法1 是一个带事务的方法, 我们将使用fun1()来调用, fun2(), 此时的方法2 fun2()我们在配置事务的时候
当fun1() 方法1 不是一个带事务的方法 , 此时 REQUIRED 与 NESTED 意义相同; 都将自身新启事务. 独立提交或回滚;