4

我有一个应用程序需要从 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

4 回答 4

8

您可以“强制”(至少到 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-tree 索引的效率。

于 2013-09-18T11:48:32.823 回答
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 = ?)
于 2013-09-18T12:31:15.507 回答
3

这只是一个建议;我不知道它是否会起作用。

您的查询的问题是您在两列上有不等式。这使得对它们都使用索引变得非常困难——这反过来又使得join效率非常低。这个想法是做两个连接,一个用于不等式的每一侧,然后idon条件中包含 。因此,只有通过两者的节点才会通过:

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查询以查看计划是否使用索引来处理这两种不等式。

于 2013-09-18T12:15:21.823 回答
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
   );
于 2013-09-18T12:48:58.587 回答