我有一个包含大约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。
答案 0 :(得分:3)
在InnoDB中,update
语句锁定他们扫描的每一行。这意味着要更新200行,它必须创建35万行级锁,同时保持回滚锁,并为读取已更改值的任何事务提供先前的值(因为事务不是承诺并且变更不是最终的)
因此,如果您需要更新所有行,请锁定整个表,您将获得更好的性能(您不需要行级锁)
但更好的是,提供一个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
作为旁注,通常不应该使用错误的选择性对列进行索引