MySQL最为人垢病的缺点就是缺乏事务的支持,MyISAM 性能虽然出众,不是没有代价的,InnoDB 又如何呢?InnoDB 的磁盘性能很令人担心,MySQL 缺乏良好的 tablespace 真是天大的缺陷!
InnoDB的表空间分成三种,一种是裸设备,一种是若干个 ibdata 文件(缺省方式),再一种是 Per-Table 文件,第一种用得少,第二种显然比第三种效率更差,本文的讨论基于 Per-Table,也即 innodb_file_per_table 配置参数。
现象重现:导出一个几百万行数据、带若干索引、有过频繁更新的表出来再导入,如果能以真实环境下的表来做测试就更理想,到 data 目录下观察对应的数据文件的 size 增长情况,会发现前 1G 速度相当令人满意,可是越往后效率越低,到后面基本就是蜗牛般的速度了。
不是只有导入才会让你慢得受不了,alter column/index 都会这样。。。
InnoDB 跟磁盘相关的文件存储,可以分成两个部分,一个是日志文件,另一个是数据文件。当有频繁的 INSERT/UPDATE 操作的时候,InnoDB 需要分别写入这两个文件,日志文件是顺序操作,数据文件包括了表数据和索引数据两个部分(和 MyISAM 直接拆开成表文件和索引文件不同,InnoDB 的表和索引是在同一个文件当中的)。
InnoDB 的索引用的是 BTREE 格式,如果当前更新的记录影响到索引的变化,逻辑上就存在三个操作,从原来的 BTREE 找到并摘除原来这行的记录并做调整、插入行数据、根据新数据查找 BTREE 相应的位置并重新插入新索引信息,假设索引数为 N,相应的逻辑操作数就为 1 + 2*N,显然这些信息不能保证在同一个磁盘连续空间上,因此需要 1 + 2*N 次的磁头移动,行数越大、文件尺寸越大,磁头的移动幅度也就可能越大,带来的后果显然是极差的磁盘 IO 效率。
MySQL 对于 MyISAM 的的磁盘 IO 优化是如何建议的呢?使用符号链接将表文件和索引文件分别指向不同的不同的目录,分散到不同的磁盘上以增加系统的访问速度。这种优化方式,在 InnoDB 上完全没有可能性!
如果有 tablespace 支持,磁盘效率问题就好解决了,一如商业数据库的做法,将日志、表文件、索引文件分别分布到不同的表空间也就是物理磁盘上,可是 MySQL 一直到 5.1 都没有提供 tablespace 功能,仅在 NDB/NDBCLUSTER 中才提供,但是 -- "CREATE TABLESPACE was added in MySQL 5.1.6. In MySQL 5.1, it is useful only with Disk Data storage for MySQL Cluster."。
不知道 Yahoo 等大网站是怎么解决这个难题的。。。头痛。。。考虑切换到 PostgreSQL 中。。