了解MySQL的服务架构,理解MySQL的设计。MySQL设计时将查询处理(Query Processing)以及其他系统任务(Server Task)和数据的存储、提取分离开,这样的设计带来了极大的灵活性,可以根据需要选择存储引擎。

不满足是向上的齿轮。

— 鲁迅

1.MySQL逻辑架构

MySQL逻辑架构

  1. 客户端层:连接处理、授权认证、安全管理等服务
  2. 核心服务层:查询解析、分析,优化、缓存以及内置函数,以及跨存储引擎的功能:存储过程、触发器、视图等
  3. 存储引擎层:存储引擎主要实现MySQL中数据的存储和提取。核心服务通过API与储存引擎进行通信。其中,事务、数据提取这样的操作也是由存储引擎这一层来实现的。同时存储引擎不会解析SQL(InnoDB会解析外键定义),只是简单的响应核心服务的请求。
  4. MySQL8之后不再内置查询缓存。

1.1 连接管理和安全性

连接管理主要负责客户端和MySQL服务的通信:接收请求,传递结果信息。每个客户端连接都会在服务器进程中分配到一个线程,这个连接的查询只会在这个单独的线程中执行。当客户端连接时认证模块会基于用户名、原始主机信息、密码对其连接进行认证,同时也会进行权限校验。

1.2 优化与执行

对于一个查询,MySQL会解析这个查询:创建解析树,并进行重写查询、决定表的读取顺序、选择索引等。

2. 并发控制

2.1 读写锁

MySQL在两个层面进行并发控制:核心服务层和存储引擎层;并发控制可以由共享锁(读锁)和排他(写锁)锁来解决并发问题:

  • 读锁是共享的,多个读操作之间互不影响。
  • 写锁是排他的,一个写操作会阻塞其他的写锁和读锁。

2.2 锁粒度

尽量只锁定需要修改的部分数据,理想的状态时只对需要修改的数据片进行精确的锁定。为了平衡锁开销和数据安全性,一般是施加行级锁。

表锁(table lock):表锁会锁定整张表,这会阻塞其他用户的所有读写操作。(写锁可以插入到所队列中读锁的前面,读锁却不能插入到写锁之前)

行级锁(row lock):InnoDB存储引擎实现了行级锁,因此InnoDB可以最大程度的支持并发处理。当然也带来了很大的开销。当然了,行级锁是存储引擎层自己实现的,核心服务层并不感知。

3. 事务

事务就是一组原子性的SQL查询,或则一个独立的工作单元:要么事务内的这一组语句一起成功,要么一起失败。

原子性(Atomicity):一个事务被视为不可分割的最小工作单元,一个事务的所有操作要么全部提交成功,要么全部失败回滚。

一致性(Consistency):数据库总是从一个一致的状态到另一个一致的状态。事务没有提交,事务的修改就不会保存到数据库中。

隔离性(isolation)通常来说,一个事务所作的操作在最终提交之前,对其他事务来说是不可见的。

持久性(durability):一旦事务提交,则其所作的修改就会永久的保存到数据库中。

3.1 隔离级别

  • Read Uncommitted(读未提交)

    事务中的修改没有提交对其他事务也是可见的。事务可以读到未提交的数据称为脏读。

  • Read Committed(读已提交)

    一个事务从开始到提交之前,所做的任何修改对他其事务是不可见的。这个级别也叫不可重复读(在同一个事务内多次读取相同范围的记录可能不一致)。

  • Repetable Read(可重复读)

    repeable read解决了脏读的问题,保证了在同一个事务的多次读取同样记录的结果是一致的(在一次事务内)。可重复读不能解决幻读的问题,即同样的事务再次读取同样范围内的记录时,可能会产生幻行(即多次读数据可能不一样)。

  • Serializable(可串行化)

    最高的隔离级别,没有并发,强制各个事务串行执行。

3.2 死锁

两个或多个事务在统一资源上相互占用,并请求对方占用的资源,从而导致恶性循环的问题。多个事务试图以不同顺序锁定资源时、多个事务同时锁定同一个资源就会产生死锁。InnoDB目前处理死锁的方法是将持有最少行级锁的事务进行回滚

3.3 事务日志

使用事务日志,存储引擎在修改表的数据时只需要修改内存拷贝,再把该修改行为持久化在硬盘的事务日志中。同时写事务日志时磁盘一小块区域的顺序IO,速度极快。事务日志持久化后,内存中被修改的数据由后台程序慢慢刷回磁盘。

如果数据修改以及记录到事务日志并持久化,此时系统崩溃,存储引擎可以在系统重启之后自动恢复数据。

3.4 MySQL的事务

MySQL默认采用自动提交事务。如果不是显式的生命一个事务,MySQL会把每一个查询都当作一个事务来操作。MySQL核心服务层部管理事务,由下层的存储引擎来实现,因此一个事务中使用多种存储引擎不可靠的。

3.5 隐式和显示锁定

InnoDB是两阶段锁定协议,在事务执行过程中,随时都可以执行锁定,锁只有在执行commit或者rollback时才会释放,并且所有的锁都在一瞬间释放。

4. 多版本并发控制(MVCC)

可以认为MVCC是行级锁的一个变种,很多情况下避免了加锁,基本实现了非阻塞读。典型的有乐观并发控制和悲观病啊控制。

MVCC是通过保存某个时间点的快照来实现的,就是说不管执行多久,每个事务看到的数据是一致的。根据事务开始时间不同, 每个事务对同一张表,同一时刻看到的数据可能是不一样的。

4.1 InnoDB的实现

InnoDB的MVCC是通过在每一行记录的后面保存两个隐藏列来实现。一个列保存了行的创建时间,一个是保存了过期时间(删除时间),当然存储的不是实际的时间,而是系统版本号(system version number),每开始一个事务,版本号都会自动递增。事务开始时刻的系统版本号作为事务的版本号,用来和查询到的每行记录的版本号作比较。

  • Select:InnoDB会根据这两个条件来查询:
    • 只查找版本号小于或者等于当前事务的数据行,这样可以保证事务读取到的数据要么是在事务开始前就存在的,要么是自己插入或者修改的。
    • 行的删除要么未定义,要么大于当前事务的版本号,这样可以保证读取到的数据在事务开始之前没有被删除
  • Insert:InnoDB为新插入的每一行数据保存当前的系统版本号为行版本号。
  • Delete:InnoDB为删除的每一行保存当前的版本号为行删除标识。
  • Update:InnoDB为插入一条新纪录,保存当前系统版本号为行版本号,同时保存当前系统的版本号到原来的行为行删除标识。

5.MySQL的存储引擎

InnoDB采用MVCC支持高并发,默认的隔离级别是Repetable read,通过间隙锁来防止幻读。同时InnoDB是基于聚簇索引建立的。具体的内容阅读到后面章节再细写。