如何改善InnoDB删除性能?
-
16-10-2019 - |
题
因此,我有此审核表(在数据库中的任何表上跟踪操作):
CREATE TABLE `track_table` (
`id` int(16) unsigned NOT NULL,
`userID` smallint(16) unsigned NOT NULL,
`tableName` varchar(255) NOT NULL DEFAULT '',
`tupleID` int(16) unsigned NOT NULL,
`date_insert` datetime NOT NULL,
`action` char(12) NOT NULL DEFAULT '',
`className` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `userID` (`userID`),
KEY `tableID` (`tableName`,`tupleID`,`date_insert`),
KEY `actionDate` (`action`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
我需要开始存档过时的项目。该桌子已经增长到约5000万行,因此我可以删除行的最快方法是一次将其删除一个表(基于 tableName
).
这效果很好,但是在一些较重的桌子上,它无法完成。我的查询删除所有具有关联的项目 delete
在tupleid/tablename组合上的作用:
DELETE FROM track_table WHERE tableName='someTable' AND tupleID IN (
SELECT DISTINCT tupleID FROM track_table
WHERE tableName='someTable' AND action='DELETE' AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
)
我让它在服务器上运行3天,并且从未完成最大的桌子。解释输出(如果我将删除切换为选择:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | PRIMARY | track_table | ref | tableID | tableID | 257 | const | 3941832 | Using where |
| 2 | DEPENDENT SUBQUERY | track_table | ref | tableID,actionDate | tableID | 261 | const,func | 1 | Using where; Using temporary |
因此,我认为400万行不应需要3天才能删除。我将我的Innodb_buffer_pool_size设置为3GB,并且服务器未设置为使用one_file_per_table。我还能改善InnoDB删除性能的其他方法? (在Mac OSX上运行MySQL 5.1.43)
解决方案
您可以分批删除数据。
在SQL Server中,语法为 delete top X
从表中排。然后,您可以循环进行,每批交易(当然有一个以上的语句),以便保持交易短并仅在短期内保持锁。
在MySQL语法中: DELETE FROM userTable LIMIT 1000
有限制(不能使用 LIMIT
例如,在使用加入的删除中),但是在这种情况下,您可以这样做。
使用还有其他危险 LIMIT
和 DELETE
关于复制;删除的行有时不会按照从主机上删除的从属上的顺序删除。
其他提示
尝试使用临时表方法。尝试这样的事情:
步骤1) CREATE TABLE track_table_new LIKE track_table;
第2步) INSERT INTO track_table_new SELECT * FROM track_table WHERE action='DELETE' AND date_insert >= DATE_SUB(CURDATE(), INTERVAL 30 day);
步骤3) ALTER TABLE track_table RENAME track_table_old;
第4步) ALTER TABLE track_table_new RENAME track_table;
步骤5) DROP TABLE track_table_old;
我没有在步骤2中包含元组字段。请查看是否会产生所需的效果。如果这是您想要的,则可能需要完全放弃元组田地,除非您出于其他原因使用元组。
批量删除不需要的行应保持其他操作可行。但是您的操作删除有条件,因此请确保条件上的列有适当的索引。
因为MySQL不支持松散索引扫描的完整函数,因此您可以尝试调整序列 KEY actionDate (action, date_insert)
至 KEY actionDate (date_insert, action)
. 。在“ date_insert”的前缀中,MySQL应该使用此索引来扫描DateTime条件之前的行。
使用此类索引,您可以将SQL写为:
DELETE
FROM track_table
WHERE tableName='someTable'
AND action='DELETE'
AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
LIMIT 1000 -- Your size of batch
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | PRIMARY | track_table | ref | tableID | tableID | 257 | const | 3941832 | Using where |
| 2 | DEPENDENT SUBQUERY | track_table | ref | tableID,actionDate | tableID | 261 | const,func | 1 | Using where; Using temporary |
- fist,从您的解释中,key_len如此之大=>您需要降低尽可能小的大小。对于您的查询,我认为最好的方法是将数据类型的数据类型从char(12)更改为tinyint,因此数据映射看起来像:
1: -> DELETE
2: -> UPDATE
3: -> INSERT
...
您也可以更改表_id而不是表格。最佳性能的DDL可以:
CREATE TABLE `track_table` (
`id` int(11) unsigned NOT NULL,
`userID` smallint(6) unsigned NOT NULL,
`tableid` smallint(6) UNSIGNED NOT NULL DEFAULT 0,
`tupleID` int(11) unsigned NOT NULL,
`date_insert` datetime NOT NULL,
`actionid` tinyin(4) UNSIGNED NOT NULL DEFAULT 0,
`className` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `userID` (`userID`),
KEY `tableID` (`tableid`,`tupleID`,`date_insert`),
KEY `actionDate` (`actionid`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `actions` (
`id` tinyint(4) unsigned NOT NULL
`actionname` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `table_name` (
`id` tinyint(4) unsigned NOT NULL
`tablename` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
因此查询可以运行看起来像:
DELETE FROM track_table WHERE tableid=@tblid AND tupleID IN (
SELECT DISTINCT tupleID FROM track_table
WHERE tableid=@tblid AND actionid=@actionid AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
).
但是最快的方法是使用分区。因此,您可以放下分区。目前,我的桌子大约有4000万行。并每小时更新(每次400k行更新),我可以将Curr_date分区和重新加载数据放入表中。 Drop命令非常快(<100ms)。希望这有所帮助。