发生此错误的方式有很多种。我将尝试列出一两个,也许这个类比对于在某些时候阅读本文的人来说是正确的。
在较大的数据集上,即使将 innodb_buffer_pool_size 更改为较大的值,当没有足够的索引来隔离 where 子句中的行时,您也可能会遇到此错误。或者在某些情况下使用主索引(参见 this)和 Roger Gammans 的评论:
来自(innodb 的 5.0 文档):-
如果您没有适合您的语句的索引并且 MySQL 必须扫描
整个表处理语句,表的每一行
被锁定,从而阻止其他用户对
桌子。创建良好的索引很重要,这样您的查询才能做到
不会不必要地扫描很多行。
使用这个简单的架构可以直观地了解此错误是如何发生和难以解决的:
CREATE TABLE `students` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`thing` int(11) NOT NULL,
`campusId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ix_stu_cam` (`camId`)
) ENGINE=InnoDB;
一个有 5000 万行的表。 FK 没有显示,不是问题。该表最初用于显示查询性能也不重要。然而,在以 1M 行的块初始化 thing=id 时,我必须在块更新期间执行限制以防止出现其他问题,方法是:
update students
set thing=id
where thing!=id
order by id desc
limit 1000000 ; -- 1 Million
这一切都很好,直到它说还有 600000 需要更新,如所见
select count(*) from students where thing!=id;
我为什么这样做count(*) 源于重复
错误1206:锁总数超过锁表大小
我可以继续降低上述更新中显示的 LIMIT,但最后我会留下 1200 != 的计数,而问题仍然存在。
为什么会继续?因为系统在扫描这个大表时填满了锁表。当然,在我看来,它可能“内部隐式事务”已将最后 1200 行更改为相等,但由于锁表已填满,实际上会在没有设置任何内容的情况下中止事务。而且这个过程会陷入僵局。
插图 2:
在此示例中,假设我有 288 行 5000 万行表可以更新如上所示。由于所描述的最终游戏问题,我经常会在运行此查询两次时发现问题:
update students set thing=id where thing!=id order by id desc limit 200 ;
但我不会有这些问题:
update students set thing=id where thing!=id order by id desc limit 200;
update students set thing=id where thing!=id order by id desc limit 88 ;
解决方案
有很多方法可以解决这个问题,包括但不限于:
A.在表明数据已更新的列上创建另一个索引,可能是boolean。并将其合并到where 子句中。然而在大表上,创建一些临时索引可能是不可能的。
B.使用尚未清理的id's 填充第二张桌子可能是另一种解决方案。再加上和update with a join模式。
C.动态更改 LIMIT 值,以免导致锁表溢出。当根本没有更多行要更新或删除(您的操作)时,可能会发生溢出,未达到 LIMIT,并且锁定表在无结果的扫描中填满了根本不存在的更多行(参见上面的 插图2)。
这个答案的主要目的是让人们了解它为什么会发生。并且让任何读者都能制定出适合他们需求的最终游戏解决方案(与有时对系统变量、重新启动和祈祷的徒劳改变相比)。