MySQL - 删除或索引?

时间:2015-03-26 23:09:50

标签: mysql indexing innodb

我需要你的一些建议。 有一些巨大的表(innodb),有大约600kk(6亿)行。 MySQL版本是5.5。实时/生产系统中的DB。 结构示例:

CREATE TABLE `rows` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `str_id` varchar(255) NOT NULL,
  `file_id` int(11) unsigned NOT NULL,
  `upload_date` datetime NOT NULL,
  `acticity_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `MSGID_INDEX` (`str_id`),
) ENGINE=InnoDB

此时一切顺利,但我们需要从表中删除垃圾行,删除子句是:

WHERE file_id = 0 AND activity_date < (NOW() - INTERVAL 7 DAY)

表满垃圾行~50-60%(300-400kk)。所以我们需要删除很多行,逻辑方式是尝试用块删除,但问题是我们没有索引(file_id,activity_date),所以删除需要太多时间。 例如(尝试1k到100k,最佳(按时间)是100k):

DELETE from rows WHERE file_id = 0 AND activity_date < (NOW() - INTERVAL 7 DAY) LIMIT 100000;

执行~5-6分钟,这需要太长时间。 也许我们需要添加索引(file_id,activity_date)到表(我们有5.5 mysql版本)然后尝试删除,但它是生产数据库,所以添加索引可能会导致一些锁,另一种方法是继续逐步删除行? 无论如何,我们需要添加索引,但如果我们在清除数据库中的垃圾后执行此操作会更好。 有什么建议吗?

UPD

似乎是我找到满意的解决方案(我使用5k块,但不保证这个5k将从DB中删除,它是我的数据库的最佳变体,并且需要几秒钟)相对于我的任务,使用id字段就像行reducer一样。感谢您的意见!方法,简单的bash脚本:

#!/bin/bash
trap "exit" INT
COUNTER=1
LIMIT=5000
START=1300000000
while :
do
        date1=$(date +"%s")
        Q="DELETE FROM TABLE WHERE id > $(($START + $LIMIT)) AND id < $(($START + 2*$LIMIT)) AND YOUR_CLAUSE LIMIT $LIMIT;"
        mysql -D DB_NAME -uroot -p"PASS" -e "$Q"
        date2=$(date +"%s")
        diff=$(($date2-$date1))
        echo "chunk($Q) deleted -- $COUNTER, $(($diff / 60)) minutes and $(($diff % 60)) seconds elapsed."
        COUNTER=$[$COUNTER +1]
        START=$(($START + $LIMIT))
        #sleep 1
done

谢谢!

1 个答案:

答案 0 :(得分:2)

LIMIT 100000太多了。 1000就足够了。

如果idactivity_date齐头并进,那么查询应该可以正常工作,直到您到达最后一个。那时它会扫描整个桌子并且是一种麻烦。

如果它们不能齐头并进,DELETE将变得越来越慢,因为它必须跨越新的&#39;带有&#39; old&#39;的行IDS。

最好根据id对表格进行分块。我在my blog中详细介绍了这一点。

请注意,它使用LIMIT 1000,1来查找到达的距离 - 它将触摸1000行,然后删除最多 1000行。这使得努力始终受到限制。

是的,这将需要&#34;天&#34;完成。那时,你也可以重新开始!

如果您没有file_id=0测试

更好(将来)将表格设为PARTITION BY RANGE(TO_DAYS(activitydate))。那么DROP PARTITION将是即时的。我在another blog中详细介绍了详细信息,包括示例代码。

更好(也许)将转换为PARTITIONing now ,方法是复制 &#34; new&#34;数据。这个一次过程就像

  • CREATE TABLE new (...) PARTITION BY ...;
  • INSERT INTO new SELECT * FROM rows WHERE activitydate > NOW() - INTERVAL 7 DAY;
  • RENAME TABLE rows TO old, new TO rows;
  • DROP TABLE old;

这将是完成整个任务的最快方法。未来的DELETE将由DROP PARTITION完成,这是即时的。但是...... INSERT...SELECT需要很长时间,而且在发生这种情况时你不应该写rows

所以,你选择:

  • 今天和明天的分块(没有停机时间)和...;或
  • 停机一次切换到PARTITION并删除旧数据。

警告:FOREIGN KEYS和某些UNIQUE限制不适用于PARTITION