MySQL架构介绍

1. MySQL逻辑架构

MySQL服务器逻辑架构图

1.1 连接管理与安全性

        每个客户端连接都会在服务器进程中拥有一个线程,这个连接的查询只会在这个单独的线程中执行,该线程只能轮流在某个CPU核心或者CPU中运行。服务器会负责缓存线程,因此不需要为每一个新建的连接创建或者销毁线程。在连接建立后,服务器会验证客户端是否具有执行某个特定查询的权限。

1.2 优化与执行

        MySQL会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引等。用户可以通过特殊的关键字提示优化器,影响它的决策过程。也可以请求优化器解释优化过程的各个因素,使用户可以知道服务器是如何进行优化决策的,并提供一个参考基准。
        对于 $SELECT$ 语句,在解析查询之前,服务器会先检查查询缓存 ( $Query\ \ Cache$ ),如果能够在其中找到对应的查询,服务器就不必再执行查询解析、优化和执行的整个过程,而是直接返回查询缓存中的结果集。

2. 并发控制

        在实际的数据库系统中,每时每刻都在发生锁定,当某个用户在修改某一部分数据时,MySQL会通过锁定防止其他用户读取统一数据。大多数时候,MySQL的内部管理都是透明的。
        所谓的锁策略,就是在锁的开销和数据的安全性之间寻求平衡,这种平衡当然也会影响到性能。大多数商业数据库系统没有提供更多的选择,一般都是在表上施加行级锁 ( $row-level\ \ lock$ ),并以各种复杂的方式来实现,以便在锁比较多的情况下尽可能地提供更好的性能。而MySQL则提供了多种选择。每种MySQL存储引擎都可以实现自己的锁策略和锁粒度。在存储引擎的设计中,锁管理是个非常重要的决定。将锁粒度固定在某个级别,可以为某些特定的应用场景提供更好的性能,但同时却会失去对另外一些应用场景的良好支持。好在MySQL支持多个存储引擎的架构,所以不需要单一的通用解决方案。

3. 事务

        事务就是一组原子性的SQL查询,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。

        一个运行良好的事务处理系统,必须满足ACID特性。但在应用逻辑中,要想实现这一点很困难,甚至可以说是不可能完成的任务。一个兼容ACID的数据库系统,需要做很多复杂但可能用户并没有觉察到的工作。

3.1 隔离级别

3.2 死锁

        为了解决死锁问题,数据库系统实现了各种死锁检测和死锁超时机制。越复杂的系统,比如InnoDB存储引擎,越能检测到死锁的循环依赖,并立即返回一个错误。还有一种解决方式,就是当查询的时间达到等待超时的设定后放弃锁请求,这种方式通常来说不太好。InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚。
        锁的行为和顺序是和存储引擎相关的。以同样的顺序执行语句,有些存储引擎会产生死锁,有些则不会。死锁的产生有双重原因:有些是因为真正的数据冲突,这种情况通常很难避免,但有些完全是由于存储引擎的实现方式导致的。

3.3 事务日志

        使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到硬盘。事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。事务日志持久后,内存中被修改的数据在后台可以慢慢地刷回到磁盘。目前大多数存储引擎都是这样实现的,我们通常称之为预写式日志 ( $Write-Ahead\ \ Logging$ ),修改数据需要写两次磁盘。

3.4 MySQL中的事务

        MySQL提供了两种事务型的存储引擎:InnoDBNDB Cluster。另外还有一些第三方存储引擎也支持事务,比较知名的包括XtraDBPBXT
        MySQL默认采用自动提交 ( $AUTOCOMMIT$ ) 模式,如果不是显式开始一个事务,则每个查询都被当做一个事务执行提交操作。通过$SET\ \ AUTOCOMMIT$ ,可以启用或者禁用自动提交模式。禁用之后,只有显式地执行提交或者回滚操作,当前事务才会结束。但对于非事务型的表,比如MyISAM或者内存表,不会有任何影响。另外还有一些命令,在执行之前会强制执行COMMIT提交当前的活动事务,比如 $ALTER\ \ TABLE$ 。
        MySQL可以通过执行 $SET\ \ TRANSACTION\ \ ISOLATION\ \ LEVEL$ 命令来设置隔离级别。新的隔离级别会在下一个事务开始的时候生效。MySQL能够识别所有的 $4$ 个ANSI隔离级别,InnoDB引擎也支持所有的隔离级别。
        MySQL服务器层部管理事务,事务是由下层的存储引擎实现的。所以在同一个事务中使用多种存储引擎是不可靠的。在事务中混合使用事务型和非事务型的表,在正常提交的情况下不会有什么问题。但是当事务需要回滚时,非事务型的表上的变更就无法撤销,这会导致数据库处于不一致的状态,这种情况很难修复。
        InnoDB采用的是两阶段锁定协议 ( $two-phase\ \ locking\ \ protocol$ )。在事务执行过程中,随时都可以执行锁定,锁只有在执行COMMIT或者ROLLBACK的时候才会释放,并且所有的锁是在同一时刻被释放。前面描述的锁定都是隐式锁定,InnoDB会根据隔离级别在需要的时候自动加锁。

