收到“超出锁定等待超时;尝试重新启动交易”,即使我没有使用交易


267

我正在运行以下MySQL UPDATE语句:

mysql> update customer set account_import_id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

我没有使用交易,为什么会出现此错误?我什至尝试重新启动MySQL服务器,但没有帮助。

该表有406,733行。

Answers:


211

您正在使用交易;autocommit不会禁用事务,它只是使它们在语句末尾自动提交。

发生的情况是,某个其他线程在某个记录上保持记录锁定(您正在更新表中的每个记录!)的时间太长,导致您的线程超时。

您可以通过以下方式查看事件的更多详细信息:

SHOW ENGINE INNODB STATUS

事件之后(在sql编辑器中)。理想情况下,请在安静的测试机上执行此操作。


1
有没有办法将输出保存到文件?我尝试了SHOW ENGINE INNODB STATUS \ G> innodb_stat.txt,但没有用。
yantaq 2015年

14
从命令行:mysql [插入凭据] -e“ SHOW ENGINE INNODB STATUS \ G”> innodb_stat.txt
VenerableAgents

如果许多mysql线程(或进程)处于繁忙状态,例如某些查询需要很长时间,那么您必须等待一些process空闲。如果是这样,您可能会收到此错误。我对吗?
zhuguowei

6
在单个事务期间在同一行上运行多个(2+)UPDATE查询也会导致此错误。
Okneloper '18

对于使用Python的用户,MySQL可以使用或connection.commit()提交。INSERTUPDATE
AER

334

如何为MySQL中的锁定表强制解锁:

像这样破坏锁可能会导致在导致锁的sql语句上不强制执行数据库中的原子性

这是骇人听闻的,正确的解决方案是修复导致锁定的应用程序。但是,当美元紧缩时,迅速的踢动将使事情再次发生变化。

1)输入MySQL

mysql -u your_user -p

2)让我们看看锁定表的列表

mysql> show open tables where in_use>0;

3)让我们看一下当前进程的列表,其中之一正在锁定您的表

mysql> show processlist;

4)杀死其中一个进程

mysql> kill <put_process_id_here>;

14
这是危险且骇人听闻的。正确的解决方案是修复您的应用程序。
Zenexer

71
废话,这使您可以撤消消息,然后修复应用程序。如果我可以给这个人100票,以解决这个问题,我必须立即解决。
蜥蜴人

7
我同意蜥蜴。在我没有权限调用SHOW ENGINE INNODB状态的情况下,这是一个非常有用的解决方案
Travis Schneeberger

8
这样杀死一个长期运行的查询怎么会很危险?客户端调用只会得到一个错误。
Xeoncross

7
@EricLeschinski我明白您的意思,但是我不得不问为什么在世界上您会在生命攸关的系统上使用像MySQL这样的草率数据库?
Xeoncross

96
mysql> set innodb_lock_wait_timeout=100

Query OK, 0 rows affected (0.02 sec)

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 100   |
+--------------------------+-------+

现在再次触发锁定。您有100秒的时间向SHOW ENGINE INNODB STATUS\G数据库发出A ,并查看有哪些其他事务正在锁定您的事务。


8
这个答案不能解释为什么问问者犯了错误。您能否详细说明为什么不给出答案?
雪橇2012年

5
+1尽管这不能直接回答问题,但对我来说,这是解决此问题的很好参考。
约瑟夫·拉什

1
@ArtB dev.mysql.com/doc/innodb/1.1/en/...在本质上是OP收到错误,因为锁被称为餐桌上和结束交易之前所经过的时间超过了lock_wait_timeout价值
fyrye

70

看一下您的数据库是否经过微调。尤其是事务隔离。增加innodb_lock_wait_timeout变量不是一个好主意。

在mysql cli中检查数据库事务隔离级别:

mysql> SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation, @@session.transaction_isolation;
+-----------------------+-----------------+------------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation  | @@session.tx_isolation |
+-----------------------+-----------------+------------------------+
| REPEATABLE-READ       | REPEATABLE-READ | REPEATABLE-READ        |
+-----------------------+-----------------+------------------------+
1 row in set (0.00 sec)

您可以通过更改隔离级别来获得改进,使用像READ COMMITTED这样的oracle来代替REPEATABLE READ(InnoDB默认值)

mysql> SET tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> 

也请仅在必要时尝试使用SELECT FOR UPDATE。


1
这是解决锁定问题的好方法。
redolent

3
对我来说很棒,my.cnf版本是[mysqld] transaction-isolation = READ-COMMITTED
Benoit Gauthier

请注意:MySQL 8已将tx_isolation变量重命名为transaction_isolation
xonya

但是,当您从读取隔离更改为“读取已提交”时,必须格外小心,以了解您要使用的内容。您可能会得到不想要的脏数据。维基百科的独立化
拥抱

27

没有建议的解决方案对我有用,但这确实可行。

某些东西阻止了查询的执行。最有可能是另一个查询在您的查询中的一个表中进行更新,插入或删除。您必须找出是什么:

SHOW PROCESSLIST;

