为什么这个update-with-join mysql查询这么慢?

时间:2013-09-18 11:37:22

标签: mysql sql performance join

我有一个应用程序需要更新层次结构中的节点,从ID已知的特定节点向上更新。我使用以下MySQL语句来执行此操作:

update node as A 
join node as B 
   on A.lft<=B.lft and A.rgt>=B.rgt 
set A.count=A.count+1 where B.id=?

该表在id上有一个主键,在lft和rgt上有索引。该声明有效,但我发现它存在性能问题。查看相应select语句的EXPLAIN结果,我看到“B”表检查的行数非常大(可能是整个表)。

我可以轻松地将查询分成两个单独的:

select lft, rgt from node where id=?
LFT=result.lft
RGT=result.rgt
update node set count=count+1 where lft<=LFT and rgt>=RGT

但为什么原始陈述没有达到预期的效果,我怎么需要重新制定它才能更好地发挥作用?

根据要求,这是创建表的缩写版本:

CREATE TABLE `node` ( 
`id` int(11) NOT NULL auto_increment, 
`name` varchar(255) NOT NULL, 
`lft` decimal(64,0) NOT NULL, 
`rgt` decimal(64,0) NOT NULL, 
`count` int(11) NOT NULL default '0', 
PRIMARY KEY (`id`), 
KEY `name` (`name`), 
KEY `location` (`location`(255)), 
KEY `lft` (`lft`), 
KEY `rgt` (`rgt`), 
) ENGINE=InnoDB

我没有尝试添加复合索引(实际上,我没有现场执行此操作所需的访问级别);但我不知道它会如何帮助,试图思考数据库引擎如何尝试解决双重不等式。

4 个答案:

答案 0 :(得分:7)

你可以“强制”(至少高达5.5,版本5.6对优化器进行了一些改进,可能会使这种重写变得多余)MySQL首先通过将拆分的第一部分作为一个来评估表B中的条件子查询,然后将其用作派生表并连接到表A:

UPDATE node AS a 
  JOIN 
    ( SELECT lft, rgt
      FROM node
      WHERE id = ? 
    ) AS b 
    ON  a.lft <= b.lft 
    AND a.rgt >= b.rgt
SET 
    a.count = a.count + 1 ; 

效率仍取决于选择哪两个索引来限制要更新的行。仍然在使用这两个索引中的任何一个之后,需要进行表查找以检查另一列。所以,我建议你在(lft, rgt)上添加一个复合索引,在(rgt, lft)上添加一个索引,这样只用一个索引来查找哪些行应该更新。

我认为你正在使用嵌套集,并且这个更新的效率在大表上不会很好,因为查询有2个范围条件并且限制了B树索引的效率。

答案 1 :(得分:4)

我猜您最大的性能问题是您正在使用的JOIN。你可以通过做两个小子查询来完成它,而不是加入两个大表。

以下是示例:

UPDATE node AS a
SET a.count = a.count+1 
WHERE a.lft <= (SELECT lft FROM node WHERE id = ?) 
AND  a.rgt >= (SELECT rgt FROM node WHERE id = ?)

答案 2 :(得分:3)

这只是一个建议;我不知道它是否会奏效。

您的查询存在的问题是您在两列上存在不等式。这使得很难为它们使用索引 - 这反过来使join非常低效。这个想法是做两个连接,一个用于不等式的每一边,然后在id条件中包含on。因此,只有通过两者的节点才能通过:

UPDATE node a JOIN 
      (SELECT lft, rgt
       FROM node
       WHERE id = ? 
      ) l
      ON a.lft <= l.lft  join
      (SELECT lft, rgt
       FROM node
       WHERE id = ? 
      ) r
      on a.rgt >= r.rgt
    SET a.count = a.count + 1 ; 

正如我所说,我不知道这是否有效。但是你应该能够随时检查查询的explain,看看该计划是否正在使用两个不等式的索引。

答案 3 :(得分:1)

我知道mysql在引用正在更新的表方面存在问题,但对我来说显而易见的解决方案是:

update node  A 
set A.count=A.count+1
WHERE EXISTS (
   SELECT *
   FROM node B 
   WHERE B.id=?
   AND A.lft<=B.lft and A.rgt>=B.rgt
   );