如何在MySQL上调试超出锁等待超时的时间?


269

在生产错误日志中,我偶尔会看到:

SQLSTATE [HY000]:常规错误:1205超出了锁定等待超时;尝试重新启动事务

我知道当时哪个查询正在尝试访问数据库,但是有没有办法找出在那个准确时刻哪个查询具有锁定?


1
我强烈建议大家
试一试

Answers:


261

这就是这个词 交易。从该语句可以明显看出,查询正在尝试更改一个或多个InnoDB表中的至少一行。

由于您知道查询,因此所有要访问的表都是罪魁祸首。

从那里,您应该可以运行 SHOW ENGINE INNODB STATUS\G

您应该能够看到受影响的表

您会获得各种其他的锁定和互斥信息。

这是我的一位客户的样品:

mysql> show engine innodb status\G
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
110514 19:44:14 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 4 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 9014315, signal count 7805377
Mutex spin waits 0, rounds 11487096053, OS waits 7756855
RW-shared spins 722142, OS waits 211221; RW-excl spins 787046, OS waits 39353
------------------------
LATEST FOREIGN KEY ERROR
------------------------
110507 21:41:35 Transaction:
TRANSACTION 0 606162814, ACTIVE 0 sec, process no 29956, OS thread id 1223895360 updating or deleting, thread declared inside InnoDB 499
mysql tables in use 1, locked 1
14 lock struct(s), heap size 3024, 8 row lock(s), undo log entries 1
MySQL thread id 3686635, query id 124164167 10.64.89.145 viget updating
DELETE FROM file WHERE file_id in ('6dbafa39-7f00-0001-51f2-412a450be5cc' )
Foreign key constraint fails for table `backoffice`.`attachment`:
,
  CONSTRAINT `attachment_ibfk_2` FOREIGN KEY (`file_id`) REFERENCES `file` (`file_id`)
Trying to delete or update in parent table, in index `PRIMARY` tuple:
DATA TUPLE: 17 fields;
 0: len 36; hex 36646261666133392d376630302d303030312d353166322d343132613435306265356363; asc 6dbafa39-7f00-0001-51f2-412a450be5cc;; 1: len 6; hex 000024214f7e; asc   $!O~;; 2: len 7; hex 000000400217bc; asc    @   ;; 3: len 2; hex 03e9; asc   ;; 4: len 2; hex 03e8; asc   ;; 5: len 36; hex 65666635323863622d376630302d303030312d336632662d353239626433653361333032; asc eff528cb-7f00-0001-3f2f-529bd3e3a302;; 6: len 40; hex 36646234376337652d376630302d303030312d353166322d3431326132346664656366352e6d7033; asc 6db47c7e-7f00-0001-51f2-412a24fdecf5.mp3;; 7: len 21; hex 416e67656c73204e6f7720436f6e666572656e6365; asc Angels Now Conference;; 8: len 34; hex 416e67656c73204e6f7720436f6e666572656e6365204a756c7920392c2032303131; asc Angels Now Conference July 9, 2011;; 9: len 1; hex 80; asc  ;; 10: len 8; hex 8000124a5262bdf4; asc    JRb  ;; 11: len 8; hex 8000124a57669dc3; asc    JWf  ;; 12: SQL NULL; 13: len 5; hex 8000012200; asc    " ;; 14: len 1; hex 80; asc  ;; 15: len 2; hex 83e8; asc   ;; 16: len 4; hex 8000000a; asc     ;;

But in child table `backoffice`.`attachment`, in index `PRIMARY`, there is a record:
PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 30; hex 36646261666133392d376630302d303030312d353166322d343132613435; asc 6dbafa39-7f00-0001-51f2-412a45;...(truncated); 1: len 30; hex 38666164663561652d376630302d303030312d326436612d636164326361; asc 8fadf5ae-7f00-0001-2d6a-cad2ca;...(truncated); 2: len 6; hex 00002297b3ff; asc   "   ;; 3: len 7; hex 80000040070110; asc    @   ;; 4: len 2; hex 0000; asc   ;; 5: len 30; hex 416e67656c73204e6f7720436f6e666572656e636520446f63756d656e74; asc Angels Now Conference Document;;

