MySQL系统架构:Storage Engine Layer

Published on 2016 - 06 - 05

MySQL存储引擎概述

MyISAM存储引擎是MySQL默认的存储引擎,也是目前MySQL使用非常广泛的存储引擎之一。它的前身就是我们在MySQL发展历程中所提到的ISAM,是ISAM的升级版本。其实在MySQL最开始发行的时候只有ISAM存储引擎,甚至当时的MySQL可以说还没有存储引擎这个概念。早期MySQL的架构中并没有sql layer和storage engine layer这两个结构清晰的层次,当时不管是理解代码还是系统架构,对于开发者来说都很痛苦的一件事情。到后来,MySQL意识到须要更改架构,将前端的业务逻辑和后端数据存储以清晰的层次结构拆分开,同时对ISAM做了功能上面的扩展和代码的重构,这就是MyISAM存储引擎的由来。

在MySQL 5.1之前(不包括5.1)的版本中,存储引擎必须在MySQL安装的时候和MySQL一起被编译并同时被安装。也就是说,5.1版之前的版本中,虽然存储引擎层和sql层的耦合已经非常少了,基本上完全是通过接口来实现交互的,但是这两层之间仍然没办法分离,即使在安装的时候也是一样。

但是从MySQL 5.1开始,MySQL AB对其结构体系做了较大的改造,并引入了一个新的概念:插件式存储引擎体系结构。MySQL AB在改造架构的时候,让存储引擎层和SQL层各自更为独立,耦合更小,甚至可以做到在线加载新的存储引擎,也就是完全可以将一个新的存储引擎加载到一个正在运行的MySQL中,且不影响MySQL的正常运行。插件式存储引擎的架构,使得存储引擎的加载和移出更为灵活方便,也使自行开发存储引擎更为方便简单。在这一点上面,目前还没有哪个数据库管理系统能够做到。

MySQL的插件式存储引擎主要包括MyISAM、InnoDB、NDB Cluster、Maria、Falcon、Memory、Archive、Merge、Federated等,其中最著名而且使用最为广泛的是MyISAM和InnoDB两种存储引擎。MyISAM是MySQL最早的ISAM存储引擎的升级版本,也是MySQL默认的存储引擎。实际上InnoDB并不是MySQL公司的,而是Innobase软件公司(在2005年被Oracle公司所收购)开发的,其最大的特点是提供了事务控制等特性,所以使用者也非常广泛。

其他一些存储引擎相对来说都应用于某些特定的场合:

  • NDB Cluster虽然也支持事务,但是主要是用于分布式环境,属于一个share nothing的分布式数据库存储引擎。
  • Maria是MySQL最新开发(还没有发布最终的GA版本)的MyISAM的升级版存储引擎。
  • Falcon是MySQL公司自行研发的替代当前InnoDB存储引擎的一款带有事务等高级特性的数据库存储引擎,目前正在研发阶段。
  • Memory存储引擎的所有数据和索引均存储于内存中,所以主要是用于一些临时表,或者对性能要求极高,但是允许在系统崩溃(crash)的时候丢失数据的特定场合。
  • Archive是一个数据经过高比例压缩存放的存储引擎,主要用于存放过期而且很少访问的历史信息,不支持索引。
  • Merge和Federated在严格意义上来说,并不能算作存储引擎。因为Merge存储引擎主要用于将几个基表中的数据合并(merge)到一起,对外作为一个表来提供服务,基表主要基于MyISAM存储引擎。而Federated所做的事情,实际上有点类似于Oracle的dblink,主要用于远程存取其他MySQL服务器上面的数据。

MyISAM存储引擎简介

在数据库中,MyISAM存储引擎的每一个表都被存放为三个以表名命名的物理文件。首先肯定会有任何存储引擎都不可缺少的存放表结构定义信息的.frm文件,另外还有.MYD和.MYI文件,分别存放了表的数据(.MYD)和索引数据(.MYI)。每个表都有且仅有这样三个文件作为MyISAM存储类型的表的存储,也就是说不管这个表有多少个索引,都存放在同一个.MYI文件中。

MyISAM支持以下三种类型的索引。

B-Tree索引

B-Tree索引,顾名思义,就是所有的索引节点都按照balance tree的数据结构来存储,所有的索引数据节点都在叶节点。

R-Tree索引

R-Tree索引的存储方式和b-tree索引有一些区别,主要设计用于为存储空间和多维数据的字段做索引,目前的MySQL版本仅支持geometry类型的字段作索引。

Full-text索引

