世界今热点:一个事务插入,另外一个事务更新操作,是否会更新成功?

2023-04-13 15:30:21    来源:博客园


(相关资料图)

1.前言

同样另外一个非常有意思的题目,值得我们思考。大概背景是这个样子的。如果有一个事务A进行插入 id > 100, 同时另外一个事务B进行更新update id > 100。那么事务B是否会更新成功。我们来画一个时序图:

time事务A事务B备注
T1insert id > 100 set status = 1
T2update id > 100 set status = 2
T3最后id > 100 status是为1 还是为2呢
2.代码

我们从事务的四个隔离性来分别讨论这个问题。所有代码如下,仅仅是隔离性级别修改。修改是status状态。以下所有操作类似于打开两个浏览器,首先请求事务A,事务A执行过程中,在请求事务B,观察结果。

事务A 进行插入
@RequestMapping("/test/publish/submit")public String testPublish1() {log.info("start...");transactionTemplate.execute(new TransactionCallback() {@Overridepublic String doInTransaction(TransactionStatus status)  {for (long i = 1000; i < 3000; i++) {TElement element = new TElement();element.setfElementId(i);element.setfElementName("111");element.setfElementStatus((byte) 1);mapper.insertSelective(element);}return "OK";}});log.info("end...");return "ok";}
事务B 进行更新
@RequestMapping("/test/publish/submit2")public String testPublish2() {log.info("start...");transactionTemplate.execute(new TransactionCallback() {@Overridepublic String doInTransaction(TransactionStatus status)  {// @Update({"UPDATE t_element SET f_element_status=#{status} WHERE f_element_id > #{elementId}"})mapper.update(1000L, (byte) 2);return "OK";}});log.info("end...");return "ok";}
2.1读未提交(READ UNCOMMITTED)级别

经过以上步骤测试,我们得出了如下日志

[INFO  2023-04-13 11:39:29.941] [http-nio-8099-exec-2] [df9c9572-a906-4f5c-91a2-c1fb4a87adcf] - [TransactionInsertUpdateTest.java.testPublishInsert:36] [start...][INFO  2023-04-13 11:39:31.708] [http-nio-8099-exec-3] [5651390e-f3be-4d35-81dc-cc7bb0062f40] - [TransactionInsertUpdateTest.java.testPublishUpdate:57] [start...][INFO  2023-04-13 11:39:31.760] [http-nio-8099-exec-3] [5651390e-f3be-4d35-81dc-cc7bb0062f40] - [TransactionInsertUpdateTest.java.testPublishUpdate:66] [end...][INFO  2023-04-13 11:39:42.952] [http-nio-8099-exec-2] [df9c9572-a906-4f5c-91a2-c1fb4a87adcf] - [TransactionInsertUpdateTest.java.testPublishInsert:50] [end...]

我们可以看到事务A在29秒开始执行,并且在42秒执行完成。事务B在31秒开始执行,并且没有阻塞,立刻执行完成。我们再来观察以下数据库中的数据,我们发觉status=1,也就是事务B没有更新成功因此在READ UNCOMMITTED下,事务会有问题。

2.2读已提交(READ COMMITTED)

经过以上步骤测试,我们得出了如下日志。我们可以看到在RC级别下,出现了和READ UNCOMMITTED同样的现象。具体原因如上,就是没有加锁。

[INFO  2023-04-13 11:46:59.684] [http-nio-8099-exec-3] [a11f29f2-0658-42b4-8eba-28b5f94f9037] - [TransactionInsertUpdateTest.java.testPublishInsert:36] [start...][INFO  2023-04-13 11:47:01.029] [http-nio-8099-exec-2] [a9d04f6e-4efe-4ed7-a2df-c2454d8d7946] - [TransactionInsertUpdateTest.java.testPublishUpdate:57] [start...][INFO  2023-04-13 11:47:01.090] [http-nio-8099-exec-2] [a9d04f6e-4efe-4ed7-a2df-c2454d8d7946] - [TransactionInsertUpdateTest.java.testPublishUpdate:66] [end...][INFO  2023-04-13 11:47:12.574] [http-nio-8099-exec-3] [a11f29f2-0658-42b4-8eba-28b5f94f9037] - [TransactionInsertUpdateTest.java.testPublishInsert:50] [end...]
2.3可重复读(REPEATABLE READ)

经过以上步骤测试,我们得出了如下日志。注意以下testPublishUpdate日志和以上日志的不同。

[INFO  2023-04-13 11:50:47.428] [http-nio-8099-exec-3] [83cac49b-f44e-44bd-a8ef-9d60714016f6] - [TransactionInsertUpdateTest.java.testPublishInsert:36] [start...][INFO  2023-04-13 11:50:48.851] [http-nio-8099-exec-2] [f66d62af-aa3f-4d3e-9f66-c97307d6e38e] - [TransactionInsertUpdateTest.java.testPublishUpdate:57] [start...][INFO  2023-04-13 11:50:53.872] [http-nio-8099-exec-3] [83cac49b-f44e-44bd-a8ef-9d60714016f6] - [TransactionInsertUpdateTest.java.testPublishInsert:50] [end...][INFO  2023-04-13 11:50:53.895] [http-nio-8099-exec-2] [f66d62af-aa3f-4d3e-9f66-c97307d6e38e] - [TransactionInsertUpdateTest.java.testPublishUpdate:66] [end...]

在事务A insert的时候,事务B update的时候阻塞了。这时候其实是间隙锁发挥了作用,也就是必须等事务A执行完毕之后,事务B才会获取锁,去update更新。那这时候就会更新成功了。数据库数据如下:

2.4序列化

隔离级别最严格的级别。同2.3会阻塞然后更新成功。

3.结论

在隔离级别为 读未提交(READ UNCOMMITTED)以及读已提交(READ COMMITTED)情况下,会出现事务更新失败的情况。本质上就是没有加锁导致的,而在RR级别,给事务A加上了间隙锁,事务B必须等待才能update成功。是用了锁的的方式来解决的,但可能存在效率的问题。所以锁尽量细化,比如行锁 > 间隙锁 > 记录锁 > 表锁。都是平衡效率以及安全的一种手段。

关键词:

X 关闭

安阳曹操墓将于2022年5月正式对公众开放
时间·2021-12-29    来源·中新网
为什么这次寒潮南方降雪这么明显?
时间·2021-12-29    来源·新华社
山西绛县6名失踪者全部遇难 当地:已立案调查
时间·2021-12-29    来源·中新网

X 关闭