------------
TRANSACTIONS
------------
Trx id counter 0 620783814
Purge done for trx's n:o < 0 620783800 undo n:o < 0 0
History list length 35
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1192212800
MySQL thread id 5341758, query id 189708501 127.0.0.1 lwdba
show innodb status
---TRANSACTION 0 620783788, not started, process no 29956, OS thread id 1196472640
MySQL thread id 5341773, query id 189708353 10.64.89.143 viget
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1223895360
MySQL thread id 5341667, query id 189706152 10.64.89.145 viget
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1227888960
MySQL thread id 5341556, query id 189699857 172.16.135.63 lwdba
---TRANSACTION 0 620781112, not started, process no 29956, OS thread id 1222297920
MySQL thread id 5341511, query id 189696265 10.64.89.143 viget
---TRANSACTION 0 620783736, not started, process no 29956, OS thread id 1229752640
MySQL thread id 5339005, query id 189707998 10.64.89.144 viget
---TRANSACTION 0 620783785, not started, process no 29956, OS thread id 1198602560
MySQL thread id 5337583, query id 189708349 10.64.89.145 viget
---TRANSACTION 0 620783469, not started, process no 29956, OS thread id 1224161600
MySQL thread id 5333500, query id 189708478 10.64.89.144 viget
---TRANSACTION 0 620781240, not started, process no 29956, OS thread id 1198336320
MySQL thread id 5324256, query id 189708493 10.64.89.145 viget
---TRANSACTION 0 617458223, not started, process no 29956, OS thread id 1195141440
MySQL thread id 736, query id 175038790 Has read all relay log; waiting for the slave I/O thread to update it
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
 ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
519878 OS file reads, 18962880 OS file writes, 13349046 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 6.25 writes/s, 4.50 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 1190, seg size 1192,
174800 inserts, 174800 merged recs, 54439 merges
Hash table size 35401603, node heap has 35160 buffer(s)
0.50 hash searches/s, 11.75 non-hash searches/s
---
LOG
---
Log sequence number 28 1235093534
Log flushed up to   28 1235093534
Last checkpoint at  28 1235091275
0 pending log writes, 0 pending chkp writes
12262564 log i/o's done, 3.25 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 18909316674; in additional pool allocated 1048576
Dictionary memory allocated 2019632
Buffer pool size   1048576
Free buffers       175763
Database pages     837653
Modified db pages  6
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 770138, created 108485, written 7795318
0.00 reads/s, 0.00 creates/s, 4.25 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
1 read views open inside InnoDB
Main thread process no. 29956, id 1185823040, state: sleeping
Number of rows inserted 6453767, updated 4602534, deleted 3638793, read 388349505551
0.25 inserts/s, 1.25 updates/s, 0.00 deletes/s, 2.75 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set, 1 warning (0.00 sec)

您应该考虑通过设置innodb_lock_wait_timeout来增加InnoDB的锁定等待超时值,默认值为50秒

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    |
+--------------------------+-------+
1 row in set (0.01 sec)

您可以/etc/my.cnf使用此行将其永久设置为更高的值

[mysqld]
innodb_lock_wait_timeout=120

并重启mysql。如果您目前无法重启mysql,请运行以下命令:

SET GLOBAL innodb_lock_wait_timeout = 120; 

您也可以在整个会话期间进行设置

SET innodb_lock_wait_timeout = 120; 

跟着你的查询


5
对于内置的InnoDB,innodb_lock_wait_timeout只能在服务器启动时设置变量。对于InnoDB插件,它可以在启动时设置或在运行时更改,并且具有全局值和会话值。
Timo Huovinen 2013年

1
嗨@rolandomysqldba,可以请您给我这个职位的建议我:stackoverflow.com/questions/18267565/...
马尼什Sapkal

