设置所有行中一列的值非常慢

时间:2013-06-29 11:41:58

标签: mysql performance innodb

我有一个包含大约35万行的表,我最近从MyISAM存储引擎更改为InnoDB。

我运行查询

UPDATE `users` SET `online` = 0

每次我的服务器启动时,使用MyISAM时都没有问题。查询通常只影响几百行。查询的执行时间很慢,平均约为1.5秒,但我可以忍受。

现在我已经更改为InnoDB,但查询可能需要几十秒才能完成。

这是mysql-slow.log

的一部分
# Query_time: 29.431546  Lock_time: 0.000091 Rows_sent: 0  Rows_examined: 348617
SET timestamp=1372505574;
UPDATE users SET online = 0;

此特定查询将在线值更改为200行,其他行已经为0。

我能够通过将查询更改为

来解决问题
UPDATE `users` SET `online` = 0 WHERE `online` != 0

此查询大约需要0.1秒

现在,这是我的问题。从MyISAM更改为InnoDB时,为什么时间会如此显着增加?

如果没有WHERE部分,查询怎么会这么慢?据我所知,MySQL的查询优化器非常强大,但这表明相反。什么可能导致这个非常慢的查询执行时间?

MySQL服务器版本是5.5.31-0。

3 个答案:

答案 0 :(得分:3)

在InnoDB中,update语句锁定他们扫描的每一行。这意味着要更新200行,它必须创建35万行级锁,同时保持回滚锁,并为读取已更改值的任何事务提供先前的值(因为事务不是承诺并且变更不是最终的)

另一方面,MyISAM会锁定整个表格。

因此,如果您需要更新所有行,请锁定整个表,您将获得更好的性能(您不需要行级锁)

但更好的是,提供一个WHERE子句,就像你一样,InnoDB只会为匹配的行获取锁(以及索引树中的一些间隙锁,但这超出了问题的范围)

答案 1 :(得分:2)

InnoDB实现了事务语义。也就是说,它向你的桌子的其他读者投射了大量的工作,即你的online列值在同一时刻都变为零。如果您的客户端或服务器崩溃,它还可以自动将值回滚到状态。 MyISAM不关心这一点。对于具有数十万行的表来说,这是一项很多工作。

它并不关心值是否已为零。无论如何它会改变它们。

当你使用WHERE子句时,你会改变很多行,所以它的事务逻辑要做的事情要少得多。

此事务逻辑是一项关键功能。你以一种边缘情况使用它并不完美。

答案 2 :(得分:1)

你最好从

重写查询
UPDATE `users` SET `online` = 0 WHERE `online` != 0

UPDATE `users` SET `online` = 0 WHERE `online` = 1
使用= vs!=

时,

索引可以更好地查找

检查http://sqlfiddle.com/#!2/31088/4 并在此处查看“查看执行计划”以查看此处的需求

!= triggers a range 
= triggers a ref which should perform better

作为旁注,通常不应该使用错误的选择性对列进行索引