本章主要介绍MySQL的服务器架构、各种存储引起之间的主要区别,区别的重要性,回顾一下MySQL的历史背景和基准测试
MySQL逻辑架构
连接管理与安全性
优化与执行
并发控制
读写锁
锁粒度
事务
隔离级别
死锁
事务日志
MySQL中的事务
多版本并发控制
MySQL的存储引擎
InnoDB存储引擎
MyISAM存储引擎
MySQL内建的其他存储引擎
第三方存储引擎
转换表的引擎
MySQL时间线(Timeline)
MySQL的开发模式
MySQL最重要、最与众不同的特性:
存储引擎架构
查询处理(Query Processing)
其他任务系统(Server Task)
数据的存储/提取
处理和存储分离的设计可以在是使用是根据性能、特性,以及其他需求来选择数据存储的方式
MySQL服务器逻辑架构图
如图,把MySQL的逻辑架构分为了三层:
最上层:大多数的网络C/S工具或服务都有的类似的架构---可以理解为C/S架构
MySQL的核心服务层:---这一层包括了大多数MySQL的核心服务功能
查询解析
分析
优化
缓存
跨存储引擎
存储过程
触发器
视图等
存储引擎---负责MySQL中的数据存储和提取
服务器通过API与存储引擎进行通信
API屏蔽不同存储引擎之间的差异,使得这些差异对上层的查询过程透明
存储引擎API包含几十个底层函数,但存储引擎不回去解析SQL(InnoDB会解析外键定义)---如"开始一个事务"or"根据主键提取一行记录"
不同存储引擎之间不会互相通信,只是简单地响应上层服务器的请求
如上图,分析客户端连接MySQL服务的C端特点:
每个C端连接都会在服务器进程中拥有一个线程,这个连接的查询只会在单独的线程中执行。
该线程只能轮流在某个CPU核心或者CPU中运行
服务器会负责缓存线程,不需要为每一个新建的连接创建或者注销线程---MySQL5.5或者更新的版本提供了API,支持线程床(Thread-Pooling)插件,可以使用池中少量线程来服务大量连接
C端连接到MySQL服务器时服务器需要对其进行认证。基于用户名、原始主机信息、密码
C端连接成功服务器会继续验证该C端是否具有执行某个特定查询的权限
如上图分析MySQL核心服务功能层
一个查询动作MySQL会做的事情:
解析查询
创建内部数据结构(解析树)
对查询进行优化
重写查询
决定表的读取顺序
选择合适的索引
...
用户通过特殊的关键字提示(hint)优化器,影响它的决策过程---where子句,也可以请求优化器解释(explain)优化过程的各个因素
优化器不关心表使用什么存储引擎,但存储引起对于优化查询有影响。优化器会请求存储引擎提供容量或某个具体操作的开销信息
select语句在解析查询前服务器会先检查缓存(Query Cache),如果能够在其中找到对应的查询,服务器就不必在执行查询解析、优化和执行的整个过程而是直接返回结果集。
背景:多个查询在同一时刻修改数据,都会产生并发控制的问题。
本章讨论服务器在两个层面的并发控制:
服务器层
存储引擎层
先来介绍为什么需要并发控制。
概念一:Unix系统的email box中的mbox文件格式是很简单的。一个mbox邮箱中的所有邮件都串行在一起,彼此首尾相连。投递邮件在文件末尾附加新邮件即可---mbox的文件读取和分析模式
概念二:两个进程在同一时刻对同一个邮箱进行投递邮件邮箱的数据就会被破坏,两封邮件的内容会交叉地附加在邮箱文件的末尾。---这个时候就会通过锁(lock)来防止数据损坏。邮箱被锁住就要等到他被释放另一个用户才能对他进行投递
概念三:"锁"的概念不支持并发,任意时刻只有一个进程可以修改邮箱的数据,在大容量的邮箱系统中就是个问题
把邮箱变成数据库中的表,邮件变成表中记录的数据就出现了这样的情况,当一个人在读取数据同时另一个人在修改数据,那么表反应出来的数据并不是实时的这个时候应该怎么处理?
解决上述问题的办法就是并发控制。
细节如图:
大多数时候MySQL锁的内部管理都是透明的
概念:
让锁定的对象更具选择性。尽量只锁定需要修改的部分数据而不是所有的资源。
方法:
只对会修改的数据片进行精确的锁定。---锁定的数据量越少,系统的并发程度越高,相互间不发生冲突即可。
一个"锁"动作的完整过程:
获得锁
检查锁是否已经解除
释放锁
...
这些步骤会增加系统开销,系统花大量的时间来管理锁那么就会影响系统的性能。
"锁"的策略就是在锁的开销和数据的安全性之间寻求平衡。不同的MySQL存储引擎有自己的锁策略和锁粒度。---锁粒度固定在某个级别可以为特定的应用场景提供更好的性能,同时会失去对另一些应用场景的良好支持。但是MySQL支持多个存储引擎的架构就能解决这一的情况。
概念:
MySQL中最基本的锁策略
开销最小的策略
过程描述:
用户在对表进行写操作(插入、删除、更新等)前,需要先获得写锁,这个动作会阻塞其他用户对该表的所有读写操作(锁定整张表)。读锁之间不互相阻塞
特点:
特定场景中,表锁也可能有良好的性能。---read local表锁支持某些类型的并发写操作。
写锁比读锁有更高的优先级,(写锁请求可以插入到读锁请求前面,读写请求不会插入到写锁请求之前)
两层机制:
MySQL存储引擎自身可以管理自己的锁,会使用各种有效的表锁来实现不同的目的
服务器会为alter table之类的语句使用表锁而忽略存储引擎的锁机制
优点:
可以最大限度地支持并发处理(同时也带来了最大的锁开销)
特点:
行级锁只在存储引擎层实现,MySQL服务层没有实现。服务层完全不了解存储引擎中的锁实现。---所有引擎都已自己的方式实现了锁的机制
概念:
一组原子性的SQL查询,一个独立的工作单元
特点:
数据库引擎能够成功地对数据库应用该组查询地全部语句,那么就执行改组查询。
如果有任何一条语句因为crash或者其他原因无法执行那么所有语句都不会执行
事务内的语句要么全部执行成功要么全部执行失败
for example
银行数据库有两张表:支票表(checking)和储蓄表(savings),现在需要从支票账户转200美元到储蓄账户需要经过的步骤:
检查支票账户余额高于200美元
从支票账户余额中减去200美元
在储蓄账户余额中增加200美元
上述三个步骤构成一个完整的动作,必须打包在一个事务当中,任何一个步骤失败必须回滚所有步骤
用start transaction开始一个事务,用commit提交事务将修改的数据持久保存,或者用rollback撤销所有的修改
语句:
start transaction(n.事务,交易);
select balance from checking where customer_id =...;
update checking set balance = balance - 200 where custormer_id =...;
update savings set balance = balance + 200 where custormer_id =...;
commit;
单纯知道事务依旧不能解决突发情况such as:
执行到第四条的时候服务器crash了
执行到第三条到第四条语句之间的时候另一个进程要删除支票账户的所有余额
...
这个时候就需要系统经过严格的ACID测试
概念
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
数据库总是从一个一致性的状态转换到另一个一致性的状态。前面的原子性保证了一致性,即便遇到服务器crash或者是突然加入进程的情况也不会导致金钱的损失
通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可以见的。---这里注意"通常来说",后面会讲到隔离级别(isolation lever)就会知道为什么说"通常来说"了
一旦事务提交,则其所做的修改就会永久保存到数据库。系统crash数据也不会丢失。持久性也分不同的级别。
事务的ACID特征在显示中基本不可能完成。一个兼容ACID的数据库系统需要做很多复杂但可能用户并没有觉察到的工作才能确保ACID的实现。
正因为MySQL的存储引擎架构让用户可以根据业务是否需要事务处理来选择合适的引擎。
对于一些不需要事务的查询类应用,可以选择一个非事务型的存储引擎,可以获得更高的性能。
即使存储引擎不支持事务,也可以用lock tables语句为应用提供一定程度的保护。
四种隔离级别
read uncommited(未提交读)
特点:
事务中的修改即使没有提交对其他事务也都是可见的。
事务可以读取未提交的数据(脏读(dirty read))
read commited(提交读)
特点:
一个事务从开始直到提交之前所做的任何修改对其他事务都是不可兼得。---不可重复读(nonrepeatable read)---两次执行同样的查询就会得到不一样的结果
repeatable read(可重复读)---MySQL默认的事务隔离级别
特点:
保证了同一个事务中多次读取同样的记录结果是一致的
无法解决幻读(phantom read)的问题
幻读
定义:当某个事务在读取某个范围内的记录时,会产生幻行(phantom row)
serializaable(可串行的)
特点:
最高的隔离级别,通过强制事务串行执行,避免了幻读。
具体实现方法:
在读取的每一行数据上都加上锁
联系客服