Full-text索引就是我们常说的全文索引,它的存储结构也是b-tree。主要是为了解决当须要用like查询时的低效问题。

上面三种MyISAM索引类型中,最经常使用的就是B-Tree索引了,偶尔会使用到Full-text,但是R-Tree索引在一般系统中都很少用到。另外MyISAM的B-Tree索引有一个较大的限制,那就是参与一个索引的所有字段的长度之和不能超过1000字节。

虽然每一个MyISAM的表都是存放在一个相同后缀名的.MYD文件中的,但是每个文件的存放格式并不完全一样,因为MyISAM的数据存放格式分为静态(FIXED)固定长度、动态(DYNAMIC)可变长度及压缩(COMPRESSED)三种格式。当然,三种格式是可以任由我们自己选择的,可以在创建表的时候通过ROW_FORMAT来指定{COMPRESSED| DEFAULT},也可以通过MyISAMpack工具来进行压缩,默认是不压缩的。而在非压缩的情况下,是静态还是动态,就和我们表中字段的定义相关了。只要表中有可变长度类型的字段存在,该表就肯定是DYNAMIC格式的,如果没有任何可变长度的字段,则为FIXED格式,当然,你也可以通过Alter Table命令,强行将一个带有VARCHAR类型字段的DYNAMIC的表转换为FIXED,但是所带来的结果是原VARCHAR字段类型会被自动转换成CHAR类型。相反如果将FIXED转换为DYNAMIC,也会将CHAR类型字段转换为Varchar类型,所以大家强行转换时一定要谨慎。

MyISAM存储引擎的表是否足够可靠呢?在MySQL用户参考手册中列出了遇到如下情况时可能会出现的表文件损坏:

  1. 当MySQL正在做写操作的时候被中止(kill)或其他情况造成异常终止;
  2. 主机崩溃(crash);
  3. 磁盘硬件故障;
  4. MyISAM存储引擎中的Bug。

MyISAM存储引擎的某个表文件出错之后,仅影响到该表,不会影响到其他表,更不会影响到数据库。如果数据库正在运行过程中发现某个MyISAM表出现问题了,则可以在线通过Check Table命令来尝试校验它,并可以通过repair table命令来尝试修复。在数据库关闭状态下,也可以通过myisamchk工具来对数据库中的某个(或某些)表进行检测或修复。不过建议不到万不得已不要轻易对表进行修复操作,修复之前尽量做好可能的备份工作,以免带来不必要的后果。

另外MyISAM存储引擎的表理论上是可以被多个数据库实例同时使用同时操作的,但是一般都不建议这样做,而且MySQL官方的用户手册中也曾提到,尽量不要在多个mysqld之间共享MyISAM存储文件。

InnoDB存储引擎简介

在MySQL中使用最为广泛的除了MyISAM之外,就数InnoDB了。InnoDB作为第三方公司所开发的存储引擎,和MySQL遵守相同的开源许可证协议。

InnoDB之所以能如此受宠,主要是因为其功能方面的几个特点。

支持事务安全

InnoDB在功能方面最重要的一点就是对事务安全的支持,这无疑是让InnoDB成为MySQL最为流行的存储引擎的一个非常重要原因。而且它实现了SQL92标准所定义的所有4个级别的要求(READ UNCOMMITTED、READ COMMITTED、REPEATABLE和SERIALIZABLE)。对事务安全的支持,无疑让很多之前因为特殊业务要求而不得不放弃使用MySQL的用户重新支持MySQL,那些之前对数据库选型持观望态度的用户,也大大增加了对MySQL好感。

数据多版本读取

InnoDB在事务支持的同时,为了保证数据的一致性及并发的性能,通过UNDO信息实现了数据的多版本读取。

锁定机制的改进

InnoDB改变了MyISAM的锁机制,实现了行锁。虽然InnoDB行锁机制的实现是通过索引来完成的,但毕竟在数据库中,99%的SQL语句都是要使用索引来做数据检索的。所以,行锁定机制也无疑为InnoDB在承受高并发压力的环境下增强了不小的竞争力。

实现外键

InnoDB实现了外键引用这一数据库的重要特性,使在数据库端控制部分数据的完整性成为可能。虽然很多数据库系统调优专家都建议不要这样做,但是对于不少用户来说在数据库端加入外键控制可能仍然是成本最低的选择。

除了以上几个功能上面的亮点,InnoDB还有很多其他功能常常带给使用者不小的惊喜,同时也为MySQL带来了更多的客户。

