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();
}
根据我的经验,此错误仅在尝试使用一个语句一次删除或插入大量记录时发生。事实上,我能够找到的可行解决方案是:
使用 innodb_buffer_pool_size
全局变量增加 innodb 缓冲池大小。
分块执行删除。
我没有尝试解决方案 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:
- 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 ... ;
- 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;
- 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”.
- 有
INDEX(date)
date
是 DATETIME
或 DATE
或 TIMESTAMP
类型
这样查询:
从 table 中删除
WHERE date
> '2015-01-31'
按 date
描述排序
限制 1000
当 DELETE 有 rows_affected == 0
时停止
我正在使用 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();
}
根据我的经验,此错误仅在尝试使用一个语句一次删除或插入大量记录时发生。事实上,我能够找到的可行解决方案是:
使用
innodb_buffer_pool_size
全局变量增加 innodb 缓冲池大小。分块执行删除。
我没有尝试解决方案 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 aDELETE
generally set record locks on every index record that is scanned in the processing of the SQL statement. It does not matter whether there areWHERE
conditions in the statement that would exclude the row. InnoDB does not remember the exactWHERE
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:
- 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 ... ;
- 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;
- 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”.
- 有
INDEX(date)
date
是DATETIME
或DATE
或TIMESTAMP
类型
这样查询:
从 table 中删除 WHERE
date
> '2015-01-31' 按date
描述排序 限制 1000当 DELETE 有 rows_affected == 0
时停止