如何提高InnoDB DELETE性能?


9

因此,我有此审核表(跟踪数据库中任何表上的操作):

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)

Answers:


11

您可以批量删除数据。

在SQL Server中,语法是delete top X表中的行。然后,您可以循环执行该操作,并为每个批次进行事务处理(当然,如果您有多个语句),因此可以使事务处理简短并仅在短期内保持锁定。

在MySQL语法中: DELETE FROM userTable LIMIT 1000

对此有一定的限制(LIMIT例如,不能在带有联接的删除中使用),但是在这种情况下,您可能可以这样做。

复制时,使用LIMITwith 还有一个危险DELETE。有时删除的行在从属服务器上的删除顺序与在主服务器上删除的顺序不同。


6

尝试使用临时表方法。尝试这样的事情:

步骤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);

第三步 ALTER TABLE track_table RENAME track_table_old;

第四步) ALTER TABLE track_table_new RENAME track_table;

步骤5) DROP TABLE track_table_old;

我没有在步骤2中包含元组字段。请查看是否会产生所需的效果。如果这是您想要的,则除非您出于其他原因使用元组字段,否则可能希望完全放弃元组字段。


这是一个有趣的解决方案。我确实需要表格中的元组字段。tableName / tupleID是正在记录的表的未定义外键。未定义,因为直到最近,该表还是MyISAM,它不支持外键。
德里克·唐尼

1

批量删除不需要的行将使其他操作可行。但是删除操作具有条件,因此请确保条件上的列具有适当的索引。

由于MySQL不支持松散索引扫描的功能齐全,您可以尝试调整顺序KEY actionDate (action, date_insert)KEY actionDate (date_insert, action)。MySQL的前缀为“ date_insert”,则应使用该索引扫描日期时间条件之前的行。

有了这样的索引,您可以将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

1
| 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 |

-拳头,从您的解释key_len很大=>您需要降低大小尽可能小。对于您的查询,我认为最好的方法是将操作字段的数据类型从char(12)更改为tinyint,因此数据映射如下所示:

1: -> DELETE
2: -> UPDATE
3: -> INSERT
...

您也可以更改table_id而不是tablename。最佳性能的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万行。并每小时更新一次(每次更新40万行),我可以删除curr_date分区并将数据重新加载到表中。drop命令非常快(<100ms)。希望能有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.