在物理存储方面,InnoDB存储引擎也和MyISAM不太一样,虽然也有.frm文件来存放表结构定义相关的元数据,但是表数据和索引数据是存放在一起的。至于是每表单独使用独享表空间存放还是所有表一起使用共享表空间,完全由用户决定(通过特定配置),同时还支持符号链接。

InnoDB的物理结构分为两大部分。

数据文件(表数据和索引数据)

存放在数据表中的数据和所有的索引数据,包括主键和其他普通索引,在InnoDB中,已经存在表空间(tablespace)这样的概念了,但是它和Oracle的表空间相比又有较大的不同。首先,InnoDB的表空间分为两种形式。一种是共享表空间,也就是所有表数据、索引数据、各种元数据及事务的UNDO数据都被存放在同一个表空间(一个或多个数据文件)中,通过innodb_data_file_path来指定,增加数据文件须要停机重启。另外一种是独享表空间,也就是每个表的数据和索引被存放在一个单独的.ibd文件中,该文件包括每个表的表数据、索引数据及该表相关的事务UNDO数据。

虽然我们可以自由选择使用共享表空间还是独享表空间来存放数据,但是共享表空间是必须存在的,因为InnoDB各表的很多元数据信息都是存放在共享表空间中的。共享表空间的数据文件可以设置为固定大小和可动态扩展两种形式,动态扩展形式的文件可以设置文件的容量和每次的扩展量。在创建自动扩展的数据文件时,建议大家最好加上最大尺寸的属性,一是因为文件系统本身是有一定大小限制的(但是InnoDB并不知道),另一个原因就是自身维护的方便。另外,InnoDB不仅可以使用文件系统,还可以使用裸设备(RAW Device)。

当我们的共享表空间快要用完的时候,必须要为其增加数据文件。共享表空间增加数据文件的操作比较简单,只须要在innodb_data_file_path参数后面按照标准格式设置好文件路径和相关属性即可,不过这里有一点须要注意的,就是InnoDB在创建新数据文件的时候是不会创建目录的,如果指定的目录不存在,则会报错,导致MySQL无法启动。另外一个比较令人头疼的就是InnoDB在给共享表空间增加数据文件之后,必须要重启数据库系统才能生效,如果是使用裸设备,还须要重启两次。这也是我一直不太喜欢使用共享表空间而选用独享表空间的原因之一。

日志文件

InnoDB的日志文件和Oracle的REDO日志比较类似,它们同样采用轮循策略来写入,甚至在老版本中还有和Oracle一样的日志归档特性。如果你的数据库中使用了InnoDB的表,那么千万别删除InnoDB的日志文件,因为这很可能会让你的数据库崩溃,且无法重新启动。

由于InnoDB是事务安全的存储引擎,所以系统崩溃对它来说并不能造成非常严重的损失,由于REDO日志的存在,且有checkpoint机制的保护,InnoDB完全可以通过REDO日志将数据库崩溃时已经完成但还没有来得及将内存中已经修改的但未完全写入磁盘的数据进行重做操作(Rodo)写入数据文件,也能够将所有已部分完成并写入磁盘的未完成事务进行回滚操作(Undo),保证数据的一致性。

InnoDB不仅在功能特性方面和MyISAM存储引擎有较大区别,配置上它也是单独处理的。在MySQL启动参数文件设置中,InnoDB的所有参数都带有前缀“innodb_”,不论是和InnoDB数据及日志相关的,还是和其他一些性能、事务等相关的参数都是一样。和所有InnoDB相关的系统变量一样,所有与InnoDB相关的系统状态值也同样全部以“Innodb_”为前缀。当然,也完全可以仅仅通过一个参数(skip-innodb)来屏蔽MySQL中的InnoDB存储引擎,这样即使在安装编译的时候将InnoDB存储引擎安装进去了,使用者也无法创建InnoDB的表。

NDB Cluster存储引擎简介

NDB存储引擎也叫NDB Cluster存储引擎,主要用于MySQL Cluster分布式集群环境,Cluster是MySQL从5.0版本才开始提供的新功能。本节内容并不只是介绍NDB存储引擎,因为离开了MySQL CLuster整个环境,NDB存储引擎也将失去太多意义。所以这一节主要介绍MySQL Cluster的相关内容。

简单地说,MySQL Cluster就是在无共享存储设备的情况下实现的一种内存数据库Cluster环境,主要是通过NDB Cluster(简称NDB)存储引擎来实现的。

一般来说,一个MySQL Cluster的环境主要由以下三部分组成。

负责管理各个节点的Manage节点主机

