【问题标题】:mysql - Deleting Rows from InnoDB is very slowmysql - 从 InnoDB 中删除行非常慢
【发布时间】:2014-04-01 11:03:50
【问题描述】:

我有一个大约 mysql 数据库。 1 TB 数据。表fuelinjection_stroke 有apprx。 1.000.000.000 行。 DBID 是主键,每次插入都会自动加一。

我正在尝试使用一个非常简单的语句删除前 1.000.000 行:

Delete from fuelinjection_stroke where DBID < 1000000;

在我的专用 8 核 Xeon 服务器(32 GB 内存,SAS 存储)上,此查询需要很长时间(>24 小时)。

知道这个过程是否可以加快速度吗?

【问题讨论】:

  • 10 亿行是相当多的。除了 pkey 之外,您的桌子上有任何索引吗?当您删除行时,必须更新索引,这可能需要时间。我不确定它在这种情况下是否有效,但您能否尝试在事务中进行删除,看看效果是否更好?
  • 对于 OP,您能否向我们介绍一下结果,我真的很感兴趣。 :)
  • 删除速度很慢。以 10.000 的部分删除的方法有效 - 但这并没有加快整个过程。我最终做了以下事情:我使用 mysqldump 将表转储到一个文件中。然后我使用 sed -i '1,1000000d' file.sql 从转储文件中删除行。我截断了表格并重新加载了转储文件……结果证明这是最快的方法……
  • OP 在对 Uriil 的回答的评论中提到“表上没有任何索引或外键”。显然,如果您的 WHERE 条件使用的是没有索引的列,那么 MySQL 必须扫描整个 TABLE 以确保它具有 DBID &lt; 1000000 的所有行。而如果DBID 上有一个索引,MySQL 就不必这样做。
  • 在这种情况下会删除多少行?如果超过 30% 的行,那么最好将剩余的 70% 复制到新表中,然后将新表重命名为原始表,并将原始表重命名为旧表,然后删除旧表。

标签: mysql sql innodb


【解决方案1】:

我相信您的桌子已被锁定。我遇到了同样的问题,发现可以很快删除 10k 条记录。因此,您可能想编写简单的脚本/程序来逐块删除记录。

   DELETE FROM fuelinjection_stroke WHERE DBID < 1000000 LIMIT 10000;

并继续执行它,直到它删除所有内容

【讨论】:

  • 正在进行的调查指出了两个方向:1)由于桌子很大,所以需要很长时间。 2)mysql实例是主从复制的主实例。尽管表上没有任何索引或外键,但两台服务器之间的 binlog 复制似乎减慢了该过程。此处建议的解决方案有效,因为单个语句不会结束。
  • 我认为这个解决方案之所以如此有效,是因为事务的大小不会变得太大,因此 MySQL 可以将其放入内存中。否则它必须将所有内容都写入磁盘,这很慢。
  • 我有更好的结果,限制为 750。删除触发器和外键检查也大大提高了性能(因为我正在为测试数据库这样做)。
【解决方案2】:

你的空间被剥夺了吗?停机时间是不可能的吗?

如果没有,您可以放入长度为 1 的新 INT 列,并将其默认为 1 表示“活动”(或任何您的术语),0 表示“非活动”。实际上,如果需要,您可以将 0 到 9 用作 10 个不同的状态。

添加这个新列需要很长时间,但是一旦结束,只要您在 PRIMARY 之外执行更新(就像您对 DELETE 所做的那样)并且您不为这个新列编制索引,您的 UPDATE 应该会很快.

InnoDB 需要这么长时间才能删除像您这样庞大的表的原因是因为集群索引。它根据您的 PRIMARY(或它找到的第一个 UNIQUE ......或者如果它找不到 PRIMARY 或 UNIQUE 的任何感觉)对您的表进行物理排序,因此当您拉出一行时,它现在重新排序您的整个表磁盘的速度和碎片整理。所以不是 DELETE 需要这么长时间。这是删除该行后的物理重新排序。

当您使用默认值创建一个新的 INT 列时,空间将被填充,因此当您更新它时,无需在您的大表中进行物理重新排序。