4. 多版本并发控制

        MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制 ( MVCC )。可以认为MVCC是行级锁的一个变种,但是它在很多情况下都避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。
        MVCC的实现,是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。不同引擎的MVCC实现是不同的,典型的有乐观并发控制和悲观并发控制。
        InnoDBMVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间。当然存储的不是实际时间,而是系统版本号。每开始一个新事务,系统版本号都会自动递增。在REPEATABLE READ隔离级别下,MVCC的操作方式如下:

        MVCC只在REPEATABLE READREAD COMMITTED两个隔离级别下工作。

5. MySQL的存储引擎

        在文件系统中,MySQL将每个数据库(也可以称之为schema)保存为数据目录下的一个子目录。创建表时,MySQL会在数据库子目录下创建一个和表同名的 $.frm$ 文件保存表的定义。可以使用 $SHOW\ \ TABLE\ \ STATUS$ 命令显示表的相关信息。

5.1 InnoDB

        InnoDBMySQL的默认事务型引擎,也是最重要、使用最广泛的存储引擎。它被设计用来处理大量的短期 ( $short-lived$ ) 事务,短期事务大部分情况是正常提交的,很少会被回滚。InnoDB的性能和自动崩溃恢复特性,使得它在非事务型存储的需求中也很流行。除非有非常特别的原因需要使用其他的存储引擎,否则应该优先考虑InnoDB引擎。
        InnoDB的数据存储在表空间 ( $tablespace$ ) 中,表空间是由InnoDB管理的一个黑盒子,由一系列的数据文件组成。在MySQL 4.1以后的版本中,InnoDB可以将每个表的数据和索引存放在单独的文件中。InnoDB也可以使用裸设备作为表空间的存储介质,但现代的文件系统使得裸设备不再是必要的选择。存在如下几种表空间:

        InnoDB使用MVVC来支持高并发,并且实现了四个标准的隔离级别。其默认级别是REPEATABLE READ,并且通过间隙锁 ( $next-key\ \ locking$ ) 策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入。
        InnoDB表是基于聚簇索引建立的,和MySQL的其他存储引擎有很大的不同,对主键查询有很高的性能。不过它的二级索引 ( $secondary\ \ index$ ,非主键索引 ) 中必须包含主键列,所以如果主键列很大的话,其他的所有索引都会很大。
        InnoDB内部做了很多优化,包括从磁盘读取数据时采用的可预测性预读,能够自动在内存中创建hash索引以加速读操作的自适应哈希索引 ( $adaptive\ \ hash\ \ index$ ),以及能够加速插入操作的插入缓冲区 ( $insert\ \ buffer$ ) 等。

5.2 MyISAM

        在MySQL 5.1及之前的版本,MyISAM是默认的存储引擎,提供了大量的特性,包括全文索引、压缩、空间函数等,但不支持事务和行级锁,以及崩溃后的安全恢复。虽然不支持事务和崩溃后的安全恢复,但对于只读的数据,或者表比较小,可以忍受修复操作,依然可以使用MyISAM
        MyISAM将表存储在两个文件中:数据文件和索引文件,分别以 $.MYD$ 和 $.MYI$ 为扩展名。MyISAM表可以包含动态或者静态(长度固定)行。在MySQL 5.0中,如果是变长行,则默认配置只能处理 $256TB$ 的数据,这个配置可以通过修改指针长度实现。
        作为MySQL最早的存储引擎之一,MyISAM有一些已经开发出来很多年的特性:

        如果表在创建并导入数据后,不会再进行修改操作,那么这样的表或许适合采用MyISAM压缩表,可以使用 $myisampack$ 对MyISAM表进行压缩。压缩表是不能进行修改的,可以极大地减少空间占用,因此也可以减少磁盘I/O,从而提升查询性能。压缩表也支持索引,但索引也是只读。

5.3 转换表的引擎

MySQL架构介绍