如何优化在100,000多条记录上运行的'col = col + 1'UPDATE查询?

时间:2010-09-06 04:16:35

标签: mysql optimization

有关背景信息,请参阅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似乎正在立即将所有内容写回磁盘。

任何建议都表示赞赏。

3 个答案:

答案 0 :(得分:4)

我从未使用它,但如果你有足够的记忆,请尝试memory table

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

答案 1 :(得分: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索引而不是默认的hash来找到一些性能:

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

答案 2 :(得分:1)

您正在更新索引列 - 索引会产生负面影响(读取:减速)INSERT / UPDATE。

如果这是一次需要纠正错误:

  1. 删除/删除正在更新的列上的索引(lft,rght)
  2. 运行更新语句
  3. 重新创建索引(这可能需要时间,可能相当于您已经体验过的总数)