我不确定您的架构到底是什么,但使用列表示行的状态比删除要快得多;但是,它会占用更多空间。

尝试设置值:

innodb_flush_log_at_trx_commit=2
innodb_flush_method=O_DIRECT (for non-windows machine)
innodb_buffer_pool_size=25GB (currently it is close to 21GB)
innodb_doublewrite=0
innodb_support_xa=0
innodb_thread_concurrency=0...1000 (try different values, beginning with 200)

参考资料:

MySQL docs for description of different variables.

MySQL Server Setting Tuning

MySQL Performance Optimization basics

http://bugs.mysql.com/bug.php?id=28382

【讨论】:

  • 我正在尝试释放数据,因为我的 sas san 接近 100% - 只有 4TB。
  • 基于此,我不明白为什么删除的行越少,删除的速度就越快。当您删除 N 行(在单个 DELETE 中)时,肯定不会重新组织表 N 次吗?因为我刚刚按照您的建议添加了MarkedForDeletion 列,但更进一步并添加了一个异步操作,该操作将(逐渐)物理删除这些小批量(正如公认的答案所暗示的那样,正如我在其他地方听到的那样) ...而且效果很好!
【解决方案3】:

你有什么索引?

我认为您的问题是删除在每次迭代时都会重建索引。

如果有的话,我会删除索引,删除,然后重新添加索引。它会快得多,(我认为)。

【讨论】:

  • 同时禁用对该表的外键引用(如果有)。
【解决方案4】:

我遇到了同样的问题,我的表有几个我不想删除和重新创建的索引。所以我做了以下事情:

create table keepers
select * from origTable where {clause to retrieve rows to preserve};
truncate table origTable;
insert into origTable null,keepers.col2,...keepers.col(last) from keepers;
drop table keepers;

大约 3 分钟内处理了大约 220 万行。

【讨论】:

    【解决方案5】:

    您的数据库可能正在检查需要在外键中修改的记录(级联、删除)。

    但是 I-Conica 的回答是一个好点(+1)。在完成 100000 次期间删除单个记录并更新大量索引的过程是低效的。只需删除索引,删除所有记录并再次创建它。

    当然,还要检查数据库中是否存在任何类型的锁。一个用户或应用程序可以锁定一条记录或表,您的查询将一直等待,直到用户释放资源或达到超时。检查您的数据库是在做实际工作还是只是在等待的一种方法是从将 --innodb_lock_wait_timeout 参数设置为几秒钟的连接中启动查询。如果它失败了,至少你知道查询没问题,你需要找到并释放那个锁。锁的例子是 Select * from XXX For update 和 uncommited transactions。

    【讨论】:

    • 我不太确定我是否理解这个答案。我的表没有任何索引。只有一个主键 DBID(在插入期间自动递增)。我的理解是,如果没有至少一个唯一索引,innodb 就无法工作,如果没有明确分配,它将隐式创建:“如果表没有 PRIMARY KEY 或合适的 UNIQUE 索引,InnoDB 在内部生成一个合成列上的隐藏聚集索引包含行 ID 值。行按 InnoDB 分配给此类表中的行的 ID 排序。"
    • 如果你只有一个主键索引,我认为问题不在于索引。请检查没有外键关系。 (例如,如果您每次删除一条记录时都有一个fuelinjection_stroke_history,您将不得不检查该辅助表中的孤儿。那将是1000.000 次查询)查询完成了吗?如果不是,我会假设它是一个锁。
    • 已复制数据库(主/从) - 这会减慢进程(二进制日志)。该表没有任何外键等。 ...
    【解决方案6】:

    对于这么长的表,我宁愿使用MYISAM,特别是在不需要大量事务的情况下。

    【讨论】:

      【解决方案7】:

      我不知道你的确切答案。但是写另一种方法来删除这些行,请试试这个。

      delete from fuelinjection_stroke where DBID in
      (
          select top 1000000 DBID  from fuelinjection_stroke 
          order by DBID asc
      )
      

      【讨论】:

        猜你喜欢
        • 2013-07-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多