### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction### The error may involve com.cgd.order.dao.OrderSaleMapper.updateSaleOrderStatus-Inline### The error occurred while setting parameters### SQL: update t_order set ORDER_STATUS = ? where ORDER_ID = ?
小盆友,如果你在日志里看到这个是不是像我一样会有很多问号??
我一个只会写增删改查sql的低层次程序员有了满奶子问号。
然而我置信啊:只有功夫深,李白碰到的老婆婆就能把铁杵磨成针。
1.首先是要理解一些除了增删改查之外的数据库基础知识
从极客上找了门MySQL实战,如果你也想买,请分割我,推荐人买有返现的。
这门课我是感觉很值,这两天为了解决这个死锁又读了一遍无关加锁的章节,发现了一条命令啊,这个命令会输入很多信息,有一节 LATESTDETECTED DEADLOCK,就是记录的最初一次死锁信息。
show engine innodb status;
我就试着执行了下,好巧不巧跟那天死锁日志里的sql一样,不过也阐明这死锁问题挺频繁的
------------------------LATEST DETECTED DEADLOCK------------------------2020-12-17 11:20:09 7fbe339f7700//第一个事务*** (1) TRANSACTION:TRANSACTION 116609954, ACTIVE 1.214 sec starting index readmysql tables in use 1, locked 1LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)LOCK BLOCKING MySQL thread id: 72830 block 64282MySQL thread id 64282, OS thread handle 0x7fbe01f7d700, query id 1042364621 172.16.21.10 prod_orderdb updating//以上乌七八糟的有事务的根底信息 大小 行数等等 //下边一行是死锁的其中一方的sql 解析一下ORDER_ID是表的主键/* 276aedd616081752079448345e2ac7/0.1.3// */update t_order set ORDER_STATUS = 4 where ORDER_ID = 234//示意这个事务在期待的锁信息*** (1) WAITING FOR THIS LOCK TO BE GRANTED://以后在期待表t_order上的 X锁, 主键为 8de0b6b3a773935bRECORD LOCKS space id 1252 page no 10646 n bits 112 index `PRIMARY` of table `t_order` trx id 116609954 lock_mode X locks rec but not gap waiting//n_fields 115 示意记录有115列 Record lock, heap no 10 PHYSICAL RECORD: n_fields 115; compact format; info bits 0//第一列 根本就是主键了 0: len 8; hex 8de0b6b3a773935b; asc s [;; 1: len 6; hex 000006ef0a51; asc Q;; 2: len 7; hex 76000006f723b0; asc v # ;; 3: len 16; hex 32303230313231323030333139363331; asc 2020121200319631;; 4: len 4; hex 80000000; asc ;; 5: SQL NULL; 6: len 4; hex 80000002; asc ;; 7: SQL NULL; 8: len 4; hex 8000000b; asc ;;...省略1xx行.//第二个事务信息*** (2) TRANSACTION:TRANSACTION 116609912, ACTIVE 7.886 sec fetching rowsmysql tables in use 1, locked 136461 lock struct(s), heap size 3241512, 306688 row lock(s), undo log entries 12MySQL thread id 72830, OS thread handle 0x7fbe339f7700, query id 1042364593 172.16.21.0 prod_orderdb Searching rows for update//同理第二个sqlupdate t_order set ORDER_STATUS = 4 where EXT_ID = '232SA'//以后事务持有的锁*** (2) HOLDS THE LOCK(S)://持有N个S锁RECORD LOCKS space id 1252 page no 10646 n bits 112 index `PRIMARY` of table `t_order` trx id 116609912 lock mode S locks rec but not gapRecord lock, heap no 2 PHYSICAL RECORD: n_fields 115; compact format; info bits 0 0: len 8; hex 8de0b6b3a7739342; asc s B;; 1: len 6; hex 000006f1aeff; asc ;; 2: len 7; hex 64000006ce20bb; asc d ;; 3: len 16; hex 32303230313231323030333139363036; asc 2020121200319606;; 4: len 4; hex 80000000; asc ;; 5: SQL NULL; 6: len 4; hex 80000002; asc ;; 7: SQL NULL; . . .//++++++++++Record lock, heap no 10 PHYSICAL RECORD: n_fields 115; compact format; info bits 0 0: len 8; hex 8de0b6b3a773935b; asc s [;; 1: len 6; hex 000006ef0a51; asc Q;; 2: len 7; hex 76000006f723b0; asc v # ;; 3: len 16; hex 32303230313231323030333139363331; asc 2020121200319631;; 4: len 4; hex 80000000; asc ;; 5: SQL NULL; . . . //++++++++++ 此处还省略N多 //++++++++++之间的行,同时持有很多行的锁//期待的锁*** (2) WAITING FOR THIS LOCK TO BE GRANTED://同样是期待X锁 主键为 8de0b6b3a773935bRECORD LOCKS space id 1252 page no 10646 n bits 112 index `PRIMARY` of table `t_order` trx id 116609912 lock_mode X locks rec but not gap waitingRecord lock, heap no 10 PHYSICAL RECORD: n_fields 115; compact format; info bits 0 0: len 8; hex 8de0b6b3a773935b; asc s [;; 1: len 6; hex 000006ef0a51; asc Q;; 2: len 7; hex 76000006f723b0; asc v # ;; 3: len 16; hex 32303230313231323030333139363331; asc 2020121200319631;; 4: len 4; hex 80000000; asc ;; 5: SQL NULL; . . .//数据库抉择回滚老本最小的一个事务进行回滚*** WE ROLL BACK TRANSACTION (1)
2.思考
两条sql,对应两条数据,八竿子打不着的两条数据在更新的时候产生了死锁
update t_order set ORDER_STATUS = 4 where ORDER_ID = 234update t_order set ORDER_STATUS = 4 where EXT_ID = '232SA'
以后问题所处环境整顿:
- 数据库事务隔离级别:读提交(READ COMMITTED)
- 表:t_order(ORDER_ID :主键; EXT_ID :非主键,无索引,但惟一)
- 两条sql都是通过惟一条件筛选数据,但不是同一条数据
极客的课程必定粗疏不到官网,所以我去官网搜了搜,因为英语不是很好,就搜了Lock关键字,把搜进去的文章挨个看了下,先看这个
https://dev.mysql.com/doc/ref...
有条件的看原文
没条件的看这 咱们只看读提交局部: 对于UPDATE或 DELETE语句, InnoDB仅对其更新或删除的行持有锁。 MySQL评估WHERE条件后,将开释不匹配行的记录锁。 这大大降低了死锁的可能性,然而依然能够产生。 对于UPDATE语句,如果某行已被锁定,则InnoDB 执行“semi-consistent”读取, 将最新的提交版本返回给MySQL,以便MySQL能够确定该行是否合乎 WHERE条件 UPDATE。 如果该行匹配(必须更新),则MySQL会再次读取该行,这一次将InnoDB其锁定或期待对其进行锁定。
官网例子
CREATE TABLE t (a INT NOT NULL, b INT) ENGINE = InnoDB; INSERT INTO t VALUES (1,2),(2,3),(3,2),(4,3),(5,2); COMMIT;在这种状况下,表没有索引,因而搜寻和索引扫描应用暗藏的汇集索引进行记录锁定(请参见第15.6.2.1节“汇集索引和二级索引”),而不是应用索引列。假如一个会话UPDATE应用以下语句执行 : # Session A START TRANSACTION; UPDATE t SET b = 5 WHERE b = 3; 还假如第二个会话 UPDATE通过在第一个会话的语句之后执行以下语句来执行: # Session B UPDATE t SET b = 4 WHERE b = 2; 在InnoDB执行UPDATE,它首先为每一行获取一个排他锁,而后确定是否对其进行批改。如果InnoDB不批改该行,则开释锁。否则,InnoDB保留该锁直到事务完结。这会影响事务处理,如下所示。应用默认REPEATABLE READ 隔离级别时:第一个UPDATE将在其读取的每一行上取得一个写(x-lock)锁,并且不会开释其中的任何一个: x-lock(1,2); retain x-lock x-lock(2,3); update(2,3) to (2,5); retain x-lock x-lock(3,2); retain x-lock x-lock(4,3); update(4,3) to (4,5); retain x-lock x-lock(5,2); retain x-lock第二UPDATE个尝试获取任何锁的块(因为第一个更新在所有行上都保留了锁),并且直到第一个UPDATE提交或回滚时才继续执行: x-lock(1,2); block and wait for first UPDATE to commit or roll back应用READ COMMITTED则有不同: 第一个UPDATE将在读取的每一行上获取一个写(x-lock)锁,并为未修改的行开释x锁: x-lock(1,2); unlock(1,2) x-lock(2,3); update(2,3) to (2,5); retain x-lock x-lock(3,2); unlock(3,2) x-lock(4,3); update(4,3) to (4,5); retain x-lock x-lock(5,2); unlock(5,2) 第二个UPDATE, InnoDB执行 “semi-consistent”读取,将读取的每一行的最新提交版本返回给MySQL,以便MySQL能够确定该行是否合乎以下 WHERE条件 UPDATE:x-lock(1,2); update(1,2) to (1,4); retain x-lockx-lock(2,3); unlock(2,3)x-lock(3,2); update(3,2) to (3,4); retain x-lockx-lock(4,3); unlock(4,3)x-lock(5,2); update(5,2) to (5,4); retain x-lock然而,如果WHERE条件包含索引列并InnoDB应用索引,则在获取和保留记录锁时仅思考索引列。
看完了例子:尽管"semi-consistent读取"的解释还有点迷糊,然而我的死锁问题曾经大抵有了头绪。
我的表EXT_ID就是没有索引啊,explain了,的确是全表查的,也就是存在这个一个逐行加锁并开释锁的过程。
当然起初又发现了另一篇文档
https://dev.mysql.com/doc/ref...
有条件的看原文去
没条件的持续 :以下示例阐明了锁定申请将导致死锁时如何产生谬误。该示例波及两个客户端A和B。首先,客户端A创立一个蕴含一行的表,而后开始事务。在事务中,A通过S lock 下抉择行来取得对行的锁定:mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;Query OK, 0 rows affected (1.07 sec)mysql> INSERT INTO t (i) VALUES(1);Query OK, 1 row affected (0.09 sec)mysql> START TRANSACTION;Query OK, 0 rows affected (0.00 sec)mysql> SELECT * FROM t WHERE i = 1 FOR SHARE;+------+| i |+------+| 1 |+------+接下来,客户端B开始事务并尝试从表中删除该行:mysql> START TRANSACTION;Query OK, 0 rows affected (0.00 sec)mysql> DELETE FROM t WHERE i = 1;删除操作须要一个X锁,然而它无奈取得X锁,因为A目前持有S锁,两个锁不兼容 ,因而该申请进入针对行和客户端B块的锁申请队列中。最初,客户端A还尝试从表中删除该行:mysql> DELETE FROM t WHERE i = 1;ERROR 1213 (40001): Deadlock found when trying to get lock;try restarting transaction此处产生死锁是因为客户端A须要X锁能力删除该行。然而,不能授予它X锁,因为客户端B曾经有一个X锁定申请,并且正在期待客户端A开释其S锁定。 因为B当时要求锁,所以A持有的S 锁也不能降级X锁。后果, InnoDB为其中一个客户端生成谬误并开释其锁。客户端返回此谬误:ERROR 1213 (40001): Deadlock found when trying to get lock;try restarting transaction再之后就能够授予对另一个客户端的锁定申请,并从表中删除该行。
我只能说这例子跟我的一毛一样啊,早找到这篇文章我还查个屁的隔离级别。
官网文档还是牛B!!!还买什么课啊!!看官网干啥!愣着啊!!