我正在运行以下MySQL UPDATE语句:
UPDATE
mysql> update customer set account_import_id = 1; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
我没有使用事务,为什么会得到这个错误?我甚至尝试重新启动我的MySQL服务器,它没有帮助。
该表有406,733行。
您是否可以更新这个表中的任何其他记录,或者这个表是否被大量使用?我想的是,当它试图获得一个锁,它需要更新这条记录时,设置的超时已经超时。你可以延长时间,这可能会有所帮助。
您正在使用一个事务;Autocommit不会禁用事务,它只是让它们在语句的末尾自动提交。
可能发生的情况是,其他一些线程在某个记录上持有记录锁(您正在更新表中的每个记录!)太长时间,而您的线程正在超时。或者在一个事务中对同一行运行多个(2+)UPDATE查询。
可以通过发出命令查看事件的更多详细信息
SHOW ENGINE INNODB STATUS
事件发生后(在SQL编辑器中)。理想情况下,在安静的测试机上执行此操作。
完全符合MarkR的说法。自动提交使每个语句成为一个语句事务。
SHOW ENGINE INNODB STATUS应该给你一些关于死锁原因的线索。还要仔细查看慢速查询日志,看看还有什么正在查询表,并尝试删除正在执行满表罐操作的任何内容。行级锁定工作得很好,但当您试图锁定所有行的时候就不行了!
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,并查看其他哪个事务正在锁定你的事务。
SHOW ENGINE INNODB STATUS\G
查看一下您的数据库是否进行了微调,特别是事务隔离。增加innodb_lock_wait_timeout变量不是一个好主意。
innodb_lock_wait_timeout
检查MySQL中的数据库事务隔离级别:
mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation, @@session.tx_isolation; +-----------------------+-----------------+------------------------+ | @@GLOBAL.tx_isolation | @@tx_isolation | @@session.tx_isolation | +-----------------------+-----------------+------------------------+ | REPEATABLE-READ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+------------------------+ 1 row in set (0.00 sec)
您可以通过更改隔离级别来获得改进。使用类似oracle的READ COMMITTED而不是REPEATABLE READ。REPEATABLE READ是InnoDB的默认值。
READ COMMITTED
REPEATABLE READ
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)
另外,尽量只在必要时使用SELECT FOR UPDATE。
SELECT FOR UPDATE
如何强制解锁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>;
确保数据库表使用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)在这种情况下遇到了类似的错误消息,可能需要仔细检查…
行数并不大……如果account_import_id不是主键,则在它上创建一个索引。
CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
某些东西阻塞了查询的执行。很可能是另一个查询更新、插入或删除查询中的某个表。你必须找出那是什么:
SHOW PROCESSLIST;
一旦你找到阻塞进程,找到它的id并运行:
id
KILL {id};
重新运行初始查询。
但我的问题是,我写了一些糟糕的SQL(作为一个新手)和几个进程对记录有一个锁<-不确定适当的措辞。我最终不得不:SHOW PROCESSLIST,然后使用KILL <id>杀死id
SHOW PROCESSLIST
KILL <id>
有同样的错误,即使我只是更新一个表与一个条目,但重新启动mysql后,它被解决了。
在我的实例中,我正在运行一个异常查询来修复数据。如果你在你的查询中锁定了表,那么你将不必处理锁定超时:
LOCK TABLES `customer` WRITE; update customer set account_import_id = 1; UNLOCK TABLES;
这对于正常使用可能不是一个好主意。
更多信息见:MySQL 8.0参考手册
如果你刚刚杀死了一个大的查询,rollback将花费时间。如果在已终止查询回滚之前发出另一个查询,则可能会得到一个锁定超时错误。这就是我的遭遇。解决办法就是再等一会儿。
rollback
细节:
我发出了一个DELETE查询,从大约100万行中删除大约90万行。
我错误地运行这个(只删除10%的行): DELETE FROM table WHERE MOD(id,10) = 0 < / p >
DELETE FROM table WHERE MOD(id,10) = 0
而不是这个(删除90%的行): DELETE FROM table WHERE MOD(id,10) != 0 < / p >
DELETE FROM table WHERE MOD(id,10) != 0
我想要删除90%的行,而不是10%。因此,我在MySQL命令行中终止了进程,因为我知道它会回滚到目前为止删除的所有行。
然后我立即运行了正确的命令,很快就得到了一个lock timeout exceeded错误。我意识到锁实际上可能是被杀死的查询的rollback仍然在后台发生。所以我等了几秒钟,重新运行了查询。
lock timeout exceeded
我有2个Doctrine DBAL连接,其中一个是非事务性的(用于重要日志),它们旨在并行运行,而不是相互依赖。
CodeExecution( TransactionConnectionQuery() TransactionlessConnectionQuery() )
我的集成测试被包装到事务中,以便在每次测试后进行数据回滚。
beginTransaction() CodeExecution( TransactionConnectionQuery() TransactionlessConnectionQuery() // CONFLICT ) rollBack()
我的解决方案是在这些测试中禁用包装事务,并以另一种方式重置db数据。
我来自谷歌,我只是想添加对我有效的解决方案。我的问题是,我试图删除一个大表的记录,它有很多FK级联,所以我得到了与OP相同的错误。
我禁用了autocommit,然后它只在SQL语句的末尾添加COMMIT就工作了。据我所知,这将逐位释放缓冲区,而不是在命令结束时等待。
autocommit
COMMIT
为了保持OP的示例,这应该是有效的:
mysql> set autocommit=0;
mysql> update customer set account_import_id = 1; commit;
不要忘记重新激活autocommit如果你想让MySQL配置像以前一样。
mysql> set autocommit=1;
mysql->SHOW PROCESSLIST; kill xxxx;
然后杀了睡着的那个。对我来说是2156。
尝试更新以下两个参数,因为它们必须具有默认值。
Innodb_lock_wait_timeout = 50
innodb_rollback_on_timeout = ON
要检查参数值,可以使用下面的SQL语句。
显示全局变量:innodb_rollback_on_timeout
我们昨天遇到了这个问题,在仔细研究了这里的每一个建议的解决方案,以及其他答案/论坛的其他几个解决方案后,我们最终在意识到实际问题时解决了它。
由于一些糟糕的计划,我们的数据库存储在一个挂载的卷上,该卷也接收我们的常规自动备份。该容量已达到最大容量。
一旦我们清理了一些空间并重新启动,这个错误就被解决了。
请注意,我们还手动杀死了几个进程:kill <process_id>;,所以这可能仍然是必要的。
kill <process_id>;
总的来说,我们的结论是,令人难以置信的是,我们的日志或警告都没有直接提到磁盘空间不足,但这似乎确实是根本原因。
在我们的案例中,这个问题与锁本身没有太大关系。
问题是我们的一个应用程序端点需要并行打开2个连接来处理单个请求。
例子:
我们的应用程序的连接池限制为10个连接。
解决方案:
小贴士:
您可以通过尝试将连接池限制降低到1并测试应用程序来轻松检查是否有嵌套连接。
在做一些测试时,我也遇到过类似的问题。
原因 -在我的情况下,事务没有从我的spring引导应用程序提交,因为我在执行期间杀死了@transactional函数(当函数更新一些行时)。由于该事务从未提交到数据库(MySQL)。
结果 -不能从任何地方更新这些行。但是能够更新表的其他行。
mysql> update some_table set some_value = "Hello World" where id = 1; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
解决方案 -杀死所有使用的MySQL进程