发送 DELETE 查询时,innoDB 是从数据库中逐条删除数据,还是首先创建要删除的数据列表?
When sending a DELETE query, does innoDB delete data from database one-by-one, or first create a list of data to be deleted?
我有一个包含约 140,000 行的数据库 table,现在我正在执行一个类似
的查询
DELETE FROM database WHERE type="delete"
但这需要很长时间。我很想停止查询,看看数据库的大小是否真的缩小了,但我不确定它是否正在创建要删除的数据列表,然后才会实际删除数据。 innoDB 如何在收到 DELETE 查询时实际清除数据库中的数据?
这是 CREATE 查询(针对 table images
):
CREATE TABLE IF NOT EXISTS `images` (
`imageID` int(11) NOT NULL AUTO_INCREMENT,
`runID_fk` int(11) DEFAULT NULL,
`sequenceID_fk` int(11) DEFAULT NULL,
`cameraID_fk` int(11) DEFAULT NULL,
`data` longblob,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`name` text,
`type` text,
`pcadata` longblob,
PRIMARY KEY (`imageID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
根据您的 CREATE
声明,您在 type
列上没有索引。因此,您的问题是由于 WHERE
子句缓慢造成的。基本上,您的查询会强制数据库服务器查看每一行,以查看 type
列中的值是否为 "delete"
。这很慢,不管你有多少行。索引会使这变得更快,因为服务器基本上可以只请求所有匹配行的列表,然后只对这些行进行操作。
作为一个非常广泛的经验法则,如果您打算在 WHERE
子句中使用一个列,则应该对其进行索引。 (当然有例外,但根据您发布的内容,这不是其中之一。)
您可以通过 运行 一个 EXPLAIN
查询查看数据库服务器正在做什么,如
EXPLAIN DELETE FROM database WHERE type="delete"
根据您的评论,您的 EXPLAIN
returns 是这样的:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE images ALL null null null null 145669 Using where
您看到的 145,699 不是(如您在评论中所写)"the size of the rows."它是服务器执行查询时必须查看的行数。如您所见,它没有任何可以使用的索引。因此,它不仅要查看要删除的行,还必须查看 145,669 行。
此外,由于您的 type
列是 TEXT
列,这会更慢。如果您打算以这种方式标记要删除的行,您应该认真考虑添加 TINYINT
、CHAR
(而不是 TEXT
)或其他列来存储行的状态。
更笼统地说,您确实应该重新考虑其中一些列类型。例如,我严重怀疑名为 name
的列是否需要 2^16 - 1
字节。
INDEX
已经提到了。
TEXT
列(其中有 4 列)可能存储在其他地方,需要额外的磁盘命中。 (已经提到)。
- InnoDB 构建了一个列表,列出了在发生崩溃时要撤消的事情;这可能是最大的成本。
- 分块删除不超过 1000 行是一个更好的主意。遍历
PRIMARY KEY
找到区块。
- 如果这是一项重复性任务,还有其他技巧。
- 如果删除超过 table 的一半,则:创建新的 table;
INSERT SELECT
复制守门员; RENAME TABLE
交换; DROP
.
有关最后三个建议的详细信息,请参阅 this。
我有一个包含约 140,000 行的数据库 table,现在我正在执行一个类似
的查询DELETE FROM database WHERE type="delete"
但这需要很长时间。我很想停止查询,看看数据库的大小是否真的缩小了,但我不确定它是否正在创建要删除的数据列表,然后才会实际删除数据。 innoDB 如何在收到 DELETE 查询时实际清除数据库中的数据?
这是 CREATE 查询(针对 table images
):
CREATE TABLE IF NOT EXISTS `images` (
`imageID` int(11) NOT NULL AUTO_INCREMENT,
`runID_fk` int(11) DEFAULT NULL,
`sequenceID_fk` int(11) DEFAULT NULL,
`cameraID_fk` int(11) DEFAULT NULL,
`data` longblob,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`name` text,
`type` text,
`pcadata` longblob,
PRIMARY KEY (`imageID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
根据您的 CREATE
声明,您在 type
列上没有索引。因此,您的问题是由于 WHERE
子句缓慢造成的。基本上,您的查询会强制数据库服务器查看每一行,以查看 type
列中的值是否为 "delete"
。这很慢,不管你有多少行。索引会使这变得更快,因为服务器基本上可以只请求所有匹配行的列表,然后只对这些行进行操作。
作为一个非常广泛的经验法则,如果您打算在 WHERE
子句中使用一个列,则应该对其进行索引。 (当然有例外,但根据您发布的内容,这不是其中之一。)
您可以通过 运行 一个 EXPLAIN
查询查看数据库服务器正在做什么,如
EXPLAIN DELETE FROM database WHERE type="delete"
根据您的评论,您的 EXPLAIN
returns 是这样的:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE images ALL null null null null 145669 Using where
您看到的 145,699 不是(如您在评论中所写)"the size of the rows."它是服务器执行查询时必须查看的行数。如您所见,它没有任何可以使用的索引。因此,它不仅要查看要删除的行,还必须查看 145,669 行。
此外,由于您的 type
列是 TEXT
列,这会更慢。如果您打算以这种方式标记要删除的行,您应该认真考虑添加 TINYINT
、CHAR
(而不是 TEXT
)或其他列来存储行的状态。
更笼统地说,您确实应该重新考虑其中一些列类型。例如,我严重怀疑名为 name
的列是否需要 2^16 - 1
字节。
INDEX
已经提到了。TEXT
列(其中有 4 列)可能存储在其他地方,需要额外的磁盘命中。 (已经提到)。- InnoDB 构建了一个列表,列出了在发生崩溃时要撤消的事情;这可能是最大的成本。
- 分块删除不超过 1000 行是一个更好的主意。遍历
PRIMARY KEY
找到区块。 - 如果这是一项重复性任务,还有其他技巧。
- 如果删除超过 table 的一半,则:创建新的 table;
INSERT SELECT
复制守门员;RENAME TABLE
交换;DROP
.
有关最后三个建议的详细信息,请参阅 this。