我正在使用perl和DBI在一个非常大的mysql表上以1000块的形式执行删除。但是我收到了这个错误:DBD::mysql::db do failed: The total number of locks exceeds the lock table size
。
以下是带有执行删除的sql语句的perl代码
my $q = q{
DELETE FROM table
WHERE date_format(date, '%Y-%m') > '2015-01' LIMIT 1000
};
my $rc = '';
until ($rc eq '0E0') {
$rc = $dbh->do($q);
$dbh->commit();
}
根据我的经验,只有在尝试使用一个语句一次删除或插入大量记录时才会出现此错误。事实上,我能找到的可行解决方案是:
使用innodb_buffer_pool_size
全局变量增加innodb缓冲池大小。
以块的形式执行删除。
我没有尝试解决方案1.有两个原因。首先,它似乎在我的特定情况下,它只会增加缓冲区最终填充之前的时间,虽然我不确定,第二,因为我们不确定它可能对使用数据库的应用程序产生什么影响。 / p>
我想知道:
*即使我正在以块的形式删除,为什么会出现此错误?
* perl和/或DBI是否有针对此问题的快速高级解决方案?
*任何其他可能导致消息的信息。
答案 0 :(得分:2)
InnoDB使用row-level locking:
14.5.8由InnoDB中的不同SQL语句设置的锁
锁定读取,
UPDATE
或DELETE
通常设置记录锁定在处理SQL语句时扫描的每个索引记录。它不会是否在语句中有WHERE
条件排除该行。 InnoDB不记得确切的WHERE
条件,但只知道扫描了哪个索引范围。锁通常是下一键锁,它也会在记录之前阻止插入“间隙”。[...]
DELETE FROM ... WHERE ...
在搜索遇到的每条记录上设置一个独占的下一键锁定。
(强调补充)
这意味着您的查询将锁定它扫描的每一行,甚至是与WHERE
子句中的条件不匹配的行。
我不知道您的查询的确切执行细节,但我想用一个大表,不会超出innodb_buffer_pool_size
的{{3}}(我相信是由所有会话共享;其他会话可以与查询同时锁定行)。特别是如果您的查询不使用索引并触发表扫描。
default 128 MB描述了针对这种情况的简单解决方法:
如果要从大表中删除多行,则可能会超出InnoDB表的锁表大小。要避免此问题,或者只是为了最小化表保持锁定的时间,以下策略(根本不使用
DELETE
)可能会有所帮助:
- 选择不要删除的行到与原始表具有相同结构的空表中:
醇>
INSERT INTO t_copy SELECT * FROM t WHERE ... ;
- 使用
醇>RENAME TABLE
以原子方式移动原始表格并将副本重命名为原始名称:
RENAME TABLE t TO t_old, t_copy TO t;
- 删除原始表:
醇>
DROP TABLE t_old;
执行
RENAME TABLE
时,没有其他会话可以访问所涉及的表,因此重命名操作不会受到并发问题的影响。请参见第13.1.20节“RENAME TABLE
语法”。
答案 1 :(得分:0)
INDEX(date)
date
的类型为DATETIME
或DATE
或TIMESTAMP
以这种方式执行查询:
DELETE FROM table
在哪里date
> “2015年1月31日”
ORDER BY date
DESC
限制1000
当DELETE具有rows_affected == 0