2
尝试运行第一个查询时收到此错误:SQL Error (1064): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\G' at line 1
Iulian Onofrei 2014年

1
@Pacerier每次重新启动mysqld时,都必须SET GLOBAL innodb_lock_wait_timeout = 120;再次运行。如果/etc/my.cnf有选项,innodb_lock_wait_timeout则为您设置。不是每个人都有SUPER权限在全球范围内改变了其他人(dev.mysql.com/doc/refman/5.6/en/...
RolandoMySQLDBA

3
@IulianOnofrei \ G字符是MySQL命令行的一个特殊功能,它会更改输出的显示方式。对于其他MySQL客户端,只需使用常规分号即可。
thenickdude

83

正如有人在许多SO线程之一中提到的那样:有时锁定表的进程在进程列表中显示为休眠!我一直在梳理头发,直到杀死所有在相关数据库中打开的睡眠线程(当时没有一个处于活动状态)。最终,该表解锁了,并让更新查询运行。

评论者说,类似于“有时,一个MySQL线程锁定一个表,然后在它等待与MySQL无关的事情发生时休眠”。

重新查看show engine innodb status日志后(一旦我找到负责锁的客户端),我注意到问题卡住的线程列在事务列表的最底部,即将出现错误的活动查询下面由于冻结的锁而退出:

------------------
---TRANSACTION 2744943820, ACTIVE 1154 sec(!!)
2 lock struct(s), heap size 376, 2 row lock(s), undo log entries 1
MySQL thread id 276558, OS thread handle 0x7f93762e7710, query id 59264109 [ip] [database] cleaning up
Trx read view will not see trx with id >= 2744943821, sees < 2744943821

(不确定“ Trx读取视图”消息是否与冻结的锁定有关,但是与其他活动事务不同,该消息不会与已发出的查询一起显示,而是声称该事务正在“清理”,但有多个行锁)

这个故事的寓意是即使线程正在休眠,事务也可以处于活动状态。


2
我不能说你救了我的命,但你确定要把我的心放在和平上。阅读您的答案,我发现一个令人毛骨悚然的线程处于活动状态3260秒,并且没有出现在任何地方。杀死它后,我所有的问题都解决了!
kommradHomer 2015年

这是我的问题。时间为20,000秒的休眠事务,这阻止了Rails应用程序中的延迟作业正常运行。感谢@Eirik
bigtex777 '16

知道为什么睡眠交易无论如何都不会被杀死吗?就像,您可以设置一个超时必须在其中完成交易吗?
patrickdavey

1
其他对锁定事务的搜索可能有帮助的命令:show processlist;显示当前正在执行的进程的详尽列表,这很不错,因为它是的精简版本show engine innodb status\g。另外,如果您的数据库位于Amazon RDS实例上,则可以CALL mysql.rds_kill(<thread_id>);用来杀死线程。我认为它具有更高的权限,因为它使我可以杀死比普通进程更多的进程kill <thread_id>;-请注意,这些进程应在MySQL CLI中运行
nickang

1
任何人都有此消息的来源-也许是说明锁定在COMMIT阶段之前放置的文档页面?尽管看到了这个确切的问题,但我仍然找不到任何东西,并且通过杀死持有锁的睡眠线程将其清除了。
艾琳·舒恩诺夫

42

由于MySQL的流行,难怪就超出了Lock等待超时;尝试重新启动事务异常在SO上引起了很多关注。

争用越多,死锁的可能性就越大,数据库引擎将通过使一个死锁的事务超时来解决死锁。此外,长时间运行的事务已修改(例如UPDATEDELETE)大量条目(如High-Performance Java Persistence中所述,它们进行锁定以避免脏写异常)书中),很可能与其他事务产生冲突。

尽管使用InnoDB MVCC,您仍然可以使用FOR UPDATE子句请求显式锁定。但是,与其他流行的DB(Oracle,MSSQL,PostgreSQL,DB2)不同,MySQL 使用REPEATABLE_READ默认隔离级别