管理节点负责整个Cluster集群中各个节点的管理工作,包括集群的配置,启动关闭各节点,以及实施数据的备份恢复等。管理节点会获取整个Cluster环境中各节点的状态和错误信息,并且将各Cluster集群中各个节点的信息反馈给整个集群中其他的所有节点。由于管理节点上保存在整个Cluster环境的配置,同时担任了集群中各节点的基本沟通工作,所以它必须是最先被启动的节点。

SQL层的SQL服务器节点(后面简称为SQL节点),也就是我们常说的MySQL Server

主要负责实现一个数据库在存储层之上的所有事情,比如连接管理,query优化和响应,Cache管理等,只有存储层的工作会交给NDB数据节点去处理。也就是说,在纯粹的MySQL Cluster环境中的SQL节点,可以被认为是一个不须要提供任何存储引擎的SQL服务器,因为它的存储引擎由Cluster环境中的NDB节点来担任。所以,SQL层各SQL服务器的启动与普通的MySQL启动有一定的区别,必须添加ndbcluster项,可以添加在my.cnf配置文件中,也可以通过启动命令行来指定。

Storage层的NDB数据节点,也就是上面说的NDB Cluster

NDB是一个内存式存储引擎,也就是说,它会将所有的数据和索引数据都加载到内存中,但也会将数据持久化到存储设备上。不过,最新版本已经支持非索引字段数据不用全部加载到内存中了,这对于有些数据量太大或基于成本考虑而没有足够内存空间来存放所有数据的用户来说,这的确是一个大好消息。

NDB节点主要是实现底层数据存储的功能,保存Cluster的数据。每一个NDB节点保存完整数据的一部分(或者一份完整的数据,视节点数目和配置而定),在MySQL CLuster里面叫做fragment。而每一个fragment,正常情况来讲都会在其他的主机上面有一份(或者多份)完全相同的镜像存在。这些都是通过配置来完成的,所以只要配置得当,MySQL Cluster在存储层就不会出现单点的问题。一般来说,NDB节点被组织成一个一个的NDB Group,一个NDB Group实际上就是一组存有完全相同的物理数据的NDB节点群。

上面提到了NDB各个节点对数据的组织,可能每个节点都存有全部的数据,也可能只保存一部分数据,主要是受节点数目和参数来控制的。首先在MySQL Cluster主配置文件(在管理节点上面,一般为config.ini)中,有一个非常重要的参数叫NoOfReplicas,这个参数指定了每一份数据被冗余存储在不同节点上面的份数,该参数一般也只须设置成2。因为正常来说,两个互为冗余的节点同时出现故障的概率还是非常小的,当然如果机器和内存足够多的话,也可以继续增大。一个节点上面是保存所有的数据还是一部分数据,还受到存储节点数目的限制。NDB存储引擎首先保证NoOfReplicas参数配置的要求来对数据冗余,并使用存储节点,然后再根据节点数目将数据分段来继续使用多余的NDB节点,分段的数目为节点总数除以NoOfReplicas所得。

其他存储引擎介绍

Merge存储引擎

Merge存储引擎,在MySQL用户手册中也提到了,被大家认为是MRG_MyISAM存储引擎。为什么?因为Merge存储引擎可以简单地理解其功能就是对结构相同的MyISAM表通过一些特殊的包装对外提供一个单一的访问入口,从而达到减小应用复杂度的目的。要创建MERGE表,不仅基表的结构要完全一致,包括字段的顺序、基表的索引也必须完全一致。

MERGE表本身并不存储数据,仅仅是为多个基表提供一个统一的存储入口。所以在创建MERGE表的时候,MySQL只会生成两个较小的文件,一个是.frm的结构定义文件,还有一个.MRG文件,用于存放参与MERGE表的名称(包括所属数据库schema)。之所以需要所属数据库的schema,是因为MERGE表不仅可以实现合并(merge)同一个数据库中的表,还可以合并不同数据库中的表,只要权限允许,并且在同一个MySQL Inctance中,就可以进行合并。MERGE表在被创建之后,仍然可以通过相关命令来更改底层的基表。

MERGE表不仅可以提供读取服务,也可以提供写入服务。要让MERGE表提供可INSERT服务,必须在表被创建的时候就指明INSERT数据要被写入哪一个基表,可以通过insert_method参数来控制。如果没有指定该参数,任何尝试往MERGE表中插入(insert)数据的操作,都会出错。此外,无法通过MERGE表直接使用基表上面的全文索引,要使用全文索引,必须通过基表本身的存取操作才能实现。

Memory存储引擎

