一、架构

Mysql分为服务层和引擎层

服务层(Service):连接器、查询缓存、分析器(词法分析、语法分析)、查询优化器、执行器;所有的内置函数,存储过程,视图等都在这里实现。

引擎层:InnoDB,MyISAM,Memory;负责数据的读取和存储;其架构模式是插件式的。

连接器:

这个阶段会等待TCP监听链接,读取用户名、密码,然后读取用户的权限,后期的操作均在这个权限上约束;

如果后期没有操作,连接会处于空闲状态,空闲过长会被断开,由参数wait_timeout决定;默认8小时;

连接过程比较复杂,所以建议采用连接池;

连接会占用资源,包括后期的查询缓存等,所以需要定期清理;

缓存器:(8.0版本后被删除)

拿到语句后会先查询缓存是否有该语句,有即返回;

基本是以key-value的形式存储的;

少用缓存器,弊大于利,除非你的数据很少更新,因为更新了会使得缓存失效;

分析器:

词法分析和语法分析;注意这里会包含包括表字是否存在,列名是否存在等判断;

优化器:

对于语句

mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;

先查t1的数据,和先查t2的数据,逻辑结果是一样的,但是效率可能差别很大,例如t1有索引而t2没有的情况下,优化器就是要做这个选择;

执行器:

先判断一下你对这个表 T 有没有执行查询的权限,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。

mysql> select * from T where ID=10;

调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;

调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。

执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给

二、Redo Log && Undo Log

例子:孔乙己,酒柜老板记账两种方式,一是每一次都翻开账本,二是每次先记在黑板上,打烊的时候记录到账本;在忙的时候第二种方式销量最高,所以Mysql也是有类似机制,叫WSL,write ahead log

Redo Log: 例子中对应的黑板就是redo log;redo log 属于引擎层日志,黑板(redo log)是有固定大小的,可以配置一组为4个文件,每个文件的大小是1GB,那么总可以记录4GB操作,写到末位又从头写,如果写完了,那么就不得不停下来先持久化了;redo log其实是用两个指针维护的,一个记录当前写位置,一个记录当前擦除位置;

Bin Log:而另外Service层也有日志,叫binlog (归档日志); 这两个区别是:

redo log是innoDB持有的,binlog是MySql Server层实现的,所有引擎都可以使用。

redo log是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑;

redo log是循环写的,空间固定会用完,binlog是追加写入,并不会覆盖以前日志;

两阶段提交:我们做的误删备份恢复一般用Binlog,另外因为redo log和binlog是在不同的地方写,所以可能存在数据不一致的情况,而解决这个不一致的方法是 “两阶段提交”;先写redo log,记为prepare状态,然后写binlog,最后再提交事物,让redo log成为commit状态;

这个两阶段提交是否可靠,在于程序在哪里crash,如果在写完binlog之后crash,那么下次程序启动必然先检查最早的事务,发现一个binlog对应的redolog还在prepare,就会把redolog状态改为commit,所以有了规则和状态的结合。

页结构:

页头:记录页面的控制信息,共56个字节,包括页的左右兄弟页面指针、页面空间使用情况等

虚记录:最大虚记录、最小虚记录

记录堆:行记录存储区,分为有效存储区和已删除存储区

自由空间链表:已删除记录组成的链表

未分配空间:还没分配的空间

Slot区:用作快速查找记录,类似跳表

页尾:存储页面校验信息

数据结构:

B+数,核心是二分查找

索引:

索引就是目录,方便你快速找到记录,实现索引的方式有很多种,常见的有有序数组,哈希表,二叉树;

有序数组(二分法查找)当然能支持数据库查询,而且适合范围查询,但比较麻烦的是,如果要插入的时候,就需要移动数据,代价比较大,哈希表又不支持有序查找,那么二叉树呢?当然也有适合和不适合的情况,那怎么办?不管是哈希还是有序数组,或者N叉树,它们都是不断迭代、不断优化的产物或者解决方案。数据库发展到今天,跳表、LSM树等数据结构也被用于引擎设计中;InnoDB就是B+树;

B+树特点,可以多很好支持索引的N叉存储,而且让叶子结点链接起来,使得可以支持范围查询;

mysql> create tableT(
idint primary key,
kint not null,
namevarchar(16),index (k)
)
engine=InnoDB;

根据以上表格,索引和存储如下所示:

MySQL技术内幕 第5版 mysql技术是_数据

可以看出,这是两种不同的索引,带记录R的的称之为主键索引,也称聚簇cu索引,其他的均是非聚簇索引;

二级索引:主键索引外的索引都是二级索引,索引的data是主键,一次查询至少走两次索引,第二次是走主键索引,我们称之为回表操作

联合索引:两个按键组合的索引,一个索引只建一个树所以联合索引也是一棵树,使用的是前缀索引和最左匹配原则,所以就出现了可以根据查询语句优化出一个最左匹配原则,减少回表操作;

前缀索引:前缀索引如果不是按照从左往右开始查找,无法使用索引,而且不能跳过中间裂,范围查询后面的列不能使用索引

最佳实践:自增主键效率高,每次查询走两次索引,随机主键容易页分离移动,如果可以使用业务主键,写入查询磁盘利用率高,每次查询走一次索引,尽量少使用联合索引

优化点:

尽量使用自增主键,因为这样可以减少插入的页分裂,自增主键基本不需要过多页移动操作;而业务逻辑字段做主键写数据成本较高。

自增主键长度较小,使得页可以装更多的索引,而业务主键数据长度较大,索引也大,占用空间;

内存管理:

BufferPool

Page

FreeList

FlushList

PageHash

LRU

事务机制:

事务特征:A 原子性,C 一致性,I 隔离性,D 持久性

隔离性:在一个数据库中有多个事务的时候,就有可能会出现脏读、不可重复读、幻读问题,为了解决这些问题,就有了隔离级别的概念;

隔离级别:Read Uncommited 读未提交 ; Read Commited 读提交; Repeated Read 可重复读;Serializable 串行化;

在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在“可重复读”隔离级别下,这个试图是在事务启动时创建的,整个事务存在期间都会用这个视图。在读提交的隔离级别下,这个视图是在每个SQL语句开始执行的时候创建的;“读未提交”则不存在视图的概念,直接返回最新值;“串行化”就直接加锁避免并发访问。

隔离的实现:MVCC,在MySql中,实际上,每条记录在更新的时候都会同时记录一条回滚操作;记录上的最新值,通过回滚操作,都可以得到前一个甚至几个变化前状态的值,有了这些回滚操作,我们就可以在每个时刻都记录当前的read-view,于是一个数据便可以对不同时间点有多个不同版本,每个版本可以通过执行回滚操作回到对应的read-view的值。所以要实现“Repeated Read”的每个事务的视图,只需记录当前时刻的所有read-view即可,这就是多版本并发控制;(所以这里我们不建议使用长事务,这样会导致MySql资源一直不释放);

优化点:不要使用长事务;