SELECT与索引的UPDATE性能

问题描述 投票:18回答:7

如果我SELECT ID然后UPDATE使用这些ID,那么UPDATE查询比使用UPDATE中的条件SELECT更快。

为了显示:

SELECT id FROM table WHERE a IS NULL LIMIT 10; -- 0.00 sec
UPDATE table SET field = value WHERE id IN (...); -- 0.01 sec

上述速度比具有相同条件的UPDATE快约100倍:

UPDATE table SET field = value WHERE a IS NULL LIMIT 10; -- 0.91 sec

为什么?

注意:a列已编入索引。

mysql performance
7个回答
29
投票

很可能第二个UPDATE语句锁定了更多行,而第一个使用唯一键并仅锁定它要更新的行。


4
投票

这两个查询不完全相同。您只知道表中的ID是唯一的。

UPDATE ... LIMIT 10最多可更新10条记录。

UPDATE ...如果存在重复的ID,则id IN(SELECT ... LIMIT 10)可以更新10个以上的记录。


4
投票

我不认为你的“为什么会有一个直截了当的回答?”没有做某种分析和研究。

SELECT查询通常是高速缓存的,这意味着如果多次运行相同的SELECT查询,则第一个查询的执行时间通常大于以下查询。请注意,这种行为只能在SELECT很重的情况下出现,而不是在第一个SELECT更快的情况下。因此,在您的示例中,由于缓存,SELECT可能需要0.00。 UPDATE查询使用不同的WHERE子句,因此它们的执行时间可能不同。

尽管列a已编入索引,但MySQL在执行SELECT或UPDATE时必须使用索引。请研究EXPLAIN输出。另外,请参阅SHOW INDEX的输出并检查“Comment”列是否为任何索引读取“disabled”?你可以在这里阅读更多 - http://dev.mysql.com/doc/refman/5.0/en/show-index.htmlhttp://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html

此外,如果我们暂时忽略SELECT并仅关注UPDATE查询,很明显它们不是都使用相同的WHERE条件 - 第一个在id列上运行而后者在a上运行。尽管两列都已编制索引,但并不一定意味着所有表索引的执行情况都相同。某些索引可能比另一索引更有效,具体取决于索引的大小或索引列的数据类型,或者它是单列还是多列索引。肯定可能还有其他原因,但我不是专家。

此外,我认为第二个UPDATE正在做更多的工作,因为与第一个UPDATE相比,它可能会放置更多的行级锁。确实,两个UPDATES最终都会更新相同的行数。但是在第一次更新中,锁定了10行,我认为在第二次UPDATE中,在执行UPDATE之前,所有a为NULL(超过10)的行都被锁定。也许MySQL首先应用锁定然后运行LIMIT子句来仅更新有限的记录。

希望以上解释有道理!


3
投票

您是否有复合索引或单独的索引?

如果它是ida列的复合索引,

在第二个更新语句中,不会使用a列的索引。原因是只使用最左边的前缀索引(除非aPRIMARY KEY

因此,如果你想要使用qazxsw poi列索引,你需要在你的a子句中包含qazxsw poi,首先是id然后是WHERE

此外,它取决于您使用的存储引擎,因为MySQL在引擎级别执行索引,而不是服务器。

你可以试试这个:

id

通过这样做a是最左边的索引,然后是UPDATE table SET field = value WHERE id IN (...) AND a IS NULL LIMIT 10;

同样来自您的评论,查找速度要快得多,因为如果您使用id,更新列将意味着a存储引擎必须将索引移动到不同的页面节点,或者如果页面已满,则必须拆分页面,因为InnoDB按顺序存储索引。此过程非常缓慢且昂贵,如果索引碎片化,或者表格非常大,则会变得更慢


1
投票

Michael J.V的评论是最好的描述。这个答案假设InnoDB是一个没有索引的列,'id'是。

第一个UPDATE命令中的WHERE子句正在处理表的主键InnoDB

第二个UPDATE命令中的WHERE子句正在处理非索引列。这使得更新列的查找速度明显变慢。

永远不要低估索引的力量。如果正确使用索引而不是没有索引的十分之一的表,那么表将表现得更好。


0
投票

关于“MySQL不支持更新您选择的同一个表”

a

这样做:

id

0
投票

接受的答案似乎是正确但不完整,存在重大差异。

据我所知,我不是SQL专家:

第一个查询您选择N行并使用主键更新它们。 这非常快,因为您可以根据最快的索引直接访问所有行。

使用LIMIT更新N行的第二个查询将锁定所有行并在更新完成后再次释放。

最大的区别在于你在案例1)中有一个RACE条件,在案例2中有一个原子UPDATE)

如果您有两个或更多同时调用案例1)查询,您将遇到从表中选择SAME ID的情况。两个调用将同时更新相同的ID,相互覆盖。这被称为“竞争条件”。

第二种情况是避免这个问题,mysql将在更新期间锁定所有行。如果第二个会话正在执行相同的命令,则它将有一个等待时间,直到行被解锁。所以没有竞争条件是可能的,代价是浪费时间。

© www.soinside.com 2019 - 2024. All rights reserved.