死锁信息
## LATEST DETECTED DEADLOCK
2019-08-02 21:20:48 0x7f34ff467700
*** (1) TRANSACTION:
TRANSACTION 55455773, ACTIVE 0 sec starting index read
mysql tables in use 2, locked 2
LOCK WAIT 197 lock struct(s), heap size 24784, 140 row lock(s)
MySQL thread id 8675, OS thread handle 139865598367488, query id 58349 10.131.10.220 root Sending data
SELECT `uapuser`.`b`.`customer_id`,`uapuser`.`b`.`security_account_id`,`uapuser`.`b`.`customer_id`,`b`.`gtid` as `gtid1`,`a`.`gtid` as `gtid2` FROM (`uapuser`.`security_account` `b` JOIN `uapuser`.`edf_dormancy_acct` `a` ON ((`uapuser`.`b`.`SECURITY_ACCOUNT_ID` = `uapuser`.`a`.`ZQZH`) and (`uapuser`.`a`.`khh` = `uapuser`.`b`.`CUSTOMER_ID`))) where ((`uapuser`.`a`.`KHH` between (100000000000 + (100 * 11)) and ((100000000000 + (100 * 11)) + 99)) or (`uapuser`.`a`.`KHH` between (100002500000 + (100 * 11)) and ((100002500000 + (100 * 11)) + 99)) or (`uapuser`.`a`.`KHH` between (100005000000 + (100 * 11)) and ((100005000000 + (100 * 11)) + 99)) or (`uapuser`.`a`.`KHH` between (100007500000 + (100 * 11)) and ((100007500000 + (100 * 11)) + 99)) or (`uapuser`.`a`.`KHH` between (100010000000 + (100 * 11)) and ((100010000000 + (100 * 11)) + 99)) or (`uapuser`.`a`.`KHH` between (100012500000 + (100 * 11)) and ((1000
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 20631 page no 5 n bits 264 index PRIMARY of table `uapuser`.`edf_dormancy_acct` /* Partition `part_26` */ trx id 55455773 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
0: len 8; hex 800000174c3094a5; asc L0 ;;
1: len 10; hex 30303632353030303034; asc 0062500004;;
2: len 6; hex 00000244ff6a; asc D j;;
3: len 7; hex 5a000000201a0c; asc Z ;;
4: len 8; hex 800000174c3094a5; asc L0 ;;
5: len 12; hex 313830303632333231343033; asc 180062321403;;
6: len 4; hex 80000000; asc ;;
7: len 4; hex 80000000; asc ;;
8: len 4; hex 7fffffff; asc ;;
9: len 0; hex ; asc ;;
10: len 8; hex 0000000000000000; asc ;;
*** (2) TRANSACTION:
TRANSACTION 55455771, ACTIVE 0 sec starting index read
mysql tables in use 2, locked 2
214 lock struct(s), heap size 24784, 272 row lock(s)
MySQL thread id 8677, OS thread handle 139865597835008, query id 58333 10.131.10.220 root Sending data
SELECT `uapuser`.`b`.`customer_id`,`uapuser`.`b`.`security_account_id`,`uapuser`.`b`.`customer_id`,`b`.`gtid` as `gtid1`,`a`.`gtid` as `gtid2` FROM (`uapuser`.`security_account` `b` JOIN `uapuser`.`edf_dormancy_acct` `a` ON ((`uapuser`.`b`.`SECURITY_ACCOUNT_ID` = `uapuser`.`a`.`ZQZH`) and (`uapuser`.`a`.`khh` = `uapuser`.`b`.`CUSTOMER_ID`))) where ((`uapuser`.`a`.`KHH` between (100000000000 + (100 * 2)) and ((100000000000 + (100 * 2)) + 99)) or (`uapuser`.`a`.`KHH` between (100002500000 + (100 * 2)) and ((100002500000 + (100 * 2)) + 99)) or (`uapuser`.`a`.`KHH` between (100005000000 + (100 * 2)) and ((100005000000 + (100 * 2)) + 99)) or (`uapuser`.`a`.`KHH` between (100007500000 + (100 * 2)) and ((100007500000 + (100 * 2)) + 99)) or (`uapuser`.`a`.`KHH` between (100010000000 + (100 * 2)) and ((100010000000 + (100 * 2)) + 99)) or (`uapuser`.`a`.`KHH` between (100012500000 + (100 * 2)) and ((100012500000 +
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 20631 page no 5 n bits 264 index PRIMARY of table `uapuser`.`edf_dormancy_acct` /* Partition `part_26` */ trx id 55455771 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
0: len 8; hex 800000174c3094a5; asc L0 ;;
1: len 10; hex 30303632353030303034; asc 0062500004;;
2: len 6; hex 00000244ff6a; asc D j;;
3: len 7; hex 5a000000201a0c; asc Z ;;
4: len 8; hex 800000174c3094a5; asc L0 ;;
5: len 12; hex 313830303632333231343033; asc 180062321403;;
6: len 4; hex 80000000; asc ;;
7: len 4; hex 80000000; asc ;;
8: len 4; hex 7fffffff; asc ;;
9: len 0; hex ; asc ;;
10: len 8; hex 0000000000000000; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 20639 page no 5 n bits 264 index PRIMARY of table `uapuser`.`edf_dormancy_acct` /* Partition `part_34` */ trx id 55455771 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
0: len 8; hex 800000174d61c1a3; asc Ma ;;
1: len 10; hex 30303832353030303032; asc 0082500002;;
2: len 6; hex 00000244ff6f; asc D o;;
3: len 7; hex 5f0000001e0850; asc _ P;;
4: len 8; hex 800000174d61c1a3; asc Ma ;;
5: len 12; hex 313830303832323634323439; asc 180082264249;;
6: len 4; hex 80000000; asc ;;
7: len 4; hex 80000000; asc ;;
8: len 4; hex 7fffffff; asc ;;
9: len 0; hex ; asc ;;
10: len 8; hex 0000000000000000; asc ;;
此处事务1等待26分区的第一个记录锁,主键为(100062500004,’0062500004’),事务2持有26分区的第一个记录锁,事务2等待34分区的第一个记录锁,主键为(100082500002,’0082500002’)
锁等待日志信息
2019-08-02T21:20:48.368206 lock_wait_time:0ms req_thd_id:8675 req_trx_id:55455773 req_trx_seq:0 req_gtm_gtid:6995719040 blk_thd_id:8677 blk_trx_id:55455771 blk_trx_seq:0 blk_gtm_gtid:6995719032
2019-08-02T21:20:48.368206 lock_wait_time:0ms req_thd_id:8675 req_trx_id:55455773 req_trx_seq:0 req_gtm_gtid:6995719040 blk_thd_id:8685 blk_trx_id:55455772 blk_trx_seq:0 blk_gtm_gtid:6995719038
2019-08-02T21:20:48.368206 lock_wait_time:0ms req_thd_id:8675 req_trx_id:55455773 req_trx_seq:0 req_gtm_gtid:6995719040 blk_thd_id:8679 blk_trx_id:55455770 blk_trx_seq:0 blk_gtm_gtid:6995719034
2019-08-02T21:20:48.368452 lock_wait_time:0ms req_thd_id:8677 req_trx_id:55455771 req_trx_seq:0 req_gtm_gtid:6995719032 blk_thd_id:8675 blk_trx_id:55455773 blk_trx_seq:0 blk_gtm_gtid:6995719040
分析:根据锁等待日志可以发现,事务1(55455773)在等待事务2(55455771)的锁,而事务2也在同时等待事务1的锁,因此,事务1和事务2形成了死锁。
General_log日志信息
2019-08-02T21:20:47.988314+08:00 8675 Query /*+GTID=6995719040*/start transaction;
2019-08-02T21:20:47.990261+08:00 8675 Query SELECT `uapuser`.`b`.`customer_id`,`uapuser`.`b`.`security_account_id`,`uapuser`.`b`.`customer_id`,`b`.`gtid` as `gtid1`,`a`.`gtid` as `gtid2` FROM (`uapuser`.`security_account` `b` JOIN `uapuser`.`edf_dormancy_acct` `a` ON ((`uapuser`.`b`.`SECURITY_ACCOUNT_ID` = `uapuser`.`a`.`ZQZH`) and (`uapuser`.`a`.`khh` = `uapuser`.`b`.`CUSTOMER_ID`))) where ((`uapuser`.`a`.`KHH` between (100000000000 + (100 * 11)) and ((100000000000 + (100 * 11)) + 99)) or ... and (`uapuser`.`a`.`MSG_CODE` = -(1)))) FOR UPDATE
2019-08-02T21:21:04.308754+08:00 8675 Query rollback
2019-08-02T21:20:47.984183+08:00 8677 Query /*+GTID=6995719032*/start transaction;
2019-08-02T21:20:47.986308+08:00 8677 Query SELECT `uapuser`.`b`.`customer_id`,`uapuser`.`b`.`security_account_id`,`uapuser`.`b`.`customer_id`,`b`.`gtid` as `gtid1`,`a`.`gtid` as `gtid2` FROM (`uapuser`.`security_account` `b` JOIN `uapuser`.`edf_dormancy_acct` `a` ON ((`uapuser`.`b`.`SECURITY_ACCOUNT_ID` = `uapuser`.`a`.`ZQZH`) and (`uapuser`.`a`.`khh` = `uapuser`.`b`.`CUSTOMER_ID`))) where ((`uapuser`.`a`.`KHH` between (100000000000 + (100 * 2)) and ((100000000000 + (100 * 2)) + 99)) or ... and (`uapuser`.`a`.`MSG_CODE` = -(1)))) FOR UPDATE
2019-08-02T21:21:04.308628+08:00 8677 Query rollback
根据事务的线程号从general_log中提取2个事务的日志,2个事务本身均只做了一个select … for update语句,而且根据where条件的规律可以判断,死锁信息中涉及的2个行记录,均不满足这2个事务的where条件。
问题现象推论
-
类似select … for update语句应该会扫描所有分区的第一行记录,并且可能需要持有锁;
-
模型简化 由于问题场景是100多个分区表的2个表join过程,此处将问题简化为6个分区的单表查询过程。 建表语句:Drop database if exists abczyy_part;
Create database abczyy_part;
Use abczyy_part;
CREATE TABLE `edf_dormancy_acct` (
`SERIAL_NO` bigint(20) NOT NULL DEFAULT '0',
`KHH` bigint(20) NOT NULL DEFAULT '0',
`ZQZH` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT '',
`MSG_CODE` int(11) NOT NULL DEFAULT '0',
`GTID` bigint(20) DEFAULT NULL,
PRIMARY KEY (`KHH`,`ZQZH`),
KEY `EDF_DORMANCY_ACCT_IDX1` (`KHH`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
/*!50100 PARTITION BY RANGE (KHH)
(PARTITION part_0 VALUES LESS THAN (100000000000) ENGINE = InnoDB,
PARTITION part_1 VALUES LESS THAN (100002500000) ENGINE = InnoDB,
PARTITION part_2 VALUES LESS THAN (100005000000) ENGINE = InnoDB,
PARTITION part_3 VALUES LESS THAN (100007500000) ENGINE = InnoDB,
PARTITION part_4 VALUES LESS THAN (100010000000) ENGINE = InnoDB,
PARTITION part_5 VALUES LESS THAN (100012500000) ENGINE = InnoDB)*/;
-- 预置15条数据,1-5分区各3条数据:
insert into edf_dormancy_acct values(100000000123,100000000123,'00000123',-1,1);
insert into edf_dormancy_acct values(100002500123,100002500123,'02500123',10,2);
insert into edf_dormancy_acct values(100005000123,100005000123,'05000123',-1,3);
insert into edf_dormancy_acct values(100007500123,100007500123,'07500123',10,4);
insert into edf_dormancy_acct values(100010000234,100010000234,'10000234',-1,5);
insert into edf_dormancy_acct values(100000000456,100000000456,'00000456',-1,6);
insert into edf_dormancy_acct values(100002500456,100002500456,'02500456',10,7);
insert into edf_dormancy_acct values(100005000456,100005000456,'05000456',-1,8);
insert into edf_dormancy_acct values(100007500456,100007500456,'07500456',10,9);
insert into edf_dormancy_acct values(100010000567,100010000567,'10000567',-1,10);
insert into edf_dormancy_acct values(100000000457,100000000457,'00000457',-1,11);
insert into edf_dormancy_acct values(100002500457,100002500457,'02500457',10,12);
insert into edf_dormancy_acct values(100005000457,100005000457,'05000457',-1,13);
insert into edf_dormancy_acct values(100007500457,100007500457,'07500457',10,14);
insert into edf_dormancy_acct values(100010000568,100010000568,'10000568',-1,15);
问题复现
为了便于调试,设置innodb行锁等待时间为1年
set global innodb_lock_wait_timeout=31536000;
单条语句执行后仅只有where条件中的行锁
-- 验证:
Session 1;
Use abczyy_part;
start transaction;
SELECT khh,gtid FROM edf_dormancy_acct where ((KHH between (100000000000 + (100 * 4)) and ((100000000000 + (100 * 4)) + 99)) or (KHH between (100010000000 + (100 * 4)) and ((100010000000 + (100 * 4)) + 99)) and (MSG_CODE = -(1)) ) FOR UPDATE;
-- 以下语句全部执行成功,没有锁等待
Session 2:
Use abczyy_part;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100000000123 for update;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100002500123 for update;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100005000123 for update;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100007500123 for update;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100010000234 for update;
查看innodb_trx发现,session 1的事务持有了2把行锁。
mysql> select trx_mysql_thread_id,trx_rows_locked from information_schema.innodb_trx\G
*************************** 1. row ***************************
trx_mysql_thread_id: 15
trx_rows_locked: 2
1 row in set (0.00 sec)
清理:断开所有链路,释放所有事务。
分区第一行记录发生锁冲突时持有该行锁
-- 验证:
-- Session 1:
Use abczyy_part;
start transaction;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100010000234 for update;
-- Session 2;
Use abczyy_part;
start transaction;
SELECT khh,gtid FROM edf_dormancy_acct where ((KHH between (100000000000 + (100 * 4)) and ((100000000000 + (100 * 4)) + 99)) or (KHH between (100010000000 + (100 * 4)) and ((100010000000 + (100 * 4)) + 99)) and (MSG_CODE = -(1)) ) FOR UPDATE;
此时,session 2的事务挂住等待session 1的分区5第一行记录行锁。
-- Session 1:
Commit;
-- 此时,session 2的sql执行成功。
以下语句全部执行成功,没有锁等待
-- Session 3:
Use abczyy_part;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100000000123 for update;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100002500123 for update;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100005000123 for update;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100007500123 for update;
以下语句挂住等待事务2的分区5第一行记录的行锁。
Session 3:
Use abczyy_part;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100010000234 for update;
查看innodb_trx发现,session 1的事务持有了3把行锁,多出来的行锁正是曾经被阻塞的分区5的第一行记录。
mysql> select trx_mysql_thread_id,trx_rows_locked from information_schema.innodb_trx\G
*************************** 1. row ***************************
trx_mysql_thread_id: 16
trx_rows_locked: 3
1 row in set (0.00 sec)
清理:断开所有链路,释放所有事务。
死锁复现
-- 验证:
session 3:
Use abczyy_part;
start transaction;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100007500123 for update;
Session 2;
Use abczyy_part;
start transaction;
SELECT khh, gtid FROM edf_dormancy_acct where ((KHH between (100000000000 + (100 * 5)) and ((100000000000 + (100 * 5)) + 99)) or (KHH between (100002500000 + (100 * 5)) and ((100002500000 + (100 * 5)) + 99)) or (KHH between (100005000000 + (100 * 5)) and ((100005000000 + (100 * 5)) + 99)) or (KHH between (100007500000 + (100 * 5)) and ((100007500000 + (100 * 5)) + 99)) or (KHH between (100010000000 + (100 * 5)) and ((100010000000 + (100 * 5)) + 99)) and (MSG_CODE = -(1))) FOR UPDATE;
-- 此时,session 2的sql挂住等待
session 4:
Use abczyy_part;
start transaction;
SELECT khh,gtid FROM edf_dormancy_acct where KHH = 100005000123 for update;
Session 1;
Use abczyy_part;
start transaction;
SELECT khh, gtid FROM edf_dormancy_acct where ((KHH between (100000000000 + (100 * 4)) and ((100000000000 + (100 * 4)) + 99)) or (KHH between (100002500000 + (100 * 4)) and ((100002500000 + (100 * 4)) + 99)) or (KHH between (100005000000 + (100 * 4)) and ((100005000000 + (100 * 4)) + 99)) or (KHH between (100007500000 + (100 * 4)) and ((100007500000 + (100 * 4)) + 99)) or (KHH between (100010000000 + (100 * 4)) and ((100010000000 + (100 * 4)) + 99)) and (MSG_CODE = -(1))) FOR UPDATE;
-- 此时,session 1的sql挂住等待
session 4:
commit;
session 3:
commit;
-- 结果:
-- 1)session 1事务触发死锁报错:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
死锁信息
## LATEST DETECTED DEADLOCK
2019-08-06 17:01:39 0x7f855ed76700
*** (1) TRANSACTION:
TRANSACTION 51732580, ACTIVE 13 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 8 lock struct(s), heap size 1160, 4 row lock(s)
MySQL thread id 15, OS thread handle 140210797487872, query id 492 localhost root Sending data
SELECT khh, gtid FROM edf_dormancy_acct where ((KHH between (100000000000 + (100 * 4)) and ((100000000000 + (100 * 4)) + 99)) or (KHH between (100002500000 + (100 * 4)) and ((100002500000 + (100 * 4)) + 99)) or (KHH between (100005000000 + (100 * 4)) and ((100005000000 + (100 * 4)) + 99)) or (KHH between (100007500000 + (100 * 4)) and ((100007500000 + (100 * 4)) + 99)) or (KHH between (100010000000 + (100 * 4)) and ((100010000000 + (100 * 4)) + 99)) and (MSG_CODE = -(1))) FOR UPDATE
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1796 page no 3 n bits 72 index PRIMARY of table `abczyy_part`.`edf_dormancy_acct` /* Partition `part_4` */ trx id 51732580 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
0: len 8; hex 8000001748e9595b; asc H Y[;;
1: len 8; hex 3037353030313233; asc 07500123;;
2: len 6; hex 000003156030; asc `0;;
3: len 7; hex f4000000230110; asc # ;;
4: len 8; hex 8000001748e9595b; asc H Y[;;
5: len 4; hex 8000000a; asc ;;
6: len 8; hex 8000000000000004; asc ;;
*** (2) TRANSACTION:
TRANSACTION 51732578, ACTIVE 31 sec starting index read
mysql tables in use 1, locked 1
10 lock struct(s), heap size 1160, 2 row lock(s)
MySQL thread id 16, OS thread handle 140210798552832, query id 484 localhost root Sending data
SELECT khh, gtid FROM edf_dormancy_acct where ((KHH between (100000000000 + (100 * 5)) and ((100000000000 + (100 * 5)) + 99)) or (KHH between (100002500000 + (100 * 5)) and ((100002500000 + (100 * 5)) + 99)) or (KHH between (100005000000 + (100 * 5)) and ((100005000000 + (100 * 5)) + 99)) or (KHH between (100007500000 + (100 * 5)) and ((100007500000 + (100 * 5)) + 99)) or (KHH between (100010000000 + (100 * 5)) and ((100010000000 + (100 * 5)) + 99)) and (MSG_CODE = -(1))) FOR UPDATE
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1796 page no 3 n bits 72 index PRIMARY of table `abczyy_part`.`edf_dormancy_acct` /* Partition `part_4` */ trx id 51732578 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
0: len 8; hex 8000001748e9595b; asc H Y[;;
1: len 8; hex 3037353030313233; asc 07500123;;
2: len 6; hex 000003156030; asc `0;;
3: len 7; hex f4000000230110; asc # ;;
4: len 8; hex 8000001748e9595b; asc H Y[;;
5: len 4; hex 8000000a; asc ;;
6: len 8; hex 8000000000000004; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1795 page no 3 n bits 72 index PRIMARY of table `abczyy_part`.`edf_dormancy_acct` /* Partition `part_3` */ trx id 51732578 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 7; compact format; info bits 0
0: len 8; hex 8000001748c333bb; asc H 3 ;;
1: len 8; hex 3035303030313233; asc 05000123;;
2: len 6; hex 00000315602f; asc `/;;
3: len 7; hex f30000010b0110; asc ;;
4: len 8; hex 8000001748c333bb; asc H 3 ;;
5: len 4; hex 7fffffff; asc ;;
6: len 8; hex 8000000000000003; asc ;;
*** WE ROLL BACK TRANSACTION (1)
总结
- select … for update分区表范围查询时需要扫描条件内涉及到所有的分区的第一行记录。假设涉及到N个分区,那么扫描次数为N*N次。
- 对于分区的第一行记录,如果不在查询条件范围内,则会在扫描到该记录时先持有锁,再释放锁,重复N次
- 对于分区的第一行记录,如果不在查询条件范围内,且在扫描时发生了行锁冲突,则将会持有该记录行X锁,直至事务结束。
问题原因
-
本质原因“分区表在范围查询场景下,扫描分区第一行记录时,在锁冲突场景下会持有该锁直至事务结束。
-
代码级简单分析
- 主要涉及三个函数: ha_innopart::read_range_first_in_part row_unlock_for_mysql() row_search_mvcc() 主要逻辑如下: ha_innopart::read_range_first_in_part 1)调用ha_innobase::index_read()函数读取分区的第一条记录; 2)如果没有满足>begin条件的记录,返回HA_ERR_END_OF_FILE; 3)如果满足>begin条件记录,但是在判断不满足<end条件,返回HA_ERR_END_OF_FILE,次过程需要调用ha_innobase::unlock_row()释放该记录的行锁; 4)如果满足>begin ,<end条件,返回成功。 正常情况,读取每一个分区第一个记录后,很快就在第三步中释放该行锁了。在释放行锁的函数row_unlock_for_mysql()中,只有当prebuilt->new_rec_locks >= 1才会释放相应的行锁。 但是对于特殊的场景:调用index_read()读取分区第一条记录时,如果产生锁等待,当锁等待结束后,获取到该行锁后,在row_search_mvcc()函数中将会重置prebuilt->new_rec_locks=0,在第三步的判断中,prebuilt->new_rec_locks=0,因此不会释放不属于自身where条件的行锁。
-
问题解决:从业务角度,针对该问题,可以将多个or条件的一个语句拆分为多个语句,这些语句在一个事务内,这样将不会出现死锁问题。