现在,您获取的锁(通过修改行或使用显式锁)将在当前运行的事务期间保持不变。如果你想之间的差异的一个很好的解释REPEATABLE_READ,并READ COMMITTED在问候锁定,请阅读这篇文章的Percona

在REPEATABLE READ中,在事务期间将保留在事务期间获取的每个锁。

在READ COMMITTED中,与STATE无关的锁将在STATEMENT完成后释放。

...

这意味着在UPDATE语句完成后,在READ COMMITTED中,其他事务可以自由更新它们本来无法更新的行(在REPEATABLE READ中)。

因此:隔离级别(REPEATABLE_READSERIALIZABLE)越大死锁的可能性。这不是“本身”的问题,而是一个权衡。

使用可以获得非常好的结果READ_COMMITED,因为当使用跨越多个HTTP请求的逻辑事务时,需要防止应用程序级丢失更新。在乐观锁定方法的目标丢失更新可能发生,即使你使用的SERIALIZABLE隔离级别,同时通过允许您使用减少了锁争用READ_COMMITED


4
锁定等待超时与死锁不同吗?例如,如果一个线程出于正当原因持有锁60秒钟,则可能会发生锁等待超时。是不是真的存在死锁,MySQL是否会检测到死锁并立即终止事务,这与锁等待超时无关吗?
ColinM 2014年

1
你是对的。DB在超时后检测到死锁并杀死了一个等待的进程,因此一个事务获胜而另一个事务失败。但是您持有锁的时间越长,应用程序的可伸缩性就越差。即使您没有遇到死锁,您仍然会增加应用程序运行时行为的可序列化部分。
Vlad Mihalcea 2014年

19

为了记录,锁定等待超时异常也会在发生死锁且MySQL无法检测到它时发生,因此它只是超时。另一个原因可能是查询运行时间过长,但是更容易解决/修复,但是在此我将不再描述这种情况。

如果死锁是在两个事务中“适当”构造的,则MySQL通常能够处理死锁。然后,MySQL仅杀死/回滚拥有较少锁的事务(它的重要性较小,因为它将影响较少的行),而让另一事务完成。

现在,假设有两个流程A和B以及3个事务:

Process A Transaction 1: Locks X
Process B Transaction 2: Locks Y
Process A Transaction 3: Needs Y => Waits for Y
Process B Transaction 2: Needs X => Waits for X
Process A Transaction 1: Waits for Transaction 3 to finish

(see the last two paragraph below to specify the terms in more detail)

=> deadlock 

这是一个非常不幸的设置,因为MySQL无法看到存在死锁(跨3个事务)。所以MySQL所做的就是……什么都没有!它只是等待,因为它不知道该怎么办。它等待,直到第一个获取的锁超过超时(进程A事务1:锁X),然后这将解除阻止锁X的锁定,从而解锁事务2等。

技巧是找出导致第一个锁定(锁定X)的原因(哪个查询)。您将很容易看到(show engine innodb status)事务3等待事务2,但看不到事务2正在等待哪个事务(事务1)。MySQL不会打印与事务1相关的任何锁或查询。唯一的提示是在事务列表的最底部。show engine innodb status打印输出的)的,您将看到事务1显然什么也不做(但实际上正在等待事务3完成)完)。

此处介绍了如何查找哪个SQL查询使正在等待的给定事务被授予锁(X锁)的技术。 Tracking MySQL query history in long running transactions

如果您想知道示例中到底是什么过程和事务。该过程是一个PHP过程。事务是innodb-trx-table定义的事务。就我而言,我有两个PHP流程,每个流程我都手动启动了一个事务。有趣的是,即使我在一个进程中启动了一个事务,MySQL实际上在内部使用了两个独立的事务(我不知道为什么,也许有些MySQL开发人员可以解释)。

MySQL在内部管理自己的事务,并决定(以我为例)使用两个事务来处理来自PHP进程(进程A)的所有SQL请求。事务1等待事务3完成的声明是MySQL内部的事情。MySQL“知道”事务1和事务3实际上是作为一个“事务”请求(来自进程A)的一部分实例化的。现在,由于“事务3”(“事务”的子部分)被阻止,因此整个“事务”被阻止了。因为“事务”无法完成事务1(也是“事务”的子部分)也被标记为未完成。这就是我所说的“事务1等待事务3完成”。


14

出现此异常的最大问题是,它通常无法在测试环境中重现,并且当它发生在产品上时,我们也不会运行innodb引擎状态。因此,在一个项目中,我将以下代码放入此异常的catch块中。这有助于我在发生异常时了解引擎状态。这很有帮助。

Statement st = con.createStatement();
ResultSet rs =  st.executeQuery("SHOW ENGINE INNODB STATUS");
while(rs.next()){
    log.info(rs.getString(1));
    log.info(rs.getString(2));
    log.info(rs.getString(3));
}

11

看一下该pt-deadlock-logger实用程序的手册页:

brew install percona-toolkit
pt-deadlock-logger --ask-pass server_name

它从上述engine innodb status提到的信息中提取信息,也可以用来创建daemon每30秒运行一次的信息。


3
此工具现在是Percona工具包的
Brad Mace 2013年

锁等待超时与死锁不同,特别是innodb不会显示有关死锁的任何信息,因为它们没有被检测到死锁,因此我认为pt-deadlock-logger没有任何帮助。
杰·帕罗林

不过,锁定超时和死锁是相关的-请参见dev.mysql.com/doc/refman/5.7/en/innodb-deadlock-detection.html
Andrei Sura,

11

从上述Rolando的答案推断出,正是这些因素阻止了您的查询:

---TRANSACTION 0 620783788, not started, process no 29956, OS thread id 1196472640
MySQL thread id 5341773, query id 189708353 10.64.89.143 viget

如果您需要执行查询而不能等待其他查询运行,请使用MySQL线程ID将其杀死:

kill 5341773 <replace with your thread id>

(显然是从mysql内部而不是外壳中)

您必须从以下位置找到线程ID:

show engine innodb status\G

命令,并找出哪个是阻塞数据库的那个。


1
你怎么知道的5341773?我看不出有何区别。
沃丁

不,可能不是那个threadID,这只是一个例子。您必须从“ show engine innodb status \ G”命令中找到线程ID,并找出哪一个是阻塞数据库的线程。
Ellert van Koperen

1
谢谢。因此,换句话说,如果不将它们一一杀死,就无法说出是哪一个?
沃丁,2015年

在事务列表中,您可以看到哪些正在运行以及运行了多长时间。因此,无需一一杀死它们,该列表通常可以使您对发生的事情有一个很好的了解。
Ellert van Koperen

10

这是我最终要做的事情,以找出导致“锁定超时”问题的“其他查询”。在应用程序代码中,我们在专用于此任务的单独线程上跟踪所有未决的数据库调用。如果任何数据库调用花费的时间超过N秒(对我们来说是30秒),我们将记录:

-- Pending InnoDB transactions
SELECT * FROM information_schema.innodb_trx ORDER BY trx_started; 

-- Optionally, log what transaction holds what locks
SELECT * FROM information_schema.innodb_locks;

通过上述操作,我们能够查明并发查询,这些查询锁定了导致死锁的行。就我而言,它们就像INSERT ... SELECT是与普通SELECT锁定底层行的语句一样。然后,您可以重新组织代码或使用其他事务隔离,例如未提交读。

祝好运!


9

您可以使用:

show full processlist

它将列出MySQL中的所有连接以及连接的当前状态以及正在执行的查询。还有一个较短的变体show processlist;,显示截断的查询以及连接状态。



-2

激活MySQL general.log(磁盘密集型),并使用mysql_analyse_general_log.pl提取长期运行的事务,例如:

--min-duration =您的innodb_lock_wait_timeout值

之后禁用general.log。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.