MYSQL的逻辑架构
MySQL最重要、最与众不同的特性是它的存储引擎架构,这种架构的设计将查询处理(QueryProcessing)及其他系统任务(Server Task)和数据的存储/提取相分离。这种处理和存储分离的设计可以在使用时根据性能、特性,以及其他需求来选择数据存储的方式。

第一层:基于网络的客户端/服务器的工具或者服务都有类似的架构。比如连接处理、授权认证、安全等等。
第二层:部署MYSQL核心服务功能,包括查询解析、分析、优化、缓存以及所有的内置函数,所有跨引擎的功能也在这一层实现:存储过程、触发器、视图等。
第三层是存储引擎层。负责MYSQL中数据的存储和提取。
连接与安全性
1.每个客户端连接都会在服务器进程中拥有一个线程,该连接的查询只会在这个单独的线程中执行,该线程留在一个内核或CPU上。服务器维护了一个缓存区,用于存放已就绪的线程,因此不需要为每个新的连接创建或销毁线程。
2.客户端连接服务器时,服务器需要对其进行身份验证,基于用户名、发起的主机名和密码。连接成功后会继续验证客户端是否具有其发出的每个查询权限。
优化与执行
MYSQL解析查询以创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以选择合适的索引等,也可以请求服务器解释优化过程的各个方面,使用户可以知道服务器是如何进行优化决策的。
并发控制
MYSQL在两个级别的并发控制:服务器级别与存储引擎级别。
读写锁
资源上的读锁是共享的,或者说是相互不阻塞的。多个客户端可以同时读取一个资源而互不干扰。写锁则是排他的,一个写锁即会阻塞读锁也会堵塞其他写锁,只有这样才能确保在特定时间点只有一个客户端能执行写入。
锁的粒度
任何时候,让锁定的数据量最小化,理论上就能保证在给定资源上同时进行更改操作,只要被修改的数据彼此不冲突即可。
表锁
锁定整张表,当客户端想对表进行写操作时,需要先获得一个写锁,这会阻塞其他客户端对该表的所有读写操作。只有没有人执行写操作时,其他读取的客户端才能获得读锁,读锁之间不会相互堵塞。
行级锁
锁定某一行,允许多人同时编辑不同的行,而不会阻塞彼此。行级锁是在存储引擎而不是服务器中实现的。
事务
事务就是一组SQL语句,作为一个工作单元以原子方式进行处理,要么全部成功,要么全部失败。
系统需要通过严格的ACID测试,原子性、一致性、隔离性、持久性。
原子性(atomicity)一个事务必须被视为一个不可分割的工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
一致性(consistency)数据库总是从一个一致性状态转换到下一个一致性状态。在前面的例子中,一致性确保了,即使在执行第3、4条语句之间时系统崩溃,支票账户中也不会损失200美元。如果事务最终没有提交,该事务所做的任何修改都不会被保存到数据库中。
隔离性(isolation)通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的,这就是隔离性带来的结果。在前面的例子中,当执行完第3条语句、第4条语句还未开始时,此时有另外一个账户汇总程序开始运行,其看到的支票账户的余额并没有被减去200美元。后面我们讨论隔离级别(isolation level)的时候,会发现为什么我们要说“通常来说”是不可见的。
持久性(durability)一旦提交,事务所做的修改就会被永久保存到数据库中。此时即使系统崩溃,数据也不会丢失。持久性是一个有点模糊的概念,实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且不可能有100%的持久性保障(如果数据库本身就能做到真正的持久性,那么备份又怎么能增加持久性呢?)。
隔离级别
4种隔离级别
READ UNCOMMITTED(未提交读)在READ UNCOMMITTED级别,在事务中可以查看其他事务中还没有提交的修改。这个隔离级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他级别好太多,却缺乏其他级别的很多好处,除非有非常必要的理由,在实际应用中一般很少使用。读取未提交的数据,也称为脏读(dirty read)。
READ COMMITTED(提交读)大多数数据库系统的默认隔离级别是READ COMMITTED(但MySQL不是)。READ COMMITTED满足前面提到的隔离性的简单定义:一个事务可以看到其他事务在它开始之后提交的修改,但在该事务提交之前,其所做的任何修改对其他事务都是不可见的。这个级别仍然允许不可重复读(nonrepeatable read),这意味着同一事务中两次执行相同语句,可能会看到不同的数据结果。
REPEATABLE READ(可重复读)REPEATABLE READ解决了READ COMMITTED[插图]级别的不可重复读问题,保证了在同一个事务中多次读取相同行数据的结果是一样的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(phantom read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(phantom row)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。本章稍后会对此做进一步讨论。
REPEATABLE READ是MySQL默认的事务隔离级别。SERIALIZABLE(可串行化)SERIALIZABLE是最高的隔离级别。该级别通过强制事务按序执行,使不同事务之间不可能产生冲突,从而解决了前面说的幻读问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中很少用到这个隔离级别,除非需要严格确保数据安全且可以接受并发性能下降的结果。

死锁
指两个或多个事务相互持有和请求相同资源上的锁,产生了循环依赖。为了解决思索,数据库系统实现了各种死锁检测和锁超时机制。InnoDB存储引擎,检测到循环依赖后会立即返回一个错误信息,目前InnoDB处理死锁的方式是将持有最少行级排他锁的事务回滚。
死锁生成的原因:1.真正的数据冲突;2.存储引擎的实现方式。
大多数情况只需要重新从头开始执行被回滚的事务即可,除非又遇到另一个死锁。
在当前连接中,可以使用SET命令设置AUTOCOMMIT变量来启用或禁用自动提交模式。启用可以设置为1或者ON,禁用可以设置为0或者OFF。如果设置了AUTOCOMMIT=0,则当前连接总是会处于某个事务中,直到发出COMMIT或者ROLLBACK,然后MySQL会立即启动一个新的事务。此外,当启用AUTOCOMMIT时,也可以使用关键字BEGIN或者START TRANSACTION来开始一个多语句的事务。修改AUTOCOMMIT的值对非事务型的表不会有任何影响,这些表没有COMMIT或者ROLLBACK的概念。
多版本并发控制
MYSQL会将行级锁和可以提高并发性能的多版本并发控制技术和MVCC结合使用。包括Oracle、PostgreSQL以及其他一些数据库系统也都使用了MVCC。
MVCC的工作原理是使用数据在某个时间点的快照来实现的,这意味着无论事务运行多长时间,都可以看到数据的一致视图,也意味着不同的事务可以在同一时间看见同一张表中的不同数据。
MVCC仅适用于REPEATABLE READ和READ COMMITTED隔离级别。
复制
MYSQL被设计用于任何给定时间只在一个节点上接受写操作,复制是MYSQL提供的一种原生方式,将一个节点执行的写操作分发到其他节点。
对于在生产环境中运行的任何数据,都应该复制并至少有三个以上的副本,理想情况下应分布在不同的地区用于灾难恢复计划。
数据文件结构
在8.0版本中,MySQL将表的元数据重新设计为一种数据字典,包含在表的.ibd文件中。这使得表结构上的信息支持事务和原子级数据定义更改。在操作期间,我们不再仅仅依赖information_schema来检索表定义和元数据,而是引入了字典对象缓存,这是一种基于最近最少使用(LRU)的内存缓存,包括分区定义、表定义、存储程序定义、字符集和排序信息。服务器访问表的元数据的方式的这一重大变化减少了I/O,非常高效。特别是当前访问最活跃的那些表,在缓存中最常出现。每个表的.ibd和.frm文件被替换为已经被序列化的字典信息(.sdi)。
InnoDB引擎
InnoDB是MySQL默认的通用存储引擎。默认情况下,InnoDB将数据存储在一系列的数据文件中,这些文件统被称为表空间(tablespace)。表空间本质上是一个由InnoDB自己管理的黑盒。InnoDB使用MVCC来实现高并发性,并实现了所有4个SQL标准隔离级别。InnoDB默认为REPEATABLE READ隔离级别,并且通过间隙锁(next-key locking)策略来防止在这个隔离级别上的幻读:InnoDB不只锁定在查询中涉及的行,还会对索引结构中的间隙进行锁定,以防止幻行被插入。
InnoDB表是基于聚簇索引构建的,InnoDB的索引结构与MySQL其他大部分存储引擎有很大的不同。聚簇索引提供了非常快速的主键查找。但是,因为二级索引(secondary index,非主键索引)需要包含主键列,如果主键较大,则其他索引也会很大。如果表中的索引较多,主键应当尽量小。
InnoDB内部做了很多优化。其中包括从磁盘预取数据的可预测性预读、能够自动在内存中构建哈希索引以进行快速查找的自适应哈希索引(adaptive hash index),以及用于加速插入操作的插入缓冲区(insert buffer)。
JSON文档支持
JSON类型在5.7版本被首次引入InnoDB,它实现了JSON文档的自动验证,并优化了存储以允许快速读取,这是对旧版本只能使用BLOB类型来处理JSON文档作为折中的重大改进。除了支持新的数据类型,InnoDB还引入了SQL函数来支持在JSON文档上的丰富操作。
数据字典的变化
MySQL 8.0的另一个主要变化是删除了基于文件的表元数据存储,并将其转移到使用InnoDB表存储的数据字典中。这给所有类似修改表结构这样的操作带来了InnoDB的崩溃恢复事务的好处。
原子DDL
MySQL 8.0引入了原子数据定义更改。这意味着数据定义语句现在要么全部成功完成,要么全部失败回滚。这是通过创建DDL特定的Undo日志和Redo日志来实现的,InnoDB便依赖这两种日志来跟踪变更——这是InnoDB经过验证的设计,已经扩展到MySQL服务器的操作中。