最近碰到一个奇怪的问题:在MySQL中修改一个大表的结构(增加一列),发现修改缓慢无比,表是用的INNODB引擎,大小大概6G的样子!按道理修改表结构应该会很快(因为CPU耗费不是很大,无非就是读就数据写入新的文件),由于执行时间超出了预期,所以该想办法解决了(要感谢我一位无所不能的伟大的同事 )
分析问题:
MYSQL修改表结构时,会从旧表中读取数据,然后写入临时表(和旧表在同一目录下,命名方式为#sql_MySQLID_ConnnectID的形式),所以假设你的磁盘IO是100M/S的话,每秒理想的情况是临时表以50M/S在增加 ).而实际发现速度只有1M/S
解决过程:
- 查看生成临时文件的大写变化(与机器的IO能力严重不符)
- 通过Top查看CPU,IO,内存使用情况,发现CPU,内存占用率非常低,根本没有跑起来,而总是在IOWait;
- 通过IOStat查看IO,发现IO Write有10M/S(说明在狂写数据,与临时文件大小变化严重不符,当然达不到50M/S,因为要做很多Check)
- 接着通过df查看磁盘空间的变化,发现1M/S(对应临时表的增加),也就是数据写出来了,但是在磁盘上看不见(太不应该了)
- 感觉应该是写日志之类的影响了性能(拼命写日志,然后又删掉,再写再删……)
结论:
MySQL的INNODB引擎会做文件恢复(通过记录操作日志来恢复数据),修改参数innodb_log_file_size为512M(默认5M),问题解决性能提升了10倍以上 。因为MySQL对这种改动完全做了日志记录基本上表有多大日志就有多大(那为什么不是性能不是提升了2倍呢?后面再说)
教训:
- 解决问题的过程很值得学习!
为什么性能是提升了10倍以上呢?每当日志文件写满后,最差的解决方案:关闭文件、删除文件、新建日志文件、重新开始写(请思考这个过程中的代价,打开、关闭特别费时间);最好的解决方案:写满后fseek到日志开头重新开始写(一次seek的代价,事实上MySQL使用两个同样的日志文件进行轮回的)。所以时刻记住fseek是一个奢侈的东西
其实这个问题有点弱智(疏忽),但是解决过程很有意思 和这个问题相关的还有写参数可以调整,但是都不是瓶颈所以不多说了。
参考资料:
http://www.muduo.net/index.php/uid-8974-action-viewspace-itemid-175571
http://nalai.net/content/view/305050/32/