带有索引的大型 MySQL 表的 DELETE 性能受到影响

Ryu*_*yuK 5 php mysql sql database indexing

假设我们有一个带有 MySQL 5.6 数据库的 Web 论坛应用程序,许多用户可以 24/7 全天候访问该应用程序。现在有一个这样的表,用于发送给用户的通知的元数据。

| notifications | CREATE TABLE `notifications` (
 `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
 `user_id` bigint(20) unsigned NOT NULL,
 `message_store_id` bigint(20) unsigned NOT NULL,
 `status` varchar(10) COLLATE ascii_bin NOT NULL,
 `sent_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (`id`),
 KEY `user_id` (`user_id`,`sent_date`)
) ENGINE=InnoDB AUTO_INCREMENT=736601 DEFAULT CHARSET=ascii COLLATE=ascii_bin |
Run Code Online (Sandbox Code Playgroud)

该表有 100 万行。有了这个表,某个 message_store_id 由于某种原因突然变得无效,我计划使用单个删除语句删除所有具有该 message_store_id 的记录,例如

DELETE FROM notifications WHERE message_store_id = 12345;
Run Code Online (Sandbox Code Playgroud)

由于这条消息被发送给了如此多的用户,因此这一条语句影响了表的 10%。同时,成千上万的用户一直在访问此通知表,因此必须存在索引。显然,删除记录时重新创建索引的成本非常高,所以我担心这样做会导致服务器资源最大化而导致停机。但是,如果我删除索引,删除记录然后再次添加索引,我必须关闭数据库一段时间,不幸的是我们的服务无法使用。

我希望 MySQL 5.6 不会愚蠢到这个单一的语句可以杀死数据库,但我想它很有可能。我的问题是,对于这种情况,索引重建真的是致命的吗?如果是这样,此操作是否有任何不需要我停止数据库进行维护的好策略?

t7k*_*7ko 4

根据您的应用程序的详细信息,您可以采用很多技巧/策略。

  1. 如果您计划定期执行这些操作(例如,这不是一次性的事情),并且 中几乎没有不同的值message_store_id,则可以使用分区。按 的值进行分区message_store_id,预先创建 X 个分区(其中 X 是 id 值数量的合理上限),然后您可以通过截断该分区来立即删除该分区中的所有记录。几毫秒的事。缺点:message_store_id必须是主键的一部分。注意:您必须事先创建分区,因为上次我使用它们时,alter table add partition重新创建了整个表,这对于大型表来说是一场灾难。
  2. 即使这alter table truncate partition对您不起作用,您仍然可以从分区中受益。如果您DELETE在分区上发出 a,通过提供相应的where条件,表的其余部分将不会受到此 DELETE 操作的影响/锁定。
  3. 删除记录而不锁定数据库太长时间的替代方法:

    while (true) {
      // assuming autocommit mode
      delete from table where {your condition} limit 10000;
      // at this moment locks are released and other transactions have a chance
      // to do some stuff.
      if (affected rows == 0) {
        break;
      }
      // This is a good place to insert sleep(5) to give other transactions
      // more time to do their stuff before the next chunk gets deleted.
    }
    
    Run Code Online (Sandbox Code Playgroud)