有关背景信息,请参阅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似乎正在立即将所有内容写回磁盘。
任何建议都表示赞赏。
答案 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。
如果这是一次需要纠正错误: