第1章 Mysql架构与历史
MYSQL最重要、最与众不同的特性是它的存储引擎架构,这种架构的设计将查询处理( Query Processing)及其他系统任务( Server Task)和数据的存储/提取相分离。这种处理和存储分离的设计可以在使用时根据性能、特性、以及其他需求来选择数据存储的方式。
MySQL逻辑架构
- 第一层: 大多数基于网络的C/S的工具或服务都是类似的结构,比如连接处理 授权认证 安全
- 第二层: 大多数MySQL 的核心服务功能都在这一层,包括查询解析、分析、优化、缓存以及所有的内置函数(日期、时间、数学和加密),所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图
- 第三层: 存储引擎负责Mysql中数据的存储和提取。和GUN/Linux下的各种文件系统一样,每个存储引擎都有它的优势和劣势。存储引擎不会解析SQL(InnoDB除外,它会解析外键定义,Mysql服务器本身没有实现该功能)不同的存储引擎也不会相互通信,只是响应上层服务器的请求,服务器通过API与存储引擎进行通信,且这些接口屏蔽了不同存储引擎之间的差异
修正认知错误:
读了高性能Mysql才认识到,对于任意一个成熟的版本,它都是由服务端层+核心功能+存储引擎三部分构成,且对于同一个版本而言,选择的存储引擎不同,导致Mysql的性能和特点也大大不同
注:
Mysql 主要分为Server层(Server服务层 + 核心功能层)和引擎层,Server层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用
引擎层是插件式的,目前主要包括,MyISAM,InnoDB(默认),Memory等
查询语句的执行流程如下:
查询缓存(命中缓存) —》权限校验
查询缓存(未命中缓存)—》分析器—》优化器—》权限校验—》执行器—》引擎
- 更新语句执行流程如下:分析器----》权限校验----》执行器—》引擎—redo log(prepare 状态—》binlog—》redo log(commit状态)
连接管理及安全性
每个客户端连接都会在服务器进程中拥有一个线程,这个链接的查询只会在这个单独的线程中执行,该线程只能轮流在某个CPU核心或CPU中运行。服务器会缓存线程,因此不需要为每一个新建的连接创建或者销毁线程。
Mysql服务在运行时即会创建好一个线程池,然后对接客户端请求,因此不需要为每一个新建的连接创建或者销毁线程
线程池的上限也决定了一个Mysql库难以承载高并发的需求
优化与执行
MYSQL会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写査询、决定表的读取顺序,以及选择合适的
素引等、同时用户可以通过特殊的关键字提示(hint)优化器,影响它的决策过程
这里想到了某天开发组长和一个组员争论的问题:
聚合索引a、b、c
在查询的时候不按照abc的顺序是否可以应用上索引,(explain select * from test where b = 1 and c = 1 and a = 1)答案是肯定的,依然会
通过索引查询
,即是因为优化器的优化
并发控制
Mysql在解决并发场景下多个查询在同一时刻修改数据问题时,主要从服务层及存储引擎层进行控制
读写锁
和Java层面的读写锁类似,对于Mysql中共享锁及排他锁,即读写锁也是保证了读读之间共享,读写,写写间相互排斥
在实际的数据库系统中,每时每刻都在发生锁定,当某个用户在修改某一部分数据时MYSQL会通过锁定防止其他用户读取同一数据,
大多数时候, MYSQL锁的内部管理都是透明的
锁粒度
一种提高共享资源并发性的方式就是让锁定对象更有选择性。尽量只锁定需要修改的部分数据,而不是所有的资源。更理想的方式
是,只对会修改的数据片进行精确的锁定。任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之
间不发生冲突即可。但于此同时,锁本身也是一种开销,如果为了减少锁定的数据量导致锁本身占用资源过多,也是得不偿失。
因此大多数商业数据库一般都是在表上施加行级锁(row - level lock),并以各种复杂的方式来实现,以便在锁比较多的情况下尽
可能提供更好的性能
而Mysql则提供了多种方案选择,每种存储引擎都可以实现自己的锁策略和锁粒度,主要有两种:
表锁:
故名意思即锁定整个表,需要注意的是写锁比读锁具有更高的优先级,因此一个写锁请求可以优先于读锁请求执行
另外,尽管各个存储引擎可以实现自己的锁机制,但是Mysql服务端会为诸如ALTER,TABLE之类的语句使用表锁,而忽略存储引擎
自己的策略
行级锁:
行级锁策略在Mysql服务端完全没有实现,都是借由存储引擎实现,如:InnoDB
事务
事务具有四个必要特性:
原子性(A):不可分割的最小单位
一致性(C):要么都成功要么都失败
隔离性(I):事务之间不可见
持久性(D):事务提交,永久生效
隔离级别
- READ UNCIMMITTED(未提交读):造成脏读,实际很少使用
- READ COMMITTED(提交读 / 不可重复读): 两次执行同样的查询,可能会得到不一样的结果
REPEATABLE READ(可重复读)
:依然会有幻读(Phantom Read) 的问题,进一步通过多版本并发控制(Multi version Concurrency Control,MVCC)解决,是MySQL的默认隔离级别- SERIALIZABLE(串行化):在读取的每一行上都加上锁,可能导致大量的超时和锁争用,实际很少使用
死锁
死锁的必要条件:
- 互斥:某种资源一次只允许一个进程访问
- 占有且等待 : 一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源
- 不可抢占:无法抢占其他已被占用的资源
- 循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源
为了解决这种问题,数据库系统实现了各种死锁检测和死锁超时机制。越复杂的系统,比如 INNODB存储引擎,越能检测到死锁的
循环依赖,并立即返回一个错误,且在同一个场景中由于存储引擎不同,A引擎可能产生死锁,B则不会
事务日志
使用事务日志,存储引擎在修改数据时只需要把修改行为记录到硬盘的事务日志中,而不用每次都将修改的数据本身持久到硬盘。
事务日志采用的是追加的方式,写日志操作是磁盘上的顺序I/O,所以要快很多,事务日志持久以后,内存中被修改的数据可以在
后台慢慢刷回到磁盘,称之为预写式日志(Write-Ahead Logging),修改操作需要写两次磁盘
Mysql中的事务
Mysql提供了两种事务性的存储引擎:InnoDB 和 NDB Cluster
自动提交(AUTOCOMMIT)
Mysql默认采用自动提交(AUTOCOMMIT)的模式,如果不明确开始一个事务,则每个查询都被当做一个事务执行提交操作
在事务中混合使用存储引擎
Mysql服务器不管理事务,事务是由下层存储引擎实现的。混合使用事务型和非事务性的表不会有问题(例如 InnoDB 和 MyISAM),但是回滚时,非事务的表无法撤回
隐式和显示锁定
InnoDB 采用的是两阶段锁定协议(two-phase locking protocol),随时可执行锁定,在执行commit 后者rollback时会释放,这是隐式锁定
显示锁定(强烈不建议使用)
SELECT … LOCK IN SHARE MODE
SELECT … FOR UPDATE
多版本并发控制
多版本并发控制,即MVCC
,该模块是基于提升并发性能的考虑,它是行级锁的一个变种,但是它在很多的情况避免了加锁的操
作,因此开销更低,虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行
INNODB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间
(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号( system version number)。每开始一个新的事务,系统版本号都
会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
我个人感觉MVCC的策略和Java8中的乐观读写锁- StampedLock 非常相似,通过乐观锁的思路,减少悲观锁的使用和阻塞的情况发生,StampedLock 是通过对比邮戳整数Stamp,而Mysql通过对比系统版本号来决定需不需要完全加锁
Mysql的存储引擎
Mysql的存储引擎多种多样,远远不是很多面试答案中题到的:MyISAM及 InnoDB两种,通过高性能Mysql书中对于各种存储引擎的
介绍,在选择存储引擎时候大概率可以毋庸置疑的选择InnoDB
不过新的问题来了,什么是存储引擎:根据较新的Mysql版本,存储引擎早已支持插件化了,即一个Mysql存储引擎是实现了Mysql存储引擎基本接口的程序,可以以插件的形式与Mysql数据库一同工作
也就是说,如果你能力足够强,你也可以完全自定义一个Mysql存储引擎
InnoDB概览
InnoDB采用mvcc来支持高并发,实现了四个标准的隔离级别,默认级别是REPEATABLE READ(可重复读),通过间隙锁(next-key
locking)策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询设计的行,还会对索引中的间隙进行锁定,防止幻影行的插入.
InnoDB是通过聚簇索引建立的,InnoDB的索引结构和其他存储引擎有很大的不同,聚簇索引对主键查询有很高的性能,但二级索
引必须包含主键列,如果主键列很大的话,其他的所有索引都会很大。因此弱表上的索引较多的话,主键应当尽可能的小。InnoDB
的存储格式是平台独立的,可以将数据和索引文件从Intel平台复制到PowerPC或者Sun SPARC.
InnoDB 内部做了很多优化,包括从磁盘读取数据时采用的可预测性预读,能够自动在内存中创建hash索引以加速读操作的自适应
哈希索引,以及能够加入检查入操邹的插入缓冲区(insert buffer)
Mysql的其他存储引擎不支持热备份,要获取一致性视图需要停止对所有表的写入。
MyISAM引擎
Mysql 5.1及以前的版本,MyISAM是默认的储存引擎。提供了大量的特性,包括全文索引 压缩 空间函数(GIS),但MyISAM不支持事务
和行级锁,而且有一个大的缺陷,崩溃后无法修复。
优势:存储数据支持压缩,占用较小
提醒:绝对不要混用存储引擎!!!