Memory存储引擎,顾名思义,它是一个将数据存储在内存中的存储引擎。Memory存储引擎不会将任何数据存放到磁盘上,在磁盘上仅仅存放了一个表结构相关信息的.frm文件。所以一旦MySQL崩溃(crash)或主机崩溃之后,Memory的表就只剩下一个结构了。Memory表支持索引,并且同时支持Hash和B-Tree两种格式的索引。由于是存放在内存中,所以Memory都是按照定长的空间来存储数据的,而且不支持BLOB和TEXT类型的字段。Memory存储引擎实现页级锁定。

既然所有数据都存放在内存中,那么它消耗的内存量可想而知。在MySQL的用户手册上面有这样一个公式来计算Memory表实际消耗内存的大小:

BDB存储引擎

BDB存储引擎全称为BerkeleyDB存储引擎,和InnoDB一样,它也不是MySQL自己开发实现的,而是由Sleepycat Software提供的,当然,也是开源存储引擎,同样支持事务安全。

BDB存储引擎的数据存放也是每个表两个物理文件,一个.frm和一个.db的文件,数据和索引信息都是存放在.db文件中。此外,BDB为了实现事务安全,也有自己的REDO日志,和InnoDB一样,它可以通过参数指定日志文件存放的位置。在锁定机制方面,BDB则和Memory存储引擎一样,实现页级锁定。

由于BDB存储引擎实现了事务安全,那么它肯定也需要自己的check point机制。BDB在每次启动的时候,都会做一次check point操作,并且将之前的所有REDO日志清空。在运行过程中,也可以通过执行flush logs来手工对BDB进行check point操作。

FEDERATED存储引擎

FEDERATED存储引擎所实现的功能和Oracle的DBLINK基本相似,主要用来提供对远程MySQL服务器上面的数据的访问接口。如果我们使用源码编译来安装MySQL,那么必须手工指定启用FEDERATED存储引擎,因为MySQL默认是不启用该存储引擎的。

当我们创建一个FEDERATED表的时候,在本地仅仅创建了表的结构定义信息文件,所有数据均实时取自远程MySQL服务器上面的数据库。

当我们通过Query操作FEDERATED表的时候,实现过程基本如下:

  1. Query调用本地接口
  2. 本地接口调用MySQL Server API
  3. MySQL Server调用客户端API访问远程数据库
  4. 远程数据库调用MySQL客户端API
  5. 转换结果包(如果有的话)到相关格式
  6. MySQL Server API返回结果行或受行影响行计数信息

ARCHIVE存储引擎

ARCHIVE存储引擎主要用于通过较小的存储空间来存放过期的很少访问的历史数据。ARCHIVE表不支持索引,包含一个.frm的结构定义文件,一个.ARZ的数据压缩文件,还有一个.ARM的元数据信息文件。由于其所存放的数据的特殊性,ARCHIVE表不支持删除、修改操作,仅支持插入和查询操作。锁定机制为行级锁定。

BLACKHOLE存储引擎

BLACKHOLE存储引擎是一个非常有意思的存储引擎,功能恰如其名,就是一个“黑洞”。就像Unix系统下面的“devnull”设备一样,不管我们写入任何信息,都是有去无回。那么BLACKHOLE存储引擎有什么用呢?在我最初接触MySQL的时候也有过同样的疑问,不知道MySQL提供这样一个存储引擎给我们用意何在?但是在后来的一次数据的迁移过程中,正是BLACKHOLE给我带来了非常大的帮助。在那次数据迁移过程中,数据须要经过一个中转的MySQL服务器做一些相关的转换操作,然后再通过复制移植到新的服务器上面。可当时我没有足够的空间来支持这个中转服务器的运作。这时候就显示出BLACKHOLE的功效了,他不会记录下任何数据,但是会在binlog中记录下所有的Query。而这些Query最终都会被复制利用,并实施到最终的slave端。

MySQL的用户手册上面还介绍了BLACKHOLE存储引擎其他几项用途:

  1. Query语法的验证。
  2. 测试二进制日志记录的性能开销,如通过比较允许二进制日志功能的BLACKHOLE与禁止二进制日志功能的BLACKHOLE的性能来实现。
  3. 查找与存储引擎自身不相关的性能瓶颈,因为BLACKHOLE本质上是一个“no-op”的存储引擎。

CSV存储引擎

CSV存储引擎实际上操作的就是一个标准的CSV文件,它不支持索引。它的主要用途就是有些时候可能会须要通过数据库中的数据导出一份报表文件,而CSV文件是很多软件都支持的一种标准格式,所以可以先在数据库中建立一张CVS表,然后将生成的报表信息插入该表,即可得到一份CSV报表文件了。

参考文档