InnoDB 锁耗尽与 DBI 下的批处理事务 DELETE

InnoDB lock exhaustion with batched transactional DELETEs under DBI

我正在使用 perl 和 DBI 在非常大的 mysql table 上以 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();
}

根据我的经验,此错误仅在尝试使用一个语句一次删除或插入大量记录时发生。事实上,我能够找到的可行解决方案是:

  1. 使用 innodb_buffer_pool_size 全局变量增加 innodb 缓冲池大小。

  2. 分块执行删除。

我没有尝试解决方案 1。有两个原因。首先,在我的具体情况下,它只会增加缓冲区最终被填满之前的时间,尽管我对此不确定,其次,因为我们不确定它可能对使用数据库的应用程序产生什么影响。

我想知道:

*为什么我在大块删除的时候会出现这个错误?

*是否有使用 perl and/or DBI 快速解决此问题的高级解决方案?

*任何其他可能导致问题的信息。

为什么即使我正在大块删除也会出现此错误?

InnoDB 使用 row-level locking:

14.5.8 Locks Set by Different SQL Statements in InnoDB

A locking read, an UPDATE, or a DELETE generally set record locks on every index record that is scanned in the processing of the SQL statement. It does not matter whether there are WHERE conditions in the statement that would exclude the row. InnoDB does not remember the exact WHERE condition, but only knows which index ranges were scanned. The locks are normally next-key locks that also block inserts into the “gap” immediately before the record.

[...]

DELETE FROM ... WHERE ... sets an exclusive next-key lock on every record the search encounters.

(强调)

这意味着您的查询将锁定它扫描的每一行,即使是与您的 WHERE 子句中的条件不匹配的行。

我不知道你的查询的确切执行细节,但我想如果有一个大的 table,不难超过 [=19= 的 default 128 MB ](我相信这是所有会话共享的;其他会话可能会在查询的同时锁定行)。特别是如果您的查询不使用索引并触发 table 扫描。

是否有针对此问题的快速高级解决方案?

MySQL manual 描述了针对这种情况的简单解决方法:

If you are deleting many rows from a large table, you may exceed the lock table size for an InnoDB table. To avoid this problem, or simply to minimize the time that the table remains locked, the following strategy (which does not use DELETE at all) might be helpful:

  1. Select the rows not to be deleted into an empty table that has the same structure as the original table:
INSERT INTO t_copy SELECT * FROM t WHERE ... ;
  1. Use RENAME TABLE to atomically move the original table out of the way and rename the copy to the original name:
RENAME TABLE t TO t_old, t_copy TO t;
  1. Drop the original table:
DROP TABLE t_old;

No other sessions can access the tables involved while RENAME TABLE executes, so the rename operation is not subject to concurrency problems. See Section 13.1.20, “RENAME TABLE Syntax”.

  1. INDEX(date)
  2. dateDATETIMEDATETIMESTAMP
  3. 类型
  4. 这样查询:

    从 table 中删除 WHERE date > '2015-01-31' 按 date 描述排序 限制 1000

  5. 当 DELETE 有 rows_affected == 0

  6. 时停止