MySQL的存储引擎
本节只是概要地描述MySQL的存储引擎,而不会涉及太多细节。
在文件系统中,MySQL将每个数据库(也可以称之为schema)保存为数据目录下的一个子目录。创建表时,MySQL会在数.据库子目录下创建一个和表同名的.frm文件保存表的定义。例如创建一个名为MyTable的表,MySQL会在文件中保存该表的定义。因为MySQL使用文件系统的目录和文件来保存数据库和表的定义,大小写敏感性和具体的平台密切相关。在Windows中,大小写是不敏感的;而在类Unix中则是敏感的。不同的存储引擎保存数据和索引的方式是不同的,但表的定义则是在MySQL服务层统一处理的。
可以使用SHOW TABLE STATUS命令(在MySQL5.0以后的版本中,也可以査询INF0RMATI0N_SCHEMA中对应的表)显示表的相关信息。例如,对于mysql数据库中的user表:
mysql> SHOW TABLE STATUS LIKE 'user' \G *************************** 1. row *************************** Name: user Engine: MyISAM Row_format: Dynamic Rows: 6 Avg_row_length: 59 Data_length: 356 Max_data_length: 4294967295 Index_length: 2048 Data_free: 0 Auto_increment: NULL Create_time: 2002-01-24 18:07:17 Update_time: 2002-01-24 21:56:29 Check_time: NULL Collation: utf8_bin Checksum: NULL Create_options: Comment: Users and global privileges 1 row in set (0.00 sec)
输出的结果表明,这是一个MyISAM表。输出中还有很多其他信息以及统计信息。下面 简单介绍一下每一行的含义。
- Name
表名。
- Engine
表的存储引擎类型。在旧版本中,该列的名字叫Type,而不是Engine。
- Rowformat
行的格式。对于MyISAM表,可选的值为Dynamic、Fixed或者Compressed。Dynamic的行长度是可变的,一般包含可变长度的字段,如VARCHAR或BLOB。Fixed的行长度则是固定的,只包含固定长度的列,如CHAR和INTEGER。Compressed的行则只在压缩表中存在。
- Rows
表中的行数。对于MyISAM和其他一些存储引擎,该值是精确的,但对于InnoDB,该值是估计值。
- Avg_row_length
平均每行包含的字节数。
- Data_length
表数据的大小(以字节为单位)。
- Max_data_length
表数据的最大容量,该值和存储引擎有关。
- Index_length
索引的大小(以字节为单位)。
- Data_free
对于MyISAM表,表示已分配但目前没有使用的空间。这部分空间包括了之前删除的行,以及后续可以被INSERT利用到的空间。
- Auto_increment
下一个AUTO_INCREMENT的值。
- Create_time
表的创建时间。
- Update_time
表数据的最后修改时间。
- Check_time
使用CKECK TABLE命令或者myisamchk工具最后一次检査表的时间。
- Collation
表的默认字符集和字符列排序规则。
- Checksum
如果启用,保存的是整个表的实时校验和。
- Create_options
创建表时指定的其他选项。
- Comment
该列包含了一些其他的额外信息。对于MyISAM表,保存的是表在创建时带的注释。对于InnoDB表,则保存的是InnoDB表空间的剩余空间信息。如果是一个视图,则该列包含“VIEW”的文本字样。
1.1 InnoDB存储引擎
InnoDB是MySQL的默认事务型引擎,也是最重要、使用最广泛的存储引擎。它被设计用来处理大量的短期(short-lived)事务,短期事务大部分情况是正常提交的,很少会被回滚。InnoDB的性能和自动崩溃恢复特性,使得它在非事务型存储的需求中也很流行。除非有非常特别的原因需要使用其他的存储引擎,否则应该优先考虑InnoDB引擎。如果要学习存储引擎,InnoDB也是一个非常好的值得花最多的时间去深入学习的对象,收益肯定比将时间平均花在每个存储引擎的学习上要髙得多。
InnoDB的历史
InnoDB有着复杂的发布历史,了解一下这段历史对于理解InnoDB很有帮助。2008年, 发布了所谓的InnoDB plugin,适用于MySQL 5.1版本,但这是Oracle创建的下一代InnoDB引擎,其拥有者是InnoDB而不是MySQL。这基于很多原因,这些原因如果要一一道来,恐怕得喝掉好几桶啤酒。MySQL默认还是选择了集成旧的InnoDB引擎。当然用户可以自行选择使用新的性能更好、扩展性更佳的InnoDB plugin来覆盖旧的版本。直到最后,在Oracle收购了Sun公司后发布的MySQL 5.5中才彻底使用InnoDB plugin替代了旧版本的InnoDB(是的,这也意味着InnoDB plugin已经是原生编译了,而不是编译成一个插件,但名字已经约定俗成很难更改)。
这个现代的InnoDB版本,也就是MySQL5.1中所谓的InnoDB plugin,支持一些新特性, 诸如利用排序创建索引(building index by sorting)、删除或者增加索引时不需要复制全表数据、新的支持压缩的存储格式、新的大型列值如BLOB的存储方式,以及文件格式管理等。很多用户在MySQL5.1中没有使用InnoDB plugin,或许是因为他们没有注意到有这个区别。所以如果你使用的MySQL 5.1,一定要使用InnoDB plugin,真的比旧版本的InnoDB要好很多。
InnoDB是一个很重要的存储引擎,很多个人和公司都对其贡献代码,而不仅仅是Oracle公司的开发团队。一些重要的贡献者包括Google、Yasufumi Kinoshita、Percona、Facebook等,他们的一些改进被直接移植到官方版本,也有一些由InnoDB团队重新实现。在过去的几年间,InnoDB的改进速度大大加快,主要的改进集中在可测量性、可扩展性、可配置化、性能、各种新特性和对Windows的支持等方面。MySQL 5.6实验室预览版和里程碑版也包含了一系列重要的InnoDB新特性。
为改善InnoDB的性能,Oracle投入了大量的资源,并做了很多卓有成效的工作(外部 贡献者对此也提供了很大的帮助)。在超过四核CPU的系统中InnoDB表现不佳,而现在已经可以很好地扩展至24核的系统,甚至在某些场景,32核或者更多核的系统中也表现良好。很多改进在MySQL 5.6中引入,
InnoDB概览
InnoDB的数据存储在表空间(tablespace)中,表空间是由InnoDB管理的一个黑盒子, 由一系列的数据文件组成。在MySQL4.1以后的版本中,InnoDB可以将每个表的数据和索引存放在单独的文件中。InnoDB也可以使用裸设备作为表空间的存储介质,但现代的文件系统使得裸设备不再是必要的选择。
InnoDB采用MVCC来支持髙并发,并且实现了四个标准的隔离级别。其默认级别是REPEATABLE READ(可重复读),并且通过间隙锁(next-key locking)策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定査询涉及的行,还会对索引中的间隙进行锁率,以防止幻影行的插入。
InnoDB表是基于聚簇索引建立的,会在后面详细讨论聚簇索引。InnoDB的索引结构和MySQL的其他存储引擎有很大的不同,聚簇索引对主键査询有很高的性能。不过它的二级索引(secondary index,非主键索引)中必须包含主键列,所以如果主键列很大的话,其他的所有索引都会很大。因此,若表上的索引较多的话,主键应当尽可能的小。InnoDB的存储格式是平台独立的,也就是说可以将数据和索引文件从Intel平台复制到PowerPC或者Sun SPARC平台。
InnoDB内部做了很多优化,包括从磁盘读取数据时采用的可预测性预读,能够自动在 内存中创建hash索引以加速读操作的自适应哈希索引(adaptive hash index),以及能够加速插入操作的插入缓冲区(insert buffer)等。
InnoDB的行为是非常复杂的,不容易理解。如果使用了InnoDB引擎,强烈建议阅读官方手册中的“InnoDB事务模型和锁”一节。如果应用程序基于InnoDB构建,则事先了解一下InnoDB的MVCC架构带来的一些微妙和细节之处是非常有必要的。存储引擎要为所有用户甚至包括修改数据的用户维持一致性的视图,是非常复杂的工作。
作为事务型的存储引擎,InnoDB通过一些机制和工具支持真正的热备份,Oracle提供 MySQL Enterprise Backup、Percona提供的开源的XtraBackup都可以做到这一点。MySQL的其他存储引擎不支持热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。
1.2 MyISAM存储引擎
在Mysql5.1及之前的版本,包括全文索引、压缩、空间函数(GIS)等,但MyISAM不支持事务和行级锁,而且有一个毫无疑问的缺陷就是崩溃后无法安全恢复。正是由于MyISAM引擎的缘故,即使MySQL支持事务已经很长时间了,在很多人的概念中MySQL还是非事务型的数据库。尽管MyISAM引擎不支持事务、不支持崩溃后的安全恢复,但它绝不是一无是处的。对于只读的数据,或者表比较小、可以忍受修复(repair)操作,则依然可以继续使用MyISAM(但请不要默认使用MyISAM,而是应当默认使用InnoDB)。
存储
MyISAM会将表存储在两个文件中:数据文件和索引文件,分别以.MYD和.MYI为扩展名。MyISAM表可以包含动态或者静态(长度固定)行。MySQL会根据表的定义来决定采用何种行格式。MyISAM表可以存储的行记录数,一般受限于可用的磁盘空间,或者操作系统中单个文件的最大尺寸。
在MySQL5.0中,MyISAM表如果是变长行,则默认配置只能处理256TB的数据,因为指向数据记录的指针长度是6个字节。而在更早的版本中,指针长度默认是4字节,所以只能处理4GB的数据。而所有的MySQL版本都支持8字节的指针。要改变MyISAM表指针的长度(调高或者调低),可以通过修改表的MAX_R0WS和AVG_R0W_LENGTH选项的值来实现,两者相乘就是表可能达到的最大大小。修改这两个参数会导致重建整个表和表的所有索引,这可能需要很长的时间才能完成。
MyISAM特性
作为MySQL最早的存储引擎之一,MyISAM有一些已经开发出来很多年的特性,可以满足用户的实际需求。
加锁与并发
MyISAM对整张表加锁,而不是针对行。读取时会对需要读到的所有表加共享锁,写入时则对表加排他锁。但是在表有读取査询的同时,也可以往表中插入新的记录(这被称为并发插入,CONCURRENT INSERT)。
修复
对于MyISAM表,MySQL可以手工或者自动执行检査和修复操作,但这里说的修复和事务恢复以及崩溃恢复是不同的概念。执行表的修复可能导致一些数据丢失,而且修复操作是非常慢的。可以通过CHECK TABLE mytable检査表的错误,如果有错误可以通过执行REPAIR TABLE mytable进行修复。另外,如果MySQL服务器已经关闭,也可以通过myisamchk命令行工具进行检査和修复操作。
索引特性
对于MyISAM表,即使是BLOB和TEXT等长字段,也可以基于其前500个字符创建索引。MyISAM也支持全文索引,这是一种基于分词创建的索引,可以支持复杂的査询。
延迟更新索引键(Delayed Key Write)
创建MyISAM表的时候,如果指定了DELAY_KEY_WRITE选项,在每次修改执行完成时,不会立刻将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区(in-memory key buffer),只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入到磁盘。这种方式可以极大地提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。延迟更新索引键的特性,可以在全局设置,也可以为单个表设置。
MyISAM压缩表
如果表在创建并导入数据以后,不会再进行修改操作,那么这样的表或许适合采用 MyISAM压缩表。
可以使用Myisampack对MyISAM表进行压缩(也叫打包pack)。压缩表是不能进行修改的(除非先将表解除压缩,修改数据,然后再次压缩)。压缩表可以极大地减少磁盘空间占用,因此也可以减少磁盘I/O,从而提升査询性能。压缩表也支持索引,但索引也是只读的。
以现在的硬件能力,对大多数应用场景,读取压缩表数据时的解压带来的开销影响并不 大,而减少I/O带来的好处则要大得多。压缩时表中的记录是独立压缩的,所以读取单行的时候不需要去解压整个表(甚至也不解压行所在的整个页面)。
MyISAM性能
MyISAM引擎设计简单,数据以紧密格式存储,所以在某些场景下的性能很好。MyISAM有一些服务器级别的性能扩展限制,比如对索引键缓冲区(key cache)的Mutex锁,MariaDB基于段(segment)的索引键缓冲区机制来避免该问题。MyISAM最典型的性能问题还是表锁的问题,如果你发现所有的査询都长期处于“Locked”状态,那么毫无疑问表锁就是罪魁祸首。
1.3 MySQL内建的其他存储引擎
MySQL还有一些有特殊用途的存储引擎。在新版本中,有些可能因为一些原因已经不再支持;另外还有些会继续支持,但是需要明确地启用后才能使用。
Archive引擎
Archive存储引擎只支持INSERT和SELECT操作,在MySQL5.1之前也不支持索引。
Archive引擎会缓存所有的写并利用zlib对插入的行进行压缩,所以比MyISAM表的磁 盘I/O更少。但是每次SELECT査询都需要执行全表扫描。所以Archive表适合日志和数据采集类应用,这类应用做数据分析时往往需要全表扫描。或者在一些需要更快速的INSERT操作的场合下也可以使用。
Archive引擎支持行级锁和专用的缓冲区,所以可以实现高并发的插入。在一个査询开 始直到返回表中存在的所有行数之前,Archive引擎会阻止其他的SELECT执行,以实现一致性读。另外,也实现了批量插入在完成之前对读操作是不可见的。这种机制模仿了事务和MVCC的一些特性,但Archive引擎不是一个事务型的引擎,而是一个针对高速插入和压缩做了优化的简单引擎。
Blackhole引擎
Blackhole引擎没有实现任何的存储机制,它会丢弃所有插入的数据,不做任何保存。但是服务器会记录Blackhole表的日志,所以可以用于复制数据到备库,或者只是简单地记录到日志。这种特殊的存储引擎可以在一些特殊的复制架构和日志审核时发挥作用。但这种应用方式我们碰到过很多问题,因此并不推荐。
CSV引擎
CSV引擎可以将普通的CSV文件(逗号分割值的文件)作为MySQL的表来处理,但这种表不支持索引。CSV引擎可以在数据库运行时拷入或者拷出文件。可以将Excel等电子表格软件中的数据存储为CSV文件,然后复制到MySQL数据目录下,就能在MySQL中打开使用。同样,如果将数据写入到一个CSV引擎表,其他的外部程序也能立即从表的数据文件中读取CSV格式的数据。因此CSV引擎可以作为一种数据交换的机制,非常有用。
Federated引擎
Federated引擎是访问其他MySQL服务器的一个代理,它会创建一个到远程MySQL服 务器的客户端连接,并将査询传输到远程服务器执行,然后提取或者发送需要的数据。最初设计该存储引擎是为了和企业级数据库如Microsoft SQL Server和Oracle的类似特性竞争的,可以说更多的是一种市场行为。尽管该引擎看起来提供了一种很好的跨服务器的灵活性,但也经常带来问题,因此默认是禁用的。MariaDB使用了它的一个后续改进版本,叫做FederatedX。
Memory引擎
如果需要快速地访问数据,并且这些数据不会被修改,重启以后丢失也没有关系,那么使用Memory表(以前也叫做HEAP表)是非常有用的。Memory表至少比MyISAM表要快一个数量级,因为所有的数据都保存在内存中,不需要进行磁盘I/O。Memory表的结构在重启以后还会保留,但数据会丢失。
Memroy表在很多场景可以发挥好的作用:
- 用于查找(lookup)或者映射(mapping)表,例如将邮编和州名映射的表。
- 用于缓存周期性聚合数据(periodically aggregated data)的结果。
- 用于保存数据分析中产生的中间数据。
Memory表支持Hash索引,因此査找操作非常快。虽然Memory表的速度非常快,但还是无法取代传统的基于磁盘的表。Memroy表是表级锁,因此并发写入的性能较低。它不支持BLOB或TEXT类型的列,并且每行的长度是固定的,所以即使指定了VARCHAR列,实际存储时也会转换成CHAR,这可能导致部分内存的浪费(其中一些限制在Percona版本已经解决)。
如果MySQL在执行査询的过程中需要使用临时表来保存中间结果,内部使用的临时表 就是Memory表。如果中间结果太大超出了Memory表的限制,或者含有BLOB或TEXT字段,则临时表会转换成MyISAM表。
提示:人们经常混淆Memory表和临时表。临时表是指使用CREATE TEMPORARY TABLE语句 创建的表,它可以使用任何存储引擎,因此和Memory表不是一回事。临时表只在单个连接中可见,当连接断开时,临时表也将不复存在。
Merge引擎
Merge引擎是MyISAM引擎的一个变种。Merge表是由多个MyISAM表合并而来的虚拟表。如果将MySQL用于日志或者数据仓库类应用,该引擎可以发挥作用。但是引入分区功能后,该引擎已经被放弃
NDB集群引擎
2003年,当时的MySQL AB公司从索尼爱立信公司收购了NDB数据库,然后开发了NDB集群存储引擎,作为SQL和NDB原生协议之间的接口。MySQL服务器、NDB集群存储引擎,以及分布式的、share-nothing的、容灾的、高可用的NDB数据库的组合,被称为MySQL集群(MySQL Cluster)。
1.4 第三方存储引擎
MySQL从2007年开始提供了插件式的存储引擎API,从此涌出了一系列为不同目的而设计的存储引擎。其中有一些已经合并到MySQL服务器,但大多数还是第三方产品或者开源项目。下面探讨一些我们认为在它设计的场景中确实很有用的第三方存储引擎。
OLTP类引擎
Percona的XtraDB存储引擎是基于InnoDB引擎的一个改进版本,已经包含在Percona Server和MariaDB中,它的改进点主要集中在性能、可测量性和操作灵活性方面。XtraDB可以作为InnoDB的一个完全的替代产品,甚至可以兼容地读写InnoDB的数据文件,并支持InnoDB的所有査询。
另外还有一些和InnoDB非常类似的OLTP类存储引擎,比如都支持ACID事务和MVCC0其中一个就是PBXT,由PaulMcCullagh和PrimebaseGMBH开发。它支持引擎级别的复制、外键约束,并且以一种比较复杂的架构对固态存储(SSD)提供了适当的支持,还对较大的值类型如BLOB也做了优化。PBXT是一款社区支持的存储引擎,MariaDB包含了该引擎。
TokuDB引擎使用了一种新的叫做分形树(Fractal Trees)的索引数据结构。该结构是缓存无关的,因此即使其大小超过内存性能也不会下降,也就没有内存生命周期和碎片的问题。TokuDB是一种大数据(Big Data)存储引擎,因为其拥有很高的压缩比,可以在很大的数据量上创建大量索引。目前其最适合在需要大量插入数据的分析型数据集的场景中使用,不过有些限制可能在后续版本中解决掉。
RethinkDB最初是为固态存储(SSD)而设计的,然而随着时间的推移,目前看起来和最初的目标有一定的差距。该引擎比较特别的地方在于采用了一种只能追加的写时复制B树(append-only copyon-write B-Tree)作为索引的数据结构。目前还处于早期开发状态,还没有测试评估过,也没有听说有实际的应用案例。
在Sun收购MySQL AB以后,Falcon存储引擎曾经作为下一代存储引擎被寄予期望,但现在该项目已经被取消很久了。Falcon的主要设计者Jim Starkey创立了一家新公司,主要做可以支持云计算的NewSQL数据库产品,叫做NuoDB(之前叫NimbusDB)。
面向列的存储引擎
MySQL默认是面向行的,每一行的数据是一起存储的,服务器的査询也是以行为单位处理的。而在大数据量处理时,面向列的方式可能效率更高。如果不需要整行的数据,面向列的方式可以传输更少的数据。如果每一列都单独存储,那么压缩的效率也会更髙。
Infobdght是最有名的面向列的存储引擎。在非常大的数据量(数十TB)时,该引擎工作良好。Infobright是为数据分析和数据仓库应用设计的。数据高度压缩,按照块进行排序,每个块都对应有一组元数据。在处理查询时,访问元数据可决定跳过该块,甚至可能只需要元数据即可满足査询的需求。但该引擎不支持索引,不过在这么大的数据量级,即使有索引也很难发挥作用,而且块结构也是一种准索引(quasi-index)。Infobright需要对MySQL服务器做定制,因为一些地方需要修改以适应面向列存储的需要。如果査询无法在存储层使用面向列的模式执行,则需要在服务器层转换成按行处理,这个过程会很慢。Infobright有社区版和商业版两个版本。
另外一个面向列的存储引擎是Calpont公司InfiniDB,也有社区版和商业版。InfiniDB可以在一组机器集群间做分布式査询,但目前还没有生产环境的应用案例。
社区存储引擎
如果要列举社区提供的所有存储引擎,可能会有两位数,甚至三位数。但是负责任地说,其中大部分影响力有限,很多可能都没有听说过,或者只有极少人在使用。在这里列举了一些,也大都没有在生产环境中应用过,慎用,后果自负。
Aria
之前的名字是Maria,是MySQL创建者计划用来替代MyISAM的一款引擎。MariaDB包含了该引擎,之前计划开发的很多特性,有些因为在MariaDB服务器层实现,所以引擎层就取消了。可以说Aria就是解决了崩溃安全恢复问题的MyISAM,当然也还有一些特性是MyISAM不具备的,比如数据的缓存(MyISAM只能缓存索引)。
Groonga
这是一款全文索引引擎,号称可以提供准确而高效的全文索引。
OQGraph
该引擎由Open Query研发,支持图操作(比如查找两点之间的最短路径),用SQL很难实现该类操作。
Q4M
该引擎在MySQL内部实现了队列操作,而用SQL很难在一个语句实现这类队列。
SphinxSE
该引擎为Sphinx全文索引搜索服务器提供了SQL接口。
Spider
该引擎可以将数据切分成不同的分区,比较髙效透明地实现了分片(shard),并且可以针对分片执行并行査询(分片可以分布在不同的服务器上)。
VPForMySQL
该引擎支持垂直分区,通过一系列的代理存储引擎实现。垂直分区指的是可以将表分成不同列的组合,并且单独存储。但对査询来说,看到的还是一张表。该引擎和Spider的作者是同一人。
1.5 选择合适的引擎
这么多存储引擎,我们怎么选择?大部分情况下,InnoDB都是正确的选择,所以Oracle 在MySQL 5.5版本时终于将InnoDB作为默认的存储引擎了。对于如何选择存储引擎,可以简单地归纳为一句话:“除非需要用到某些InnoDB不具备的特性,并且没有其他办法可以替代,否则都应该优先选择InnoDB引擎”。例如,如果要用到全文索引,建议优先考虑InnoDB加上Sphinx的组合,而不是使用支持全文索引的MyISAM。当然,如果不需要用到InnoDB的特性,同时其他引擎的特性能够更好地满足需求,也可以考虑一下其他存储引擎。举个例子,如果不在乎可扩展能力和并发能力,也不在乎崩溃后的数据丢失问题,却对InnoDB的空间占用过多比较敏感,这种场合下选择MyISAM就比较合适。
除非万不得已,否则建议不要混合使用多种存储引擎,否则可能带来一系列复杂的问题, 以及一些潜在的bug和边界问题。存储引擎层和服务器层的交互已经比较复杂,更不用说混合多个存储引擎了。至少,混合存储对一致性备份和服务器参数配置都带来了一些困难。如果应用需要不同的存储引擎,请先考虑以下几个因素。
事务
如果应用需要事务支持,那么InnoDB(或者XtraDB)是目前最稳定并且经过验证的选择。如果不需要事务,并且主要是SELECT和INSERT操作,那么MyISAM是不错的选择。一般日志型的应用比较符合这一特性。
备份
备份的需求也会影响存储引擎的选择。如果可以定期地关闭服务器来执行备份,那么备份的因素可以忽略。反之,如果需要在线热备份,那么选择InnoDB就是基本的要求。
崩溃恢复
数据量比较大的时候,系统崩溃后如何快速地恢复是一个需要考虑的问题。相对而言,MyISAM崩溃后发生损坏的概率比InnoDB要髙很多,而且恢复速度也要慢。因此,即使不需要事务支持,很多人也选择InnoDB引擎,这是一个非常重要的因素。
特有的特性
最后,有些应用可能依赖一些存储引擎所独有的特性或者优化,比如很多应用依赖聚簇索引的优化。另外,MySQL中也只有MyISAM支持地理空间搜索。如果一个存储引擎拥有一些关键的特性,同时却又缺乏一些必要的特性,那么有时候不得不做折中的考虑,或者在架构设计上做一些取舍。某些存储引擎无法直接支持的特性,有时候通过变通也可以满足需求。
你不需要现在就做决定。接下来会提供很多关于各种存储引擎优缺点的详细描述,也会讨论一些架构设计的技巧。一般来讲,可能有很多选项你还没有意识到,等学习更多知识再来看这个问题可能更有帮助些。如果无法确定,那么就使用InnoDB,这个默认选项是安全的,尤其是搞不清楚具体需要什么的时候。
如果不了解具体的应用,上面提到的这些概念都是比较抽象的。所以接下来会讨论一些 常见的应用场景,在这些场景中会涉及很多的表,以及这些表如何选用合适的存储引擎,下一节将进行一些总结。
日志型应用
假设你需要实时地记录一台中心电话交换机的每一通电话的日志到MySQL中,或者通 过Apache的mod_log_sql模块将网站的所有访问信息直接记录到表中。这一类应用的插入速度有很高的要求,数据库不能成为瓶颈。MyISAM或者Archive存储引擎对这类应用比较合适,因为它们开销低,而且插入速度非常快。
如果需要对记录的日志做分析报表,则事情就会变得有趣了。生成报表的SQL很有可能 会导致插入效率明显降低,这时候该怎么办?
一种解决方法,是利用MySQL内置的复制方案将数据复制一份到备库,然后在备库上执行比较消耗时间和CPU的查询。这样主库只用于高效的插入工作,而备库上执行的査询也无须担心影响到日志的插入性能。当然也可以在系统负载较低的时候执行报表查询操作,但应用在不断变化,如果依赖这个策略可能以后会导致问题。
另外一种方法,在日志记录表的名字中包含年和月的信息,比如web_logs_2012_01或者web_logs_2012_01。这样可以在已经没有插入操作的历史表上做频繁的査询操作,而不会干扰到最新的当前表上的插入操作。
只读或者大部分情况下只读的表
有些表的数据用于编制类目或者分列清单(如工作岗位、竞拍、不动产等),这种应用 场景是典型的读多写少的业务。如果不介意MyISAM的崩溃恢复问题,选用MyISAM引擎是合适的。不过不要低估崩溃恢复问题的重要性,有些存储引擎不会保证将数据安全地写入到磁盘中,而许多用户实际上并不清楚这样有多大的风险(MyISAM只将数据写到内存中,然后等待操作系统定期将数据刷出到磁盘上)。
提示:一个值得推荐的方式,是在性能测试环境模拟真实的环境,运行应用,然后拔下电源模拟崩溃测试。对崩溃恢复的第一手测试经验是无价之宝,可以避免真的碰到崩溃时手足无措。
不要轻易相信“MyISAM比InnoDB快”之类的经验之谈,这个结论往往不是绝对的。在很多我们已知的场景中,InnoDB的速度都可以让MyISAM望尘莫及,尤其是使用到聚簇索引,或者需要访问的数据都可以放入内存的应用。
当设计上述类型的应用时,建议采用InnoDB。MyISAM引擎在一开始可能没有任何问题,但随着应用压力的上升,则可能迅速恶化。各种锁争用、崩溃后的数据丢失等问题都会随之而来。
订单处理
如果涉及订单处理,那么支持事务就是必要选项。半完成的订单是无法用来吸引用户的。 另外一个重要的考虑点是存储引擎对外键的支持情况。InnoDB是订单处理类应用的最佳选择。
电子公告牌和主题讨论论坛
对于MySQL用户,主题讨论区是个很有意思的话题。当前有成百上千的基于PHP或者 Perl的免费系统可以支持主题讨论。其中大部分的数据库操作效率都不髙,因为它们大多倾向于在一次请求中执行尽可能多的査询语句。另外还有部分系统设计为不采用数据库,当然也就无法利用到数据库提供的一些方便的特性。主题讨论区一般都有更新计数器,并且会为各个主题计算访问统计信息。多数应用只设计了几张表来保存所有的数据,所以核心表的读写压力可能非常大。为保证这些核心表的数据一致性,锁成为资源争用的主要因素。
尽管有这些设计缺陷,但大多数应用在中低负载时可以工作得很好。如果Web站点的规 模迅速扩展,流量随之猛增,则数据库访问可能变得非常慢。此时一个典型的解决方案是更改为支持更高读写的存储引擎,但有时用户会发现这么做反而导致系统变得更慢了。
用户可能没有意识到这是由于某些特殊査询的缘故,典型的如:
mysql> SELECT COUNT(*) FROM table;
问题就在于,不是所有的存储引擎运行上述査询都非常快:对于MyISAM确实会很快, 但其他的可能都不行。每种存储引擎都能找出类似的对自己有利的例子。其它文章会将帮助用户分析这些状况,演示如何发现和解决存在的这类问题。
CD-ROM应用
如果要发布一个基于CD-ROM或者DVD-ROM并且使用MySQL数据文件的应用,可以考虑使用MyISAM表或者MyISAM压缩表,这样表之间可以隔离并且可以在不同介质上相互拷贝。MyISAM压缩表比未压缩的表要节约很多空间,但压缩表是只读的。在某些应用中这可能是个大问题。但如果数据放到只读介质的场景下,压缩表的只读特性就不是问题,就没有理由不采用压缩表了。
大数据量
什么样的数据量算大?我们创建或者管理的很多InnoDB数据库的数据量在3〜5TB之 间,或者更大,这是单台机器上的量,不是一个分片(shard)的量。这些系统运行得还不错,要做到这一点需要合理地选择硬件,做好物理设计,并为服务器的I/O瓶颈做好规划。在这样的数据量下,如果采用MyISAM,崩溃后的恢复就是一个噩梦。
如果数据量继续增长到10TB以上的级别,可能就需要建立数据仓库。Infobdght是MySQL数据仓库最成功的解决方案。也有一些大数据库不适合Infobright,却可能适合TokuDB。
1.6 转换表的引擎
有很多种方法可以将表的存储引擎转换成另外一种引擎。每种方法都有其优点和缺点。 在接下来的章节中,将讲述其中的三种方法。
ALTER TABLE
将表从一个引擎修改为另一个引擎最简单的办法是使用ALTER TABLE语句。下面的语句将mytable的引擎修改为InnoDB :
mysql> ALTER TABLE mytable ENGINE = InnoDB;
上述语法可以适用任何存储引擎。但有一个问题:需要执行很长时间。MySQL会按行将数据从原表复制到一张新的表中,在复制期间可能会消耗系统所有的I/O能力,同时原表上会加上读锁。所以,在繁忙的表上执行此操作要特别小心。一个替代方案是采用接下来将讨论的导出与导入的方法,手工进行表的复制。
如果转换表的存储引擎,将会失去和原引擎相关的所有特性。例如,如果将一张InnoDB表转换为MyISAM,然后再转换回InnoDB,原InnoDB表上所有的外键将丢失。
导出与导入
为了更好地控制转换的过程,可以使用mysqldump工具将数据导出到文件,然后修改文件中CREATE TABLE语句的存储引擎选项,注意同时修改表名,因为同一个数据库中不能存在相同的表名,即使它们使用的是不同的存储引擎。同时要注意mysqldump默认会自动在CREATE TABLE语句前加上DROP TABLE语句,不注意这一点可能会导致数据丢失。
创建与查询(CREATE和SELECT)
第三种转换的技术综合了第一种方法的高效和第二种方法的安全。不需要导出整个表的 数据,而是先创建一个新的存储引擎的表,然后利用INSERT…SELECT语法来导数据:
mysql> CREATE TABLE innodb_table LIKE myisam_table; mysql> ALTER TABLE innodb_table ENGINE=InnoDB; mysql> INSERT INTO innodb_table SELECT * FROM myisam_table;
数据量不大的话,这样做工作得很好。如果数据量很大,则可以考虑做分批处理,针对 每一段数据执行事务提交操作,以避免大事务产生过多的undo。假设有主键字段id,重复运行以下语句(最小值x和最大值y进行相应的替换)将数据导入到新表:
mysql> START TRANSACTION; mysql> INSERT INTO innodb_table SELECT * FROM myisam_table WHERE id BETWEEN x AND y; mysql> COMMIT;
这样操作完成以后,新表是原表的一个全量复制,原表还在,如果需要可以删除原表。 如果有必要,可以在执行的过程中对原表加锁,以确保新表和原表的数据一致。
Percona Toolkit 提供了一个pt-online-schema-change的工具(基于 Facebook 的在线schema变更技术),可以比较简单、方便地执行上述过程,避免手工操作可能导致的失误和烦琐。