1、MySQL逻辑架构
最上层的服务并不是 MySQL所独有的,大多数基于网络的客户端/服务器的工具或者服务都有类似的架构。比如连接处理、授权认证、安全等等。
第二层架构是 MySQL 比较有意思的部分。大多数 MySQL 的核心服务功能都在这一层包括查询解析、分析、优化、缓存以及所有的内置函数(例如,日期、时间、数学和加密函数),所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等。
第三层包含了存储引擎。存储引擎负责 MySOL中数据的存储和提取。和 GNU/Linux 下的各种文件系统一样,每个存储引擎都有它的优势和劣势。服务器通过 API 与存储引擎进行通信。这些接口屏蔽了不同存储引擎之间的差异,使得这些差异对上层的查询过程透明。存储引擎 API包含几十个底层函数,用于执行诸如“开始一个事务”或者“根据主键提取一行记录”等操作。但存储引擎不会去解析 SQL,不同存储引擎之间也不会相互通信,而只是简单地响应上层服务器的请求。
1.1、连接管理与安全性
每个客户端连接都会在服务器进程中拥有一个线程,这个连接的査询只会在这个单独的线程中执行,该线程只能轮流在某个 CPU 核心或者 CPU 中运行。服务器会负责缓存线程,因此不需要为每一个新建的连接创建或者销毁线程。当客户端(应用)连接到 MySQL服务器时,服务器需要对其进行认证。认证基于用户名、原始主机信息和密码。如果使用了安全套接字(SSL)的方式连接,还可以使用X.509证书认证。一旦客户端连接成功,服务器会继续验证该客户端是否具有执行某个特定查询的权限(例如,是否允许客户端对世界数据库的国家)表执行SELECT语句)
1.2、优化与执行
MySQL会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化包括重写查询、决定表的读取顺序,以及选择合适的索引等。用户可以通过特殊的关键字提示(提示)优化器,影响它的决策过程。也可以请求优化器解释(explain)优化过程的各个因素,使用户可以知道服务器是如何进行优化决策的,并提供一个参考基准,便于用户重构查询和模式、修改相关配置使应用尽可能高效运行。
优化器并不关心表使用的是什么存储引擎,但存储引擎对于优化查询是有影响的。优化器会请求存储引擎提供容量或某个具体操作的开销信息,以及表数据的统计信息等。例如,某些存储引擎的某种索引,可能对一些特定的查询有优化。对于SELECT语句,在解析查询之前,服务器会先检查查询缓存,如果能够在其.中找到对应的查询,服务器就不必再执行查询解析、优化和执行的整个过程,而是直接返回查询缓存中的结果集。
2、并发控制
无论何时,只要有多个查询需要在同一时刻修改数据,都会产生并发控制的问题。本文的目的是讨论 MySQL在两个层面的并发控制:服务器层与存储引擎层。并发控制是一个内容庞大的话题,有大量的理论文献对其进行过详细的论述。本文只简要地讨论MySQL如何控制并发读写。
以 Unix 系统的 email box为例,典型的 mbox 文件格式是非常简单的。一个 mbox 邮箱中的所有邮件都串行在一起,彼此首尾相连。这种格式对于读取和分析邮件信息非常友好,同时投递邮件也很容易,只要在文件末尾附加新的邮件内容即可。
但如果两个进程在同一时刻对同一个邮箱投递邮件,会发生什么情况?显然,邮箱的数据会被破坏,两封邮件的内容会交叉地附加在邮箱文件的末尾。设计良好的邮箱投递系统会通过锁(1ock)来防止数据损坏。如果客户试图投递邮件,而邮箱已经被其他客户锁住那就必须等待,直到锁释放才能进行投递。
这种锁的方案在实际应用环境中虽然工作良好,但并不支持并发处理。因为在任意一个时刻,只有一个进程可以修改邮箱的数据,这在大容量的邮箱系统中是个问题。
2.1、读写锁
从邮箱中读取数据没有这样的麻烦,即使同一时刻多个用户并发读取也不会有什么问题因为读取不会修改数据,所以不会出错。但如果某个客户正在读取邮箱,同时另外一个用户试图删除编号为 25 的邮件,会产生什么结果?结论是不确定,读的客户可能会报错退出,也可能读取到不一致的邮箱数据。所以,为安全起见,即使是读取邮箱也需要特别注意。
如果把上述的邮箱当成数据库中的一张表,把邮件当成表中的一行记录,就很容易看出,同样的问题依然存在。从很多方面来说,邮箱就是一张简单的数据库表。修改数据库表中的记录,和删除或者修改邮箱中的邮件信息,十分类似。
解决这类经典问题的方法就是并发控制,其实非常简单。在处理并发读或者写时,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。这两种类型的锁通常被称为共享锁(shared 1ock)和排他锁(exclusive lock),也叫读锁(read lock)和写锁(writelock)。
这里先不讨论锁的具体实现,描述一下锁的概念如下: 读锁是共享的,或者说是相互不阻塞的。多个客户在同一时刻可以同时读取同一个资源,而互不干扰。写锁则是排他的也就是说一个写锁会阻塞其他的写锁和读锁,这是出于安全策略的考虑,只有这样,才能确保在给定的时间里,只有一个用户能执行写入,并防止其他用户读取正在写入的同一资源。
在实际的数据库系统中,每时每刻都在发生锁定,当某个用户在修改某一部分数据时MySQL 会通过锁定防止其他用户读取同一数据。大多数时候,MySQL锁的内部管理都是透明的。
2.2、锁粒度
一种提高共享资源并发性的方式就是让锁定对象更有选择性。尽量只锁定需要修改的部分数据,而不是所有的资源。更理想的方式是,只对会修改的数据片进行精确的锁定任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可。
问题是加锁也需要消耗资源。锁的各种操作,包括获得锁、检查锁是否已经解除、释放锁等,都会增加系统的开销。如果系统花费大量的时间来管理锁,而不是存取数据,那么系统的性能可能会因此受到影响。
所谓的锁策略,就是在锁的开销和数据的安全性之间寻求平衡,这种平衡当然也会影响到性能。大多数商业数据库系统没有提供更多的选择,一般都是在表上施加行级锁(row-level 1ock),并以各种复杂的方式来实现,以便在锁比较多的情况下尽可能地提供更好的性能。
而MySOL则提供了多种选择。每种MySQL存储引擎都可以实现自己的锁策略和锁粒度。在存储引擎的设计中,锁管理是个非常重要的决定。将锁粒度固定在某个级别,可以为某些特定的应用场景提供更好的性能,但同时却会失去对另外一些应用场景的良好支持。好在 MySQL支持多个存储引擎的架构,所以不需要单一的通用解决方案。下面将介绍两种最重要的锁策略。
2.2.1、表锁(table lock)
表锁是 MySQL,中最基本的锁策略,并且是开销最小的策略。表锁非常类似于前文描述的邮箱加锁机制:它会锁定整张表。一个用户在对表进行写操作(插入、删除、更新等)前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他读取的用户才能获得读锁,读锁之间是不相互阻塞的。
在特定的场景中,表锁也可能有良好的性能。例如,READ LOCAL表锁支持某些类型的并发写操作。另外,写锁也比读锁有更高的优先级,因此一个写锁请求可能会被插入到读锁队列的前面(写锁可以插入到锁队列中读锁的前面,反之读锁则不能插入到写锁的前面)。
尽管存储引擎可以管理自己的锁,MySQL本身还是会使用各种有效的表锁来实现不同的目的。例如,服务器会为诸如 ALTER TABLE之类的语句使用表锁,而忽略存储引擎的锁机制。
2.2.2、行级锁(row lock)
行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销)。众所周知,在InnoDB 和 XtraDB,以及其他一些存储引擎中实现了行级锁。行级锁只在存储引擎层实现,而 MySQL服务器层(如有必要,请回顾前文的逻辑架构图)没有实现。服务器层完全不了解存储引擎中的锁实现。在本章的后续内容以及全书中,所有的存储引擎都以自己的方式显现了锁机制。
3、事务
在理解事务的概念之前,接触数据库系统的其他高级特性还言之过早。事务就是一组原子性的 SQL查询,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。
事务的ACID和事务的隔离级别在此就不赘述。
3.1、事务日志
事务日志可以帮助提高事务的效率。使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序 I/O,而不像随机I/0 需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。事务日志持久以后,内存中被修改的数据在后台可以慢慢地刷回到磁盘。目前大多数存储引警都是这样实现的,我们通常称之为预写式日志(Write-Ahead Logging),修改数据需要写两次磁盘。
如果数据的修改已经记录到事务日志并持久化,但数据本身还没有写回磁盘,此时系统崩溃,存储引擎在重启时能够自动恢复这部分修改的数据。具体的恢复方式则视存储引擎而定。
3.2、MySQL 中的事务
MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster。另外还有一些第三方存储引擎也支持事务,比较知名的包括 XtraDB 和PBXT。
3.2.1、自动提交(AUTOCOMMIT)
MySQL默认采用自动提交(AUTOCOMMIT)模式。也就是说,如果不是显式地开始一个事务,则每个查询都被当作一个事务执行提交操作。在当前连接中,可以通过设置AUTO COMMIT 变量来启用或者禁用自动提交模式。
3.2.2、在事务中混合使用存储引擎
MySQL服务器层不管理事务,事务是由下层的存储引擎实现的。所以在同一个事务中使用多种存储引警是不可靠的。
如果在事务中混合使用了事务型和非事务型的表(例如InnoDB和 MyISAM 表),在正常提交的情况下不会有什么问题。但如果该事务需要回滚,非事务型的表上的变更就无法撤销,这会导致数据库处于不一致的状态,这种情况很难修复,事务的最终结果将无法确定。所以,为每张表选择合适的存储引擎非常重要。
在非事务型的表上执行事务相关操作的时候,MySQL通常不会发出提醒,也不会报错有时候只有回滚的时候才会发出一个警告:“某些非事务型的表上的变更不能被回滚” 但大多数情况下,对非事务型表的操作都不会有提示。
3.2.3、隐式和显式锁定
InnoDB 采用的是两阶段锁定协议(two-phase locking protocol)。在事务执行过程中,随时都可以执行锁定,锁只有在执行COMMIT 或者 ROLLBACK的时候才会释放,并且所有的锁是在同一时刻被释放。前面描述的锁定都是隐式锁定,InnoDB 会根据隔离级别在需要的时候自动加锁。
另外,InnoDB 也支持通过特定的语句进行显式锁定,这些语句不属于 SQL规范
LOCK IN SHARE MODESELECT
SELECT ... FOR UPDATE
MySQL 也支持 LOCK TABLES 和 UNLOCK TABLES 语句,这是在服务器层实现的,和存储引擎无关。它们有自己的用途,但并不能替代事务处理。如果应用需要用到事务,还是应该选择事务型存储引擎。
经常可以发现,应用已经将表从MyISAM转换到InnoDB,但还是显式地使用LOCK TABLES语句。这不但没有必要,还会严重影响性能,实际上InnoDB 的行级锁工作得更好。
3.3、多版本并发控制
MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。不仅是 MySQL,包括 Oracle、PostgreSQL等其他数据库系统也都实现了MVCC,但各自的实现机制不尽相同,因为MVCC 没有一个统一的实现标准。
可以认为 MVCC 是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。
MVCC的实现,是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。如果之前没有这方面的概念,这句话听起来就有点迷惑。熟悉了以后会发现,这句话其实还是很容易理解的。
前面说到不同存储引擎的MVCC 实现是不同的,典型的有乐观(optimistic)并发控制和悲观(pessimistic)并发控制。下面我们通过InnoDB 的简化版行为来说明MVCC 是如何工作的。
InnoDB的 MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。下面看一下在 REPEATABLE READ 隔离级别下,MVCC 具体是如何操作的。
SELECT
InnoDB 会根据以下两个条件检查每行记录:
a. InnoDB 只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
b.行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。
只有符合上述两个条件的记录,才能返回作为查询结果
INSERT
InnoDB 为新插入的每一行保存当前系统版本号作为行版本号。
DELETE
InnoDB 为删除的每一行保存当前系统版本号作为行删除标识。
UPDATE
InnoDB 为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。
保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。
MVCC 只在 REPEATABLE READ和 READ COMMITTED 两个隔离级别下工作。其他两个隔离级别都和 MVCC不兼容,因为 READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。
4、MySQL 的存储引擎
4.1、InnoDB 概览
InnoDB 的数据存储在表空间(tablespace)中,表空间是由InnoDB 管理的一个黑盒子由一系列的数据文件组成。在 MySQL 4.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的其他存储引擎不支持热备份,要获取一致性视图需要停止对所有表的写入而在读写混合场景中,停止写入可能也意味着停止读取。
4.2、MyISAM 存储引擎
在 MySQL 5.1 及之前的版本,MyISAM 是默认的存储引擎。MyISAM 提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM 不支持事务和行级锁,而且有一个毫无疑问的缺陷就是崩溃后无法安全恢复。正是由于 MyISAM 引擎的缘故,即使MySQL支持事务已经很长时间了,在很多人的概念中 MySQL还是非事务型的数据库。尽管 MyISAM 引擎不支持事务、不支持崩溃后的安全恢复,但它绝不是一无是处的。对于只读的数据,或者表比较小、可以忍受修复(repair)操作,则依然可以继续使用 MyISAM(但请不要默认使用 MyISAM,而是应当默认使用 InnoDB)。
4.2.1、存储
MyISAM 会将表存储在两个文件中:数据文件和索引文件,分别以.MYD和.MYI为扩展名。MyISAM 表可以包含动态或者静态(长度固定)行。MySQL会根据表的定义来决定采用何种行格式。MyISAM 表可以存储的行记录数,一般受限于可用的磁盘空间或者操作系统中单个文件的最大尺寸。
在MySQL 5.0中,MyISAM 表如果是变长行,则默认配置只能处理 256TB 的数据,因为指向数据记录的指针长度是6个字节。而在更早的版本中,指针长度默认是4字节,所以只能处理 4GB 的数据。而所有的 MySQL版本都支持8字节的指针。要改变MyISAM 表指针的长度(调高或者调低),可以通过修改表的MAX ROWS和 AVG ROWLENGTH选项的值来实现,两者相乘就是表可能达到的最大大小。修改这两个参数会导致重建整个表和表的所有索引,这可能需要很长的时间才能完成。
4.2.2、MyISAM 特性
作为MySQL 最早的存储引擎之一,MyISAM 有一些已经开发出来很多年的特性,可以满足用户的实际需求。
加锁与并发
MyISAM 对整张表加锁,而不是针对行。读取时会对需要读到的所有表加共享锁写入时则对表加排他锁。但是在表有读取查询的同时,也可以往表中插入新的记录(这被称为并发插入,CONCURRENT INSERT)。
修复
对于 MyISAM 表,MySQL 可以手工或者自动执行检査和修复操作,但这里说的修复和事务恢复以及崩溃恢复是不同的概念。执行表的修复可能导致一些数据丢失而且修复操作是非常慢的。可以通过CHECK TABLE mytable检查表的错误,如果有错误可以通过执行 REPAIR TABLE mytable进行修复。另外,如果 MySQL 服务器已经关闭,也可以通过 myisamchk 命令行工具进行检査和修复操作。
索引特性
对于 MyISAM 表,即使是 BLOB和 TEXT 等长字段,也可以基于其前 500 个字符创建索引。MyISAM 也支持全文索引,这是一种基于分词创建的索引,可以支持复杂的查询。
创建 MyISAM 表的时候,如果指定了 DELAY KEY WRITE 选项,在每次修改执行完成时,不会立刻将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区(in-memorykey buffer),只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入到磁盘。这种方式可以极大地提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。延迟更新索引键的特性,可以在全局设置,也可以为单个表设置。
MyISAM 压缩表
如果表在创建并导入数据以后,不会再进行修改操作,那么这样的表或许适合采用MyISAM 压缩表。
可以使用 myisampack 对 MyISAM 表进行压缩(也叫打包pack)。压缩表是不能进行修改的(除非先将表解除压缩,修改数据,然后再次压缩)。压缩表可以极大地减少磁盘空间占用,因此也可以减少磁盘 I/0,从而提升查询性能。压缩表也支持索引,但索引也是只读的。
以现在的硬件能力,对大多数应用场景,读取压缩表数据时的解压带来的开销影响并不大,而减少 I/0 带来的好处则要大得多。压缩时表中的记录是独立压缩的,所以读取单行的时候不需要去解压整个表(甚至也不解压行所在的整个页面)。
MyISAM 性能
MyISAM 引擎设计简单,数据以紧密格式存储,所以在某些场景下的性能很好MyISAM 有一些服务器级别的性能扩展限制,比如对索引键缓冲区(key cache)的Mutex锁,MariaDB 基于段(segment)的索引键缓冲区机制来避免该问题。但 MyISAM最典型的性能问题还是表锁的问题,如果你发现所有的查询都长期处于“Locked”状态那么毫无疑问表锁就是罪魁祸首
4.3、MySQL 内建的其他存储引擎
MySQL还有一些有特殊用途的存储引擎。在新版本中,有些可能因为一些原因已经不再支持;另外还有些会继续支持,但是需要明确地启用后才能使用。
Archive 引擎
Archive 存储引擎只支持 INSERT和SELECT操作,在 MySQL 5.1 之前也不支持索引。Archive 引擎会缓存所有的写并利用 zlib 对插入的行进行压缩,所以比 MyISAM 表的磁盘 I/0 更少。但是每次 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 表在很多场景可以发挥好的作用
1、用于查找(lookup)或者映射(mapping)表,例如将邮编和州名映射的表。
2、用于缓存周期性聚合数据(periodically aggregated data)的结果。
3、用于保存数据分析中产生的中间数据
Memory 表支持 Hash索引,因此査找操作非常快。虽然 Memory 表的速度非常快,但还是无法取代传统的基于磁盘的表。Memroy表是表级锁,因此并发写入的性能较低。它不支持 BLOB 或 TEXT类型的列,并且每行的长度是固定的,所以即使指定了 VARCHAR列,实际存储时也会转换成CHAR,这可能导致部分内存的浪费(其中一些限制在 Percona版本已经解决)。
如果 MySQLL在执行查询的过程中需要使用临时表来保存中间结果,内部使用的临时表就是 Memory表。如果中间结果太大超出了 Memory表的限制,或者含有 BLOB 或 TEXT字段,则临时表会转换成 MyISAM 表。
4.4、选择合适的引擎
这么多存储引擎,我们怎么选择?大部分情况下,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 支持地理空间搜索。如果一个存储引擎拥有一些关键的特性,同时却又缺乏一些必要的特性,那么有时候不得不做折中的考虑,或者在架构设计上做一些取舍。某些存储引擎无法直接支持的特性有时候通过变通也可以满足需求。
5、总结
MySQL拥有分层的架构。上层是服务器层的服务和查询执行引擎,下层则是存储引擎。虽然有很多不同作用的插件 API,但存储引擎 API还是最重要的。如果能理解 MySQL在存储引擎和服务层之间处理査询时如何通过 API来回交互,就能抓住 MySOL的核心基础架构的精髓。
MySQL 最初基于ISAM 构建(后来被 MyISAM 取代),其后陆续添加了更多的存储引擎和事务支持。MySOL有一些怪异的行为是由于历史遗留导致的。例如,在执行 ALTERTABLE 时,MySOL提交事务的方式是由于存储引擎的架构直接导致的,并且数据字典也保存在.fm文件中(这并不是说InnoDB会导致ALTER变成非事务型的。对于InnoDB来说所有的操作都是事务)。