> For the complete documentation index, see [llms.txt](https://books.innohub.top/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://books.innohub.top/mysql/info/01-mysql-arch.md).

# MySql 架构与历史

\[TOC]

## 1. 逻辑架构

![arch](https://user-gold-cdn.xitu.io/2020/5/21/172374df43d5b907?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)

Mysql 是C/S架构，总共可以分为三层：

* 最上层客户端，mysql的图形化连接软件和各种编程语言的mysql连接模块，例如Python的MySQLdb模块，还有API接口等等
* 第二层就是mysql的核心服务，也称为service层。包括：查询解析、分析、优化、缓存以及所有的内置函数（例如：日期、时间、数学和加密函数）。同时，所有的跨存储引擎的功能都在这一层实现：存储过程、触发器、视图等
* 第三层为存储层，包含了存储引擎，存储引擎负责数据的存储与提取，服务器通过API与存储引擎进行通信，这些接口屏蔽了不同存储引擎之间的差异，使得这些差异对于上层的查询过程透明。

## 2. 并发控制

每一个客户端都会在服务器中拥有一个线程，这个连接的查询只会在该线程中进行，这个线程只会轮流在某一个cpu核心或者cpu上运行，服务器负责缓存线程。只要有多个查询需要同时操作数据都会有并发控制问题。mysql在**服务器层**、**存储引擎层**进行并发控制。

* 读写锁（共享锁、排他锁）
* 锁粒度： 行级锁、表级锁 不同类型的存储引擎会实现各自的锁策略以及锁粒度。**ALTER TABLE** 操作服务器层会使用表锁忽略存储引擎的锁机制；而存储引擎中的行锁实现对于服务器层也是不可见的
* 事务： 一组原子性的SQL查询，或者一个独立的工作单元

### 2.1 事务

一个运行良好的事务管理系统，需要具有ACID属性：

* **原子性**（Atomicity）： 一个事务是一个最小的可操作单元，要么成功，要么回滚，不可能只执行一部分
* **一致性**（Consistency）：数据库总是从一个一致性状态转移到另一个一致性状态
* **隔离性**（Isolation）：通常来说，一个事务所做的修改在提交之前对于其他事务是不可见的；在不同的隔离级别下，事务具有不同的可见性
* **持久性**（Durability）：事务一旦提交，所做的修改就会永久保存在数据库中

### 2.2 隔离级别

每种存储引擎实现的隔离级别不尽相同，主要有以下隔离级别：

* **未提交读(Read Uncommited)**:

  事务的修改即使没有提交，对于其他事务也是可见的，也称为脏读。这个隔离级别会导致很多问题，从性能上讲，并不会比其他隔离级别好太多，实际很少使用。
* **提交读（Read Commited）**:

  一个事务开始时只能看到其他事务已提交的结果，换句话说，一个事物未提交时所做的操作修改对于其他事务都是不可见的。又称为**不可重复读**，两次执行查询的结果可能不一致。
* **可重复读（Repeatable Read）**:

  解决了脏读的问题，该级别保证同一个事务多次读取同样记录的结果是一致的。但是理论上可重复读不能解决**幻读(Phantom Read)\***&#x7684;问题, 幻读是指 `当事务读取某一范围的数据时，另一个事务在该范围插入了新的记录，导致之前事务再次读取该范围的数据时会产生幻行` 。 InnoDB通过**多版本并发控制(MVCC, Multiversion Concurrency Control)** 解决幻读问题 **可重复读也是Mysql默认的隔离级别**
* **可串行化（Serializable）**：

  最高隔离级别，强制事务串行执行，避免幻读问题。可串行化通过对于每一行数据加锁，会导致大量的超时以及锁竞争问题。只有在非常需要确保数据一致，而且接受没有并发的情况下使用。

### 2.3 死锁

多个事务在同一资源上相互占用，并且请求锁定对方占用的资源，从而导致恶性循环的现象。多个事务试图以不同顺序锁定多个资源时就会产生死锁。数据库有多种死锁检测以及死锁超时机制。死锁发生后只有部分或者完全回滚其中一个事务才可以打破死锁。

### 2.4 事务日志

事务日志可以帮助提高事务的效率，进行数据修改时，**只修改其内存拷贝，修改记录持久化到事务日志中，不需要立即将修改数据持久化到硬盘，之后依据日志异步进行数据持久化**。事务日志采用追加方式，写日志操作只涉及一小块区域内的顺序io，效率更高。称为预写式日志（Write-Ahead Logging）

### 2.5 MVCC

大多数存储引擎都实现了MVCC，可以认为MVCC是行级锁的一个变种，但在很多情况下避免了加锁操作，因此开销更低。MVCC通过保存数据在某一个时间点的快照实现的，从而保证同一个事务看到的数据一致。典型的实现有乐观的并发控制以及悲观的并发控制。

InnoDB的mvcc通过在每一行记录的后面保存两个隐藏的列实现的。分别保存行的创建时间、删除时间。保存的并不是实际的时间戳，而是系统版本号。**每开始一个事务，系统版本号都会加一，事务开始时刻的系统版本号作为事务版本号**：

对于事务A，版本号 Va

* **SELECT**

  a. 创建版本号小于等于事务版本号Va的行

  b. 行的删除版本号未定义或者大于Va,确保事务读取的行在事务开始前未被删除
* **INSERT**

  插入的每一行使用系统版本号作为创建版本号
* **DELETE**

  为删除的行保存当前系统版本号作为行删除标志
* **UPDATE**

  相当于删除原有记录，创建一条新的记录。新插入一行使用系统版本号作为创建版本号，并将原有行的删除版本号置为系统版本号
