【问题标题】:error 1206 whenever trying to delete records from a table尝试从表中删除记录时出现错误 1206
【发布时间】:2013-11-24 10:48:09
【问题描述】:

我有一个包含超过 4000 万条记录的表。我想使用 sql 查询删除大约 150000 条记录:

DELETE
FROM t
WHERE date="2013-11-24"

但我收到错误 1206(锁的总数超过了锁表的大小)。 我搜索了很多并更改了缓冲池大小:

innodb_buffer_pool_size=3GB

但它没有用。 我也尝试锁定表,但也没有用:

Lock Tables t write;
DELETE
FROM t
WHERE date="2013-11-24";
unlock tables;

我知道一种解决方案是拆分删除过程,但我希望这是我的最后一个选择。 我用的是mysql服务器,服务器操作系统是centos,服务器内存是4GB。

我将不胜感激。

【问题讨论】:

  • 修改变量后是否重启了mysql?
  • DELETE桌子上有触发器吗?
  • 我改完后重启了mysql。
  • 我没有删除表。我想从没有任何约束的表“t”中删除记录。
  • 我确定您拥有庞大的数据库!删除你的老板记录或让他购买更好的服务器!

标签: mysql sql


【解决方案1】:

您可以在删除时使用Limit,并尝试一次删除 10,000 条记录的数据:

DELETE
FROM t
WHERE date="2013-11-24"
LIMIT 10000

您还可以包含 ORDER BY 子句,以便按照该子句指定的顺序删除行:

DELETE
FROM t
WHERE date="2013-11-24"
ORDER BY primary_key_column
LIMIT 10000

【讨论】:

  • 感谢您的回答。但正如我之前所说,我希望拆分删除过程是我的最后选择。我正在寻找任何解决方案,例如更改配置 mysql 服务器或服务器操作系统。
【解决方案2】:

发生此错误的方式有很多种。我将尝试列出一两个,也许这个类比对于在某些时候阅读本文的人来说是正确的。

在较大的数据集上,即使将 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)。

这个答案的主要目的是让人们了解它为什么会发生。并且让任何读者都能制定出适合他们需求的最终游戏解决方案(与有时对系统变量、重新启动和祈祷的徒劳改变相比)。

【讨论】:

    【解决方案3】:

    最简单的方法是在date 列上创建索引。我有 1.7 亿行,正在删除 650 万行。我遇到了同样的问题并通过在WHERE 子句中使用的列上创建non-clustered 索引来解决它,然后我执行了删除查询并且它起作用了。 如果您以后不需要索引,请删除它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-08
      • 2019-12-05
      • 2019-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-02
      相关资源
      最近更新 更多