【问题标题】:How to optimize a 'col = col + 1' UPDATE query that runs on 100,000+ records?如何优化在 100,000+ 条记录上运行的 'col = col + 1' UPDATE 查询?
【发布时间】:2010-09-06 04:16:34
【问题描述】:

有关背景信息,请参阅this previous question。我正在尝试使用 SQL 重新编号损坏的 MPTT 树。该脚本在逻辑上运行良好,只是太慢了。

我反复需要执行这两个查询:

UPDATE `tree`
SET    `rght` = `rght` + 2
WHERE  `rght` > currentLeft;

UPDATE `tree`
SET    `lft` = `lft` + 2
WHERE  `lft` > currentLeft;

表是这样定义的:

CREATE TABLE `tree` (

  `id`        char(36) NOT NULL DEFAULT '',
  `parent_id` char(36) DEFAULT NULL,
  `lft`       int(11) unsigned DEFAULT NULL,
  `rght`      int(11) unsigned DEFAULT NULL,
  ... (a couple of more columns) ...,

  PRIMARY KEY (`id`),
  KEY `parent_id` (`parent_id`),
  KEY `lft` (`lft`),
  KEY `rght` (`rght`),
  ... (a few more indexes) ...

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

数据库是 MySQL 5.1.37。表中目前有约 120,000 条记录。两个UPDATE 查询中的每一个都需要大约 15 - 20 秒来执行。 WHERE 条件可能适用于大多数记录,因此几乎所有记录每次都需要更新。在最坏的情况下,两个查询的执行次数与数据库中的记录一样多。

有没有办法通过将值保存在内存中、延迟写入磁盘、延迟索引更新或类似的方式来优化此查询?现在的瓶颈似乎是硬盘吞吐量,因为 MySQL 似乎正在立即将所有内容写回磁盘。

任何建议表示赞赏。

【问题讨论】:

  • 一些想法:降低/消除 ACID 隔离级别。删除索引并在完成后添加,锁定表。将一大块更新包装到一个事务中。转换为 MyISAM 然后再转换回来。
  • @Rob 如果您能扩展您的回答,特别是降低隔离级别的具体步骤,我会很高兴。 :)

标签: mysql optimization


【解决方案1】:

我没用过,但如果你有足够的内存,试试memory table

创建一个与树结构相同的表,插入到 .. select from ..,针对内存表运行脚本,然后将其写回。

【讨论】:

  • 这看起来确实是最好的选择。我目前正在试验它...有人知道任何陷阱吗?
  • 谢谢,就像一个魅力。切换到 MEMORY 表并选择正确的索引将运行时间从几天缩短到大约 10 分钟。有关完整的最终脚本,请参阅 here
【解决方案2】:

根据要求扩展评论中的一些想法:

默认是在每次提交后刷新到磁盘。您可以在一次提交中包装多个更新或更改此参数:

http://dev.mysql.com/doc/refman/5.1/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit

隔离级别很容易更改。只要确保水平适合您的设计。这可能无济于事,因为正在使用范围更新。在寻找更多并发性时很高兴知道:

http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html

最终,在注意到查询中的范围更新后,最好的选择是 andrem 指出的 MEMORY 表。此外,您可能能够通过使用 btree 索引而不是默认的哈希来找到一些性能:

http://www.mysqlperformanceblog.com/2008/02/01/performance-gotcha-of-mysql-memory-tables/

【讨论】:

  • 感谢您指出哈希/B-Tree 索引。我目前对 id/parent_id 列使用哈希,对 lft/rght 列使用 B-Tree,这似乎是最合适的。使用 MEMORY 表已经将它从每记录秒数提高到每秒记录数,让我们看看我是否可以从中获得更多性能。
  • 事实证明,混合方法实际上是最好的方法。我在操作的第一部分使用哈希索引,然后在第二部分切换到 B-Trees。这将整个例程加快了 500%。 :)
【解决方案3】:

您正在更新索引列 - 索引会对插入/更新产生负面影响(阅读:减慢)。

如果这是一次需要纠正的事情:

  1. 删除/删除正在更新的列上的索引 (lft, rght)
  2. 运行更新语句
  3. 重新创建索引(这可能需要一些时间,可能与您已经体验过的完全一样)

【讨论】:

  • 连同这些查询一起运行了许多SELECT 查询(请参阅original problem and answer)。删除索引不会对这些产生负面影响吗?
  • @deceze:是的,如果优化器正在使用索引(存在并不能确保使用),则 SELECT 可能会变慢,但事实上数据正在更改并且查询仍在运行变异数据对我来说更重要......
  • 这确实是我通常这样做的方式,但您不必删除索引本身。 ALTER TABLE 有一个 DISABLE/ENABLE KEYS 语句也可以解决问题。与必须重新创建整个索引相比,ENABLE 更省力且速度更快。
猜你喜欢
  • 1970-01-01
  • 2020-03-04
  • 2021-06-01
  • 2016-07-09
  • 2018-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多