找到阻塞过程后,找到它id并运行:

KILL {id};

重新运行您的初始查询。


我不小心杀死了SHOW PROCESSLIST列出的所有进程;现在我在phpmyadmin中收到500错误。那500条错误与杀死这些进程有关吗?如果是,如何重新启动它。
Dashrath

我可以看到您所描述的内容,但是取消该进程无效。该进程的命令被“杀死”,但仍保留在进程列表中。
Torsten

12

MarkR所说的100%。自动提交使每个语句成为一个语句事务。

SHOW ENGINE INNODB STATUS应该给您一些有关死锁原因的线索。也请仔细查看您的慢查询日志,以了解还有什么查询表并尝试删除正在执行全表扫描的所有内容。行级锁定效果很好,但是当您尝试锁定所有行时却不能!


5

您是否可以更新该表中的任何其他记录,或者该表是否被大量使用?我在想的是,在尝试获取锁时,它需要更新此记录,而设置的超时已超时。您可能可以增加时间,这可能会有所帮助。


3
也许innodb_lock_wait_timeoutmy.cnf
oblig

1
我在my.cnf中设置:<br/> innodb_lock_wait_timeout = 120 <br/>,对于MySQL 5.5,默认值为50。进行此更改之后,我无法在单元测试中看到此问题!从proxool切换到tomcat jdbc池后,发生了这种情况。可能是由于tomcat pool有更多的交易时间吗?
2012年

3

行数不是很多...如果不是主键,请在account_import_id上创建索引。

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);

天哪...这救了我。我通过删除索引来固定生产数据库,并对其进行了修复。谢谢。
Nick Gotch

2

如果您刚刚杀死了一个大查询,将需要一些时间到rollback。如果在终止的查询完成回滚之前发出另一个查询,则可能会收到锁定超时错误。那就是我发生的事情。解决的办法是稍等一下。

细节:

我已经发出了DELETE查询,以删除大约100万行中的大约900,000。

我错误地运行了此命令(仅删除了10%的行): DELETE FROM table WHERE MOD(id,10) = 0

代替此操作(删除90%的行): DELETE FROM table WHERE MOD(id,10) != 0

我想删除90%的行,而不是10%。因此,我在MySQL命令行中终止了该进程,因为它知道该进程将回滚到目前为止已删除的所有行。

然后,我立即运行了正确的命令,并在lock timeout exceeded之后不久出现了错误。我意识到,锁实际上可能是rollback仍在后台发生的被杀死查询的锁。因此,我等待了几秒钟,然后重新运行查询。


1

确保数据库表正在使用InnoDB存储引擎和READ-COMMITTED事务隔离级别。

您可以通过SELECT @@ GLOBAL.tx_isolation,@@ tx_isolation进行检查;在mysql控制台上。

如果未将其设置为READ-COMMITTED,则必须进行设置。在设置它之前,请确保您在mysql中具有SUPER特权。

您可以从http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html寻求帮助。

通过设置此选项,我认为您的问题将得到解决。


您可能还想检查一下您是否不想同时在两个进程中进行更新。在这种情况下,用户(@tala)遇到了类似的错误消息,也许要仔细检查一下...


1

我来自Google,我只是想添加对我有用的解决方案。我的问题是我正在尝试删除级联很多FK的大表的记录,因此我得到了与OP相同的错误。

我禁用了该功能autocommit,然后仅COMMIT在SQL语句的末尾添加了它。据我了解,这会一点一点地释放缓冲区,而不是在命令末尾等待。

为了与OP的示例保持一致,这应该可行:

mysql> set autocommit=0;

mysql> update customer set account_import_id = 1; commit;

autocommit如果您想像以前一样保留MySQL配置,请不要忘记再次激活。

mysql> set autocommit=1;


0

晚了聚会(像往常一样),但是我的问题是我写了一些不好的SQL(是新手),并且几个进程对记录有锁定<-不确定是否合适。我最终不得不这样做:SHOW PROCESSLIST然后使用杀死IDKILL <id>


0

当我使用php语言构造出口时,这种事情发生在我身上。在交易中。然后,该事务“挂起”,您需要杀死mysql进程(如上使用processlist所述)。


0

在我的实例中,我正在运行异常查询以修复数据。 如果您锁定查询中的表,则无需处理锁定超时:

LOCK TABLES `customer` WRITE;
update customer set account_import_id = 1;
UNLOCK TABLES;

对于正常使用,这可能不是一个好主意。

有关更多信息,请参见: MySQL 8.0参考手册


0

我遇到了具有2个Doctrine DBAL连接的情况,其中之一是非事务性的(用于重要日志),它们旨在并行运行,而不依赖于彼此。

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

我的集成测试被包装到事务中,以便在测试后回滚数据。

beginTransaction()
CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery() // CONFLICT
)
rollBack()

我的解决方案是在那些测试中禁用包装事务,并以另一种方式重置数据库数据。


-4

即使我只用一个条目更新一个表,也发生了同样的错误,但是在重启mysql之后,它得到了解决。

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.