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

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 例如,在使用加入的删除中),但是在这种情况下,您可以这样做。

使用还有其他危险 LIMITDELETE 关于复制;删除的行有时不会按照从主机上删除的从属上的顺序删除。

其他提示

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

步骤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)。希望这有所帮助。

许可以下: CC-BY-SA归因
不隶属于 dba.stackexchange
scroll top