記錄一次比較詭異的mysql死鎖日志。系統(tǒng)運行幾個月來笤昨,就在前幾天發(fā)生了一次死鎖,而且就只發(fā)生了一次死鎖握恳,整個排查過程耗時將近一天咐吼,最后感謝我們的DBA大神和老大一起分析找到原因炭臭。
診斷死鎖
借助于我們dapeng開源的分布式日志查詢系統(tǒng)(EFF)及日志告警系統(tǒng),我們得益于及時發(fā)現了業(yè)務中的死鎖日志。(想了解dapeng分布式日志查詢系統(tǒng)請點擊)
08-02 08:46:24 787 dapeng-eventbus--scheduler-0 ERROR [] -
eventbus: 定時輪詢線程內出現了異常格嘁,已捕獲 msg:Deadlock found when trying to get lock; try restarting transaction
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_161]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_161]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_161]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_161]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:400) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.Util.getInstance(Util.java:383) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:987) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3847) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3783) ~[mysql-connector-java-5.1.36.jar:5.1.36]
// 省略部分...
at com.today.eventbus.scheduler.ScalaSql$.executeUpdateWithGenerateKey(ScalaSql.scala:104) ~[event-bus_2.12-2.0.4.jar:2.0.4]
at com.today.eventbus.scheduler.ScalaSql$.executeUpdate(ScalaSql.scala:57) ~[event-bus_2.12-2.0.4.jar:2.0.4]
at com.today.eventbus.scheduler.MsgPublishTask.$anonfun$doPublishMessagesAsync$1(MsgPublishTask.scala:154) ~[event-bus_2.12-2.0.4.jar:2.0.4]
at com.today.eventbus.scheduler.MsgPublishTask.$anonfun$doPublishMessagesAsync$1$adapted(MsgPublishTask.scala:144) ~[event-bus_2.12-2.0.4.jar:2.0.4]
at com.today.eventbus.scheduler.ScalaSql$.withTransaction(ScalaSql.scala:26) ~[event-bus_2.12-2.0.4.jar:2.0.4]
at com.today.eventbus.scheduler.MsgPublishTask.doPublishMessagesAsync(MsgPublishTask.scala:144) ~[event-bus_2.12-2.0.4.jar:2.0.4]
at com.today.eventbus.scheduler.MsgPublishTask.$anonfun$startScheduled$2(MsgPublishTask.scala:95) ~[event-bus_2.12-2.0.4.jar:2.0.4]
//省略部分...
當我們找到業(yè)務中的ERROR日志后,發(fā)現只有如下簡單的一句發(fā)現死鎖的日志吼野,無法進行死鎖定定位聘鳞,我們需要診斷分析當時出現死鎖的上下文信息以及相關的執(zhí)行語句。這時候就需要查看mysql
死鎖日志了妻怎。
首先壳炎,需要打開mysql死鎖日志開關,我們線上是已經打開的逼侦。打開方式
set global innodb_print_all_deadlocks=on
然后我們去mysql錯誤日志中撈這次的死鎖日志
2018-08-02T08:46:24.758848+08:00 871635 [Note] InnoDB:
*** (1) TRANSACTION:
TRANSACTION 206663716, ACTIVE 0 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 10 row lock(s), undo log entries 5
MySQL thread id 872226, OS thread handle 140243727415040, query id 461709452 192.168.10.126 today_user updating
DELETE FROM dp_common_event WHERE id in (268241,268242,268243,268810,268811)
2018-08-02T08:46:24.758879+08:00 871635 [Note] InnoDB: *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 812 page no 3 n bits 80 index PRIMARY of table `order_db`.`dp_common_event` trx id 206663716 lock_mode X waiting
Record lock, heap no 10 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 8; hex 8000000000041a0c; asc ;;
1: len 6; hex 00000c517014; asc Qp ;;
2: len 7; hex b5000006a20110; asc ;;
3: len 30; hex 636f6d2e746f6461792e6170692e6f726465722e7363616c612e6576656e; asc com.today.api.order.scala.even; (total 52 bytes);
4: len 30; hex 636f6d2e746f6461792e6170692e6f726465722e7363616c612e6576656e; asc com.today.api.order.scala.even; (total 371 bytes);
5: len 4; hex 5b625460; asc [bT`;;
2018-08-02T08:46:24.759242+08:00 871635 [Note] InnoDB: *** (2) TRANSACTION:
TRANSACTION 206663700, ACTIVE 0 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
10 lock struct(s), heap size 1136, 6 row lock(s), undo log entries 6
MySQL thread id 871635, OS thread handle 140243167844096, query id 461709463 192.168.10.126 today_user update
INSERT INTO dp_common_event set id=268244, event_type='com.today.api.order.scala.events.StoreOrderEndEventNew', event_binary=x'636F6D2E746F6461792E6170692E6F726465722E7363616C612E6576656E74732E53746F72654F72646572456E644576656E744E65770016A8DF2015181C16F8E9111814313136303035303135333331373037353336373615061504180731322E35303030180731322E353030304806302E30303030F806302E30303030080E01311808313136303035303016E699A80436D0EBCC819F59280018001680DCCC819F59150216D0F7C8819F5915021680DCCC819F5915DEB1931E00192C16EEFC1E18143131363030353031353333313730373533363736180832303531383430301502180CE7899BE88289E58DA4E7B289160018003806302E303030301502180731302E30303030180015002806302E3030303016000016F0FC1E181431313630303530313533333137303735333637361808323034373138303415041812E9A699E88F87E6B2B9E88F9CE58C85383067160018003806302E3030303015021806322E35303030180015002806302E30303030160000190800'
2018-08-02T08:46:24.759270+08:00 871635 [Note] InnoDB: *** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 812 page no 3 n bits 80 index PRIMARY of table `order_db`.`dp_common_event` trx id 206663700 lock_mode X locks rec but not gap
Record lock, heap no 10 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 8; hex 8000000000041a0c; asc ;;
1: len 6; hex 00000c517014; asc Qp ;;
2: len 7; hex b5000006a20110; asc ;;
3: len 30; hex 636f6d2e746f6461792e6170692e6f726465722e7363616c612e6576656e; asc com.today.api.order.scala.even; (total 52 bytes);
4: len 30; hex 636f6d2e746f6461792e6170692e6f726465722e7363616c612e6576656e; asc com.today.api.order.scala.even; (total 371 bytes);
5: len 4; hex 5b625460; asc [bT`;;
2018-08-02T08:46:24.759691+08:00 871635 [Note] InnoDB: *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 812 page no 3 n bits 80 index PRIMARY of table `order_db`.`dp_common_event` trx id 206663700 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 6; compact format; info bits 32
0: len 8; hex 8000000000041a0a; asc ;;
1: len 6; hex 00000c517024; asc Qp$;;
2: len 7; hex 40000001ac0a84; asc @ ;;
3: len 30; hex 636f6d2e746f6461792e6170692e6f726465722e7363616c612e6576656e; asc com.today.api.order.scala.even; (total 54 bytes);
4: len 30; hex 636f6d2e746f6461792e6170692e6f726465722e7363616c612e6576656e; asc com.today.api.order.scala.even; (total 476 bytes);
5: len 4; hex 5b625460; asc [bT`;;
2018-08-02T08:46:24.760094+08:00 871635 [Note] InnoDB: *** WE ROLL BACK TRANSACTION (1)
解讀死鎖日志
死鎖日志里面記錄了兩個事務匿辩,事務1
,事務2
榛丢,其中事務1
最終被回滾了
- 事務1
持有的鎖
事務1鎖住了5個對象铲球,持有10把鎖。執(zhí)行的sql語句為
DELETE FROM dp_common_event WHERE id in (268241,268242,268243,268810,268811)
等待的鎖
等待order_db.dp_common_event
的主鍵的排他鎖(X鎖)
- 事務2
持有的鎖
事務2鎖住了10個對象晰赞,持有6把鎖稼病,sql語句如下
INSERT INTO dp_common_event set id=268244, event_type='com.today.api.order.scala.events.StoreOrderEndEventNew', event_binary=x'二進制數據(略)'
等待的鎖
lock_mode X locks gap before rec insert intention waiting
意思為事務2正在向表dp_common_event
插入一行數據,在獲取意向插入鎖之前需要先獲取到當前表的一個排他鎖(LOCK_X
)选侨。
查看發(fā)生死鎖時間上下文業(yè)務日志
//省略
08-02 08:46:24 684 dapeng-container-biz-pool-79 DEBUG [ac1e0002a068ab95] - SQL Preparing: INSERT INTO dp_common_event set id=?, event_type=?, event_binary=? args: WrappedArray(JdbcValue(268812), JdbcValue(com.today.api.order.scala.events.CreateOrderEventNew), JdbcValue([B@255b9459))
08-02 08:46:24 688 dapeng-container-biz-pool-79 DEBUG [ac1e0002a068ab95] - SQL result: 1
// 省略中間業(yè)務代碼
08-02 08:46:24 758 dapeng-container-biz-pool-79 DEBUG [ac1e0002a068ab95] - SQL Preparing: INSERT INTO dp_common_event set id=?, event_type=?, event_binary=? args: WrappedArray(JdbcValue(268244), JdbcValue(com.today.api.order.scala.events.StoreOrderEndEventNew), JdbcValue([B@3f796e10))
08-02 08:46:24 762 dapeng-container-biz-pool-79 DEBUG [ac1e0002a068ab95] - SQL result: 1
在一個事務中(根據死鎖日志,此事務為事務2)然走,我們先后向dp_common_event
表插入了兩次數據援制,然后就在這時候發(fā)生了死鎖。
//第一條數據
INSERT INTO dp_common_event set id= 268812, event_type='com.today.api.order.scala.events.CreateOrderEventNew', event_binary= [B@255b9459);
//第二條數據
INSERT INTO dp_common_event set id= 268244, event_type='com.today.api.order.scala.events.StoreOrderEndEventNew', event_binary=[B@3f796e10)
與此同時芍瑞,事務1(發(fā)生死鎖后被回滾的一方)的操作如下:
DELETE FROM dp_common_event WHERE id in (268241,268242,268243,268810,268811)
查看表結構
CREATE TABLE `dp_common_event` (
`id` bigint(20) NOT NULL COMMENT '事件id晨仑,全局唯一, 可用于冪等操作',
`event_type` varchar(255) DEFAULT NULL COMMENT '事件類型',
`event_binary` blob COMMENT '事件內容',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
PRIMARY KEY (`id`),
KEY `idx_dp_common_event` (`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
可以看到,該表id
字段為主鍵拆檬,update_at
是普通索引洪己。
分析
時間線分析
通過比對,我們整理出事務1和事務2執(zhí)行的先后順序竟贯,如下圖:
t1 時刻
事務2向dp_common_event
表插入一條數據答捕,插入的數據id為268812
,插入完成后屑那,此時事務2會持有鎖,鎖住的是一行記錄拱镐。lock_mode X locks rec but not gap
代表鎖住的是一個索引,不是一個范圍齐莲。
t2 時刻
事務1開啟事務,并刪除 dp_common_event
一部分數據痢站,使用的條件是in
的形式,刪除的數據為(268241,268242,268243,268810,268811)
选酗。
我們在本地模擬插入上述5條數據阵难,并使用desc分析這條刪除語句。
desc delete from dp_common_event where id in (268241,268242,268243,268810,268811);
結果如下圖:
key
欄為空,說明mysql
并沒有走主鍵索引芒填,這是為什么呢呜叫?
原來,當mysql走索引查詢的數據占全表30%以上時殿衰,mysql就不會選擇走索引朱庆。由于dp_common_event
表的數據量很少,使用 in 的模式并沒有走索引闷祥。因此這里沒有加行鎖娱颊。這里加的鎖就是Gap鎖。需要鎖住的是一個范圍凯砍。
本例中,gap鎖從左至右依次鎖住 268241 到 268811之間的每一條記錄,以及268811以上的所有的記錄箱硕。由于事務2中鎖住的記錄id
為268812
,該條記錄的鎖被事務2悟衩。因此gap鎖當鎖到268812
這條記錄時剧罩,會進行等待。等待巨鹿如下座泳。lock_mode X locks rec but not gap
代表的意思是等待的是一條記錄而不是一個范圍惠昔。
t3 時刻
事務2繼續(xù)向下執(zhí)行幕与,插入第二條數據,id為268244
镇防,由于268244
這條記錄已經在事務1中被`gap鎖鎖啦鸣。因此,這時候事務2反過來要去等待事務1的gap鎖,而上一步事務1再等待事務2的記錄鎖来氧。這就造成了循環(huán)等待赏陵,出現死鎖。于是事務1回滾饲漾,事務2提交了。
t3時刻mysql錯誤日志日下
RECORD LOCKS space id 812 page no 3 n bits 80 index PRIMARY of table `order_db`.`dp_common_event` trx id 206663716 lock_mode X waiting
Record lock, heap no 10 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
如何避免死鎖
首先,上述例子中缕溉,如果事務2的兩條插入語句的id是順序遞增的,那么再第二條語句插入時考传,它的id值不會被事務1的gap鎖鎖住,這樣也不會造成死鎖证鸥。
其次,如果事務2中每次往dp_common_event
中只插入一條數據僚楞,也不會造成死鎖。
第三枉层,如果事務1中使用的時精確單條刪泉褐,以多條語句單獨刪的形式,那么就不會鎖住范圍鸟蜡,而是精確的鎖住每一行膜赃,這樣也不回出現死鎖。
總結揉忘,由于每次插入的id是通過一個分布式取號服務獲取的跳座,該取號服務會有兩個節(jié)點,兩個節(jié)點的id會有一個區(qū)間的差距泣矛,比如a節(jié)點目前取號的起始值為268241疲眷,那么b節(jié)點的起始值為268810。因此您朽,每次插入時不能嚴格保證id的絕對遞增狂丝。
因此,我們決定在刪除操作時哗总,精確到一行一行進行刪除几颜。
案例2
由于某次需要刪除線上的臟數據時,與生產的業(yè)務相沖出現了死鎖,如下日志顯示魂奥。
08-05 18:55:56 582 dapeng-container-biz-pool-1 ERROR [ac190004bcca51e5] -
Deadlock found when trying to get lock; try restarting transaction
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:
Deadlock found when trying to get lock; try restarting transaction
// 省略部分
at com.mysql.jdbc.Util.handleNewInstance(Util.java:400) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.Util.getInstance(Util.java:383) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:987) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3847) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3783) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2447) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2594) ~[mysql-connector-java-5.1.36.jar:5.1.36]
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2545) ~[mysql-connector-java-5.1.36.jar:5.1.36]
//省略部分
at wangzx.scala_commons.sql.RichDataSource.$anonfun$generateKey$1(RichDataSource.scala:30) ~[scala-sql_2.12-2.0.6.jar:2.0.6]
at wangzx.scala_commons.sql.RichDataSource.withConnection(RichDataSource.scala:12) ~[scala-sql_2.12-2.0.6.jar:2.0.6]
at wangzx.scala_commons.sql.RichDataSource.generateKey(RichDataSource.scala:30) ~[scala-sql_2.12-2.0.6.jar:2.0.6]
at com.today.service.order_new.sql.OrderSql$.createOrderDetail(OrderSql.scala:80) ~[order_service_2.12-0.2.0-SNAPSHOT.jar:0.2.0-SNAPSHOT]
at com.today.service.order_new.action.CreateOrderAction.$anonfun$action$1(CreateOrderAction.scala:53) [order_service_2.12-0.2.0-SNAPSHOT.jar:0.2.0-SNAPSHOT]
at com.today.service.order_new.action.CreateOrderAction.$anonfun$action$1$adapted(CreateOrderAction.scala:53) [order_service_2.12-0.2.0-SNAPSHOT.jar:0.2.0-SNAPSHOT]
at scala.collection.immutable.List.foreach(List.scala:389) ~[scala-library.jar:na]
at com.today.service.order_new.action.CreateOrderAction.action(CreateOrderAction.scala:53) [order_service_2.12-0.2.0-SNAPSHOT.jar:0.2.0-SNAPSHOT]
at com.today.service.order_new.action.CreateOrderAction.action(CreateOrderAction.scala:24) [order_service_2.12-0.2.0-SNAPSHOT.jar:0.2.0-SNAPSHOT]
at com.today.service.commons.Action.execute(Action.scala:15) [service-commons_2.12-1.5-SNAPSHOT.jar:1.5-SNAPSHOT]
at com.today.service.commons.Action.execute$(Action.scala:10) [service-commons_2.12-1.5-SNAPSHOT.jar:1.5-SNAPSHOT]
at com.today.service.order_new.action.CreateOrderAction.execute(CreateOrderAction.scala:24) [order_service_2.12-0.2.0-SNAPSHOT.jar:0.2.0-SNAPSHOT]
at com.today.service.order_new.OrderService2Impl.createOrder(OrderService2Impl.scala:45) [order_service_2.12-0.2.0-SNAPSHOT.jar:0.2.0-SNAPSHOT]
死鎖日志
*** (1) TRANSACTION:
TRANSACTION 216689660, ACTIVE 27 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 2
MySQL thread id 995341, OS thread handle 140243168642816, query id 539333372 192.168.10.131 today_user update
INSERT INTO `order_detail` SET
id = 379373,
order_no = '11600202533466529666',
detail_seq = 1,
sku_no='20507572',
sku_name='海氏海諾創(chuàng)可貼',
sku_version=0,
barcode='',
coupon_id=0,
sku_count=1,
sku_selling_price=7.0,
discount_amount = 0.0,
remark = '',
created_at = now(),
created_by = 1,
updated_at = now(),
updated_by=1
,promotion_id = 0
2018-08-05T18:55:56.524630+08:00 995779 [Note] InnoDB: *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1045 page no 6219 n bits 440 index idx_order_detail of table `order_db`.`order_detail` trx id 216689660 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
0: len 20; hex 3131363030353031353333303531333137333333; asc 11600501533051317333;;
1: len 8; hex 8000000000032b87; asc + ;;
2018-08-05T18:55:56.524779+08:00 995779 [Note] InnoDB: *** (2) TRANSACTION:
TRANSACTION 216688923, ACTIVE 49 sec fetching rows, thread declared inside InnoDB 1251
mysql tables in use 2, locked 2
1653 lock struct(s), heap size 172240, 160498 row lock(s), undo log entries 43
MySQL thread id 995779, OS thread handle 140243732473600, query id 539339615 localhost root Sending data
delete from orders where order_no in (
select a.order_no from (
select order_no from orders where finish_time between '2018-07-01 00:00:00' and '2018-07-31 00:00:00' and store_id in ('11866600','11888700','10008000','11866601','10009000','11703083','10003502','11608093','10004102','10010100',
'11699800','11888900') ) as a
)
2018-08-05T18:55:56.524820+08:00 995779 [Note] InnoDB: *** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1045 page no 6219 n bits 440 index idx_order_detail of table `order_db`.`order_detail` trx id 216688923 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
0: len 20; hex 3131363030353031353333303531333137333333; asc 11600501533051317333;;
1: len 8; hex 8000000000032b87; asc + ;;
2018-08-05T18:55:56.524993+08:00 995779 [Note] InnoDB: *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 817 page no 2397 n bits 184 index PRIMARY of table `order_db`.`orders` trx id 216688923 lock mode S waiting
Record lock, heap no 106 PHYSICAL RECORD: n_fields 26; compact format; info bits 0
0: len 8; hex 8000000000032336; asc #6;;
1: len 6; hex 00000cea6bfc; asc k ;;
2: len 7; hex aa000001430110; asc C ;;
3: len 20; hex 3131363030323032353333343636353239363636; asc 11600202533466529666;;
4: len 2; hex 8001; asc ;;
5: len 4; hex 80000001; asc ;;
6: len 5; hex 8000070000; asc ;;
7: len 5; hex 8000070000; asc ;;
8: len 8; hex 8000000000000002; asc ;;
9: len 4; hex 80b1014a; asc J;;
10: len 8; hex 8000000000000000; asc ;;
11: len 5; hex 8000000000; asc ;;
12: len 5; hex 8000000000; asc ;;
13: len 5; hex 8000000000; asc ;;
14: len 2; hex 8000; asc ;;
15: SQL NULL;
16: SQL NULL;
17: len 5; hex 99a08b2dde; asc - ;;
18: len 0; hex ; asc ;;
19: len 0; hex ; asc ;;
20: SQL NULL;
21: len 4; hex 81e24944; asc ID;;
22: len 5; hex 99a08b2ddd; asc - ;;
23: len 4; hex 80000001; asc ;;
24: len 4; hex 5b66d7a1; asc [f ;;
25: len 4; hex 80000001; asc ;;
2018-08-05T18:55:56.525617+08:00 995779 [Note] InnoDB: *** WE ROLL BACK TRANSACTION (1)
分析死鎖日志
主要是事務1 insert
操作時在插入order_detail
表時菠剩,需要獲取插入意向鎖之前,需要獲取一個gap x鎖耻煤。
事務2在刪除數據時具壮,需要獲取一個 S鎖准颓,共享鎖。orders表的棺妓。