我正在运行以下 MySQLUPDATE
语句:
mysql> update customer set account_import_id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
我没有使用交易,为什么会出现此错误?我什至尝试重新启动我的 MySQL 服务器,但没有帮助。
该表有 406,733 行。
我正在运行以下 MySQLUPDATE
语句:
mysql> update customer set account_import_id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
我没有使用交易,为什么会出现此错误?我什至尝试重新启动我的 MySQL 服务器,但没有帮助。
该表有 406,733 行。
如何强制解锁 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>;
您正在使用交易;autocommit 不会禁用事务,它只是让它们在语句结束时自动提交。
正在发生的事情是,其他一些线程在某条记录上持有记录锁(您正在更新表中的每条记录!)太长时间,并且您的线程正在超时。
您可以通过发出来查看事件的更多详细信息
SHOW ENGINE INNODB STATUS
事件之后(在 SQL 编辑器中)。理想情况下,在安静的测试机器上执行此操作。
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 秒的时间向数据库发出 aSHOW ENGINE INNODB STATUS\G
并查看其他哪些事务正在锁定您的事务。
看看您的数据库是否经过微调。尤其是事务隔离。增加 innodb_lock_wait_timeout 变量不是一个好主意。
在 mysql cli 中检查您的数据库事务隔离级别:
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)
您可以通过更改隔离级别来获得改进,使用像 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。
某些东西阻止了查询的执行。很可能是另一个查询从您的查询中的一个表中更新、插入或删除。你必须找出那是什么:
SHOW PROCESSLIST;
找到阻塞进程后,找到它id
并运行:
KILL {id};
重新运行您的初始查询。
100% 符合 MarkR 所说的。自动提交使每个语句成为一个语句事务。
SHOW ENGINE INNODB STATUS
应该给你一些关于僵局原因的线索。好好看看你的慢查询日志,看看还有什么在查询表,并尝试删除任何正在执行全表扫描的东西。行级锁定效果很好,但当您尝试锁定所有行时就不行了!
您可以更新此表中的任何其他记录,还是此表被大量使用?我在想的是,当它尝试获取需要更新此记录的锁时,设置的超时已超时。您可以增加时间,这可能会有所帮助。
行数不是很大...如果它不是主键,则在 account_import_id 上创建一个索引。
CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
如果你刚刚杀死了一个大查询,那么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
仍在后台发生的被终止查询的锁。所以我等了几秒钟,然后重新运行了查询。
尝试更新以下两个参数,因为它们必须具有默认值。
innodb_lock_wait_timeout = 50
innodb_rollback_on_timeout = ON
要检查参数值,您可以使用以下 SQL。
显示全局变量,如“innodb_rollback_on_timeout”;
确保数据库表使用 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)在这种情况下遇到了类似的错误消息,可能会仔细检查......
我来自 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;
晚会(像往常一样)但是我的问题是我写了一些糟糕的 SQL(作为新手)并且几个进程锁定了记录 <--不确定适当的措辞。我最终不得不:SHOW PROCESSLIST
然后使用杀死IDKILL <id>
我在使用php语言构造exit的时候就发生过这种事情;在交易中间。然后这个事务“挂起”,你需要杀死 mysql 进程(上面用 processlist 描述;)
在我的例子中,我正在运行异常查询来修复数据。 如果您在查询中锁定表,则不必处理锁定超时:
LOCK TABLES `customer` WRITE;
update customer set account_import_id = 1;
UNLOCK TABLES;
对于正常使用,这可能不是一个好主意。
有关更多信息,请参阅: MySQL 8.0 参考手册
我遇到了 2 个 Doctrine DBAL 连接,其中一个是非事务性的(用于重要日志),它们旨在并行运行,而不是相互依赖。
CodeExecution(
TransactionConnectionQuery()
TransactionlessConnectionQuery()
)
在非常测试之后,我的集成测试被包装到用于数据回滚的事务中。
beginTransaction()
CodeExecution(
TransactionConnectionQuery()
TransactionlessConnectionQuery() // CONFLICT
)
rollBack()
我的解决方案是在这些测试中禁用包装事务并以另一种方式重置数据库数据。
我们昨天遇到了这个问题,经过这里几乎所有建议的解决方案,以及来自其他答案/论坛的其他几个解决方案,一旦我们意识到实际问题,我们最终解决了这个问题。
由于一些糟糕的计划,我们的数据库存储在一个安装的卷上,该卷也接收我们的定期自动备份。该卷已达到最大容量。
一旦我们清理了一些空间并重新启动,这个错误就解决了。
请注意,我们确实还手动终止了几个进程:kill <process_id>;
因此这可能仍然是必要的。
总的来说,我们的结论是,我们的日志或警告都没有直接提到磁盘空间不足,这令人难以置信,但这似乎是根本原因。
我在使用 python 访问 mysql 数据库时遇到了类似的错误。
python 程序正在使用 while 和 for 循环。
在适当的行关闭光标和链接解决了问题
https://github.com/nishishailesh/sensa_host_com/blob/master/sensa_write.py
见第 230 行
似乎询问重复链接而不关闭前一个链接会产生此错误
有同样的错误,即使我只用一个条目更新一个表,但在重新启动mysql后,它就解决了。