《MySQL技术内幕——InnoDB存储引擎》

MySQL体系结构和存储引擎

1.1 定义数据库和实例

数据库:物理操作系统文件或其他形式文件类型的集合。
实例:MYSQL数据库由一个后台线程以及一个共享内存区构成。

1.2 MySQL体系架构

在这里插入图片描述
连接者:不同语言的代码程序和mysql的交互(SQL交互)
1、连接池 管理、缓冲用户的连接,线程处理等需要缓存的需求
2、管理服务和工具组件 系统管理和控制工具,例如备份恢复、Mysql复制、集群等
3、sql接口 接受用户的SQL命令,并且返回用户需要查询的结果
4、查询解析器 SQL命令传递到解析器的时候会被解析器验证和解析(权限、语法结构)
5、查询优化器 SQL语句在查询之前会使用查询优化器对查询进行优化
select id,name from user where age = 40;
a、这个select 查询先根据where 语句进行选取,而不是先将表全部查询出来以后再进行age过滤
b、这个select查询先根据id和name进行属性投影,而不是将属性全部取出以后再进行过滤
c、将这两个查询条件联接起来生成最终查询结果
6、缓存 如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据
7、插入式存储引擎 存储引擎说白了就是如何管理操作数据(存储数据、如何更新、查询数据等)的一种方法。因为在关系数据库
中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操作此表的类型)

1.3 MySQL存储引擎

show ENGINES 可以查看支持哪些引擎。
在这里插入图片描述

InnoDB存储引擎概述

2.1 InnoDB存储引擎概述

MySQL5.5版本开始的默认存储引擎。
其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读。

2.2 InnoDB存储引擎的版本

2.3 InnoDB体系架构

在这里插入图片描述

2.3.1 后台线程

InnoDB存储引擎是多线程模型。
1.Master Thread
负责将缓存池中的数据异步刷新到磁盘。
2.IO Thread
用来处理AIO的回调。
3.Purge Thread
用来回收已经使用并分配的undo页。
4.Page Cleaner Thread
脏页刷新操作。

2.3.2 内存

1.缓冲池
缓冲池就是一款内存区域,通过内存的速度来弥补磁盘较慢对数据库性能的影响。

在这里插入图片描述
2.LRU List、Free List 和Flush List。
3.重做缓存日志。
首先将重做日志放到这个缓存区中,然后按一定频率将其刷新到重做日志文件中。

2.4 CheckPoint技术

CheckPoint主要作用:缩短数据库恢复时间、缓冲池不够用时,将脏页刷新到磁盘、重做日志不可用时,刷新到磁盘。

2.5 Master Thread工作方式

2.6 InnoDB的关键特性

2.6.1 插入缓冲

1.Insert Buffer
对于非聚集性索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断要插入的非聚集性索引是都在缓冲池中,如果存在,则直接插入,如果不存在则放入到一个Insert Buffer对象中。
2.Change Buffer

2.6.2 两次写

数据库宕机时,可能正在写入某个页到表中,如果这个页没有写完成,则会丢失数据。
doublewrite

2.6.3 自适应哈希索引

存储引擎会自动根据访问的频率和模式来自动地为某些热点数据建立哈希索引。

2.6.4 异步IO

异步IO可以同时发起多个IO操作,还可以进行IO merage。

2.6.5 刷新邻接页

当刷新一个脏页时,存储引擎会检测改页所在区是否未脏页,然后一起进行刷新操作。

文件

3.1 参数文件

数据库参数可以看成键值对。包括动态参数和静态参数。

3.2 日志文件

  • 错误日志 error log
  • 二进制日志 binlog
  • 慢查询日志 show query log
  • 查询日志

3.2.1 错误日志

错误日志对MySQL的启动、运行、关闭过程进行了记录。

3.2.2 慢查询日志

通过慢查询日志可以找出有问题额SQL 语句,对其进行优化。

3.2.3 查询日志

查询日志记录了所有对数据库请求的信息。

3.2.4 二进制文件

二进制文件记录了所有对MySQL数据库执行更改的所有操作。
二进制日志主要有以下作用:

  • 恢复
  • 复制
  • 审计

3.3 套接字文件

UNIX下可以通过套接字方式连接,因此需要一个套接字文件。

3.4 pid文件

当MYSQL实例启动时,会将自己的进程写入一个文件——该文件以.pid结尾。

3.5 表结构定义文件

.firm文件用来存储表结构定义文件和视图。

3.6 InnoDB存储引擎文件

表就是关于特定实体额数据集合。

4.1 索引组织表

在 InnoDB存储引擎中,表就是关于特定实体的数据集合。表都是根据主键顺序组织存放的。这种存储方式成为索引组织表。

在 InnoDB存储引擎表中,每张表都是有主键的,如果没有主键则会按照如下方式选择或者创建主键:

  • 首先判断表是否有唯一非空索引,如果有,则该列为主键。
  • 如果不符合,则创建一个6字节的指针。
    如果有多个非空唯一索引时,则按照创建顺序现在第一个列作为主键。

4.2 InnoDB逻辑存储结构

从InnoDB存储引擎的逻辑存储结构来看,所有数据都被逻辑地存放在一个空间中,称为表空间。
表空间又由段(segment)、区(extent)、页(page)组成。
在这里插入图片描述

4.2.1 表空间

表空间存放了所有数据,每个表可以有自己的表空间,表空间的大小可以不断增长。

4.2.2 段

表空间是由各个段组成的,常见的有数据段、索引段、回滚段等。
前面介绍过InnoDB引擎表是索引组织的,因此数据即索引,索引即数据。数据段中为B+树的叶子节点,索引段中为B+树的非索引节点。

4.2.3 区

区是由连续的页组成的空间。在任何情况下,每个区的大小都为1MB。在默认情况下,InnoDB存储引擎的页大小为16kb,即一个区有64个页。

4.2.4 页

页的大小固定默认16kb,可以通过innodb_page_size修改页的大小。

4.2.5 行

4.6 约束

4.6.1 数据完整性

关系型数据库能保证自身存储数据的完整性,不需要应用程序的控制。
主要形式有:

  • 选择一个合适的实际类型确保一个数值满足特定的条件。
  • 外键约束
  • 触发器
  • Default约束

4.6.2 约束的创建和查找

表建立时就进行定义
利用ALERT TABLE命令来创建约束

4.6.3 约束和索引的区别

约束是一个逻辑概念,用来保证数据完整性。
索引是一个数据结构,既有逻辑上的概念,又有物理表现形式。

4.6.4 对错误数据的约束

通过sql_mode来设置对输入值的约束。

4.6.5 ENUM和SET约束

sex EMUM(‘male’,‘female’) 用来约束性别只能是male或female。统一需要设置sql_mode。

4.6.6 触发器与约束

触发器的作用是在执行INSERT、DELETE、UPDATE命令之前或之后自动调用sql命令或者存储过程。

4.6.7 外键约束

CREATE TABLE parent(
id INT NOT NULL,
PRIMARY KEY(id)
)ENGINE=INNODB

CREATE TABLE child(
id INT NOT NULL,
parent_id INT,
FOREIGN KEY(parent_id) REFERENCES parent(id)
)ENGINE=INNODB

4.7 视图

在MySQL数据库中,视图是一个命名的虚表,它由一个sql查询来定义,可以当做表使用。视图中的数据没有实际存储。

索引与算法

5.1 INNODB存储引擎索引概述

InnoDB存储引擎常见索引:

  • B+树索引
    B+树索引并不能找到给定键值的具体的行,而是找到数据行所在的页,然后把页读到内存,查找需要的数据。
  • 全文索引
  • 哈希索引
    InnoDB存储引擎的哈希索引是自适应的,会根据表的使用情况自动生成好像索引。

5.2 数据结构与算法

5.2.1二分查找法

也称折半查找法

5.2.2 二叉树和平衡二叉树

5.3 B+树

B+树是由B树和索引顺序访问方法演化而来。
在B+树中,所有记录节点都是按照键值的大小顺序存放在同一层的叶子节点上。

5.4 B+树索引

B+树索引就是B+树在数据库中的实现。
B+树索引可以分为聚集索引和辅助索引。不管是聚集索引还是辅助索引,其内部都是B+树实现,即高度平衡,叶子节点存放着所有数据。
聚集索引与辅助索引的不同是,叶子节点是否存放一整行数据。

5.4.1 聚集索引

聚集索引就是按照每张表的主键构造一颗B+树,同时叶子节点存放的是一整行的数据。每张表只能拥有一个聚簇索引。

由于定义了数据的逻辑顺序,聚簇索引能够特别快的访问针对范围值的查询。

聚集索引的另一个好处是对于主键的排序查找和范围查找速度非常快。

5.4.2 辅助索引

辅助索引叶子节点并不包含行记录的全部数据,辅助索引存储的页签存储的是聚集索引键值。
辅助索引的书签就是相应行数据的聚集索引键。

当通过辅助索引来查找数据时,InnoDB存储引擎会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后通过主键索引找到一个完整的行记录。

在这里插入图片描述

5.4.3 B+树索引的分裂

nnoDB存储引擎的Page Header中有几个部分来保存插入的顺序信息:
PAGE_LAST_INSERT
PAGE_DIRECTION
PAGE_N_DIRECTION
通过这些信息,可以决定是向左还是向右分裂,同时决定将分裂点记录为哪一个。

5.4.3 B+树索引的管理

  1. 索引的管理
    索引的创建和删除有两种方法。一种是ALERT语句,一种是CREATE/DROP INDEX。
    用户想要查看索引,可以使用SHOW INDEX。
  2. Fast Index Creation

5.5 Cardinality值

并不是在所有的查询条件中出现的列都需要添加索引。
怎么查看索引具有高选择性呢?

5.6 B+树索引的使用

5.6.2 联合索引

联合索引指对表上多列进行索引。
在这里插入图片描述

联合索引是有序的。就上面的例子来说,数据按照(a,b)的顺序进行存放,即(1,1)、(1,2)、(2,1)、(2,4)、(3,1)、(3,2)。
select * from t where a = xxx and b = xxxx; select * from t where a =
xxx; (a,b)、(a)都是有序的,都可以使用到联合索引。 select * from t where b =
xxx;因为b不是排序的所以无法使用到索引。

CREATE TABLE t(
a INT NOT NULL,
b INT,
FOREIGN KEY(a)
KEY index1(a,b)
)ENGINE=INNODB

联合索引的第二个好处是已经对第二个键值进行了排序处理。下列语句是可以用到索引的:
select * from t where a = xxx and order by b;

5.6.3 覆盖索引

即从辅助索引中就可以查询得到的记录,而不需要查询聚集索引中的记录。

CREATE TABLE buy_log(
	user_id INT NOT NULL,
	buy_date TIMESTAMP
)
ALTER TABLE buy_log ADD KEY(user_id,buy_date)

在通常条件下,诸如(a,b)的联合索引,一般是不可用选择b列中的查询条件的,但是对于统计操作,并且是覆盖索引,可以使用:

SELECT COUNT(*) FROM buy_log WHERE buy_date > "2011-01-01" AND buy_date < "2012-01-01"

5.6.4 优化器选择不使用索引的情况

某些情况下,EXPLIAIN查看执行计划时,发现优化器没有选择索引去查找数据,而是通过扫描聚集索引。这种情况下多发生于范围扫描、JOIN连接操作。

5.6.5 索引提示

用户指定某个索引可以使用FORCE INDEX。

5.6.4 Multi-Range Read 优化

Multi-Range Read 优化的目的是为了减少磁盘的随机访问,并且将随机访问转换为较为顺序的访问。

5.7 哈希算法

哈希算法是一种常见的算法,时间复杂度为O(1);

6.1 什么是锁

锁机制用于管理对共享资源的并发访问。

6.2 lock 与 latch

latch一般称为闩锁,其要求持续时间短,模式主要为互斥量和读写锁。其目的用来保护并发线程操作临界资源的准确性,并且通常没有死锁检测机制。
lock的对象是事务,用来锁定的是数据库中的对象,入表、页、行。有死锁检测机制。
在这里插入图片描述
通过 show engine innodb mutex 查看mutex锁

6.3 INNODB引擎中的锁

6.3.1 锁的类型

INNODB存储引擎实现了如下两种标准的行级锁

  • 共享锁(S LOCK):允许事务读取一行的数据。
  • 排它锁(X LOCK):允许事务删除或更新一行数据。
    在这里插入图片描述
    共享锁之间锁兼容,其他之间不兼容。

6.3.2 一致性非锁定读

一致性非锁定读是指数据库通过多版本控制的方式来读取当前执行时间数据库中的行数据。如果读取的行正在执行DELETE或UPDATE操作,这时读操作不会等待行上的锁释放,而是去读取行的快照数据。
之所以成为非锁定读,因为不需要等待访问行上X锁释放。快照数据是指该行之前版本的数据,是通过undo段来完成。
在这里插入图片描述

一行记录的快照可能不只有一个快照数据,一般称这种技术为多版本并发技术,MVCC多版本并发控制。
READ COMMITED 隔离级别下读取到的是被锁定行的最新一份快照数据。
REPATETABLE READ 隔离级别下总是读取事务开始时的数据版本。

在这里插入图片描述
READ COMMITED 隔离级别下读取到的总是最新的数据,所以是空。
REPATETABLE READ 隔离级别下总是读取事务开始时的数据版本,读取结果是 id=1的数据。

所谓乐观锁

6.3.3 一致性锁定读

在REPATETABLE READ模型下,InnoDB的SELECT操作使用一致性非锁定读。在某些情况下,需要对数据库读取操作加锁以保证数据逻辑一致性。
InnoDB提供两种一致性锁定读操作

SELECT ... FOR UPDATE

SELECT … FOR UPDATE对读取的行加一个X锁,其他事物不能对已锁定的行加任何锁。

SELECT ... LOCK IN SHARE MODE

SELECT … LOCK IN SHARE MODE 对读取的记录加一个S锁,其他事物可以向被锁定的行加S锁,但是加X锁会被阻塞。

所谓悲观锁

6.3.4 自增长与锁

每个自增长的值的表都一个自增长计数器,当我们对含有自增长的计时器的表进行插入操作时,这个计时器会被初始化
这其实是一个自增长锁,为了提高插入性能,锁在一个sql语句执行完就释放,而不是整个事务结束。
为了提高插入的性能 引入了轻量级互斥量的自增长机制。提了一个参数 innodb_autoinc_lock_mode来控制自增长的模式,该参数的默认值是1

6.4 锁的算法

6.4.1 行锁有三种算法

  • Record Lock 单行记录上的锁
    Record Lock 总是锁住索引记录,如果没有设置任何索引,innodb会设置隐式的隐式主键来锁定记录
  • Gap Lock 间隙锁,锁定一个范围,但不包含记录本身
  • Next-Key Lock Record Lock+Gap Lock ,锁定一个范围,并锁定记录本身
    当查询的索引含有唯一属性时,会降级为 Record Lock,即仅锁住索引本身。

6.4.2 解决Phantom Problem(幻像问题)

Phantom Problem指在同一事务下连续执行两次相同的sql语句,可能导致不同的结果,第二次SQL语句可能返回之前不存在的行。
在READ REPEATABLE下, Next-Key Lock机制可以避免Phantom Problem问题。

6.5 锁问题

锁会带来三种问题

6.5.1 脏读

脏读就是再不同的事务下,一个事务可以读到另一个事务未提交的数据,违反了数据库的隔离性。

脏读发生的条件是READ UNCOMMITED

6.5.2 不可重复读(幻读)

一个事务内两次读取到的数据不一致的情况为不可重复读。违反了数据库一致性的要求。
不可重复读与脏读的区别是:脏读读到的是未提交的数据,不可重复读读到的是已经提交的数据。
不可重复读示例:
在这里插入图片描述
在Next-Lock算法下,对索引的扫描,不仅锁住扫描到的索引,还会锁住这些索引覆盖的范围,因此在这个锁住的范围内插入是不允许的。避免了另外的事务在范围内插入导致的不可重复读问题。
因此InnoDB默认事务隔离级别是READ REPEATABLE。

6.5.3 丢失更新

丢失更新是另一个锁导致的问题,简单来说就是其中一个事务的操作会被另一个事务所覆盖,从而导致不一致的问题。

 例如:
 1.事务t1将行记录r更改为v1,但是事务t1未提交。
 2.与此同时,t2将行记录r改完t2,t2未提交。
 3.事务t1提交。
 4.事务t2提交。

在当前事务的隔离级别中,DML操作会被加上行锁或其他对象级别的锁,在步骤2中t2会被阻塞,直到t1提交。
在生成应用中还会产生逻辑意义上的丢失问题。

 1.事务t1查询一行数据并防止本地内存中,展示给用户user1。
 2.事务t2也查询到此行数据并展示给用户user2。
 3.用户user1修改这行记录,并更新数据库提交。
 4.用户user2修改这行记录,并更新数据库提交。

在这个过程中,user1的操作丢失了。要避免这种情况发生,需要让事务在这种情况下的操作串行化,而不是并行操作。
在这里插入图片描述
如果用户查询之后,进行一些额外的操作,再进行update就可能会出现丢失更新的情况。

6.6 阻塞

因为不同锁之间的兼容性关系,在有些时刻一个事务中的锁需要等待另一个事务中的锁来是否它所占的资源,这就是阻塞。

6.7 死锁

6.7.1 死锁的概念

死锁指的是两个或者两个以上的事务在执行过程中,因争夺锁资源而照成的一种互相等待的现象。

6.7.3 死锁的实例

在这里插入图片描述
A和B资源互相等待的时候会产生死锁,简称AB-BA锁。
在这里插入图片描述
A对4持有了X锁,B在请求中获得了4的S锁,在A进行插入时会导致死锁发生。

6.8 锁升级

锁升级是指将当前锁的粒度降低。
InnoDB引擎不存在锁升级的问题。因为其不是根据每个记录来产生行锁的,相反,根据事务访问的页来进行锁管理,采用的是位图的方式。

事务

ACID原则。

7.1 认识事务

7.1.1 概述

  • A(Atomicity)
    要保持原子性,要么都做,要么什么都不做。
  • C(Consistency)
    一致性是指从一种状态转移到另一种状态时一致的状态。
  • I(Isolation)
    隔离性要求事务提交前对其他事务都不可以见。
  • D(Durability)
    事务一旦提交,其结果就是永久性的。

7.1.2 分类

  • 扁平事务
  • 带有保存点的 扁平事务
  • 链事务
  • 嵌套事务
  • 分布式事务

7.2 事务的实现

7.2.1 redo

重做日志用来实现事务的持久性,即D。其由两部分组成:一是内存中的重做日志缓存,其是易失的;二是重做日志文件,其是持久的。
redo log用来保证事务的持久性,undo log用来帮助事务回滚以及MVCC的功能。

与binlog的区别

  • 重做日志是在InnoDB引擎产生的,二进制日志是在mysql上层产生,任何引擎都会产生二进制日志。
  • 其次二进制的日志进记录的是逻辑日志,记录的是对应的sql;重做日志是物理格式的日志,其记录的是对于每一个页的修改。
  • 二进制日志只在事务提交完成后进行一次写入。重做日志是在事务进行中不断写入。
    重做日志是幂等的,而二进制日志不一定是幂等的,比如有INSERT操作。

7.2.2 undo

重做日志记录了日志的行为,可以很好地通过其对页进行“重做”操作。
但是事务有时还需要进行回滚操作,这时就需要undo。
undo是逻辑日志,实际上做的是与先前相反的动作。对应每个INSETR,执行DELETE操作。
除了回滚操作,undo的另一个作用是MVCC。

7.6 事务的隔离级别

  • 读未提交
  • 读已提交
  • 可重复读
  • 序列化

7.7 分布式事务

7.7.1 mysql分布式事务

分布式事务指允许多个独立的事务资源参与到一个全局的事务中。全局事务要求所有参与的事务要么全部提交,要么全部失败。

XA事务允许不同数据库之间的分布式事务。
XA事务由一个或多个资源管理器、一个事务管理器、以及一个应用程序组成。
分布式事务使用两阶段提交方式。
第一阶段,所有参与全局事务的节点都开始准备,告诉事务管理器准备好提交了。
第二阶段,事务管理器告诉资源管理器执行ROLLBACk还是COMMIT。如果任何一个节点显示不能提交,那么所有节被告知需要回滚。
MYSQL XA事务语法:
XA START ‘a’;
INSERT INTO z SELECT 11;
XA END ‘a’;
XA PREPARE ‘a’;
XA RECOVER;
XA COMMIT;

7.7.2 内部分布式事务

在这里插入图片描述
最常见的XA存在于binlog和InnoDB之间。如果执行完1,2,在执行3时发生了宕机,就会造成主从不同步。

7.8 不好的事务习惯

7.8.1 在循环中提交

每一次提交都要写重做日志,循环提交效率会很慢

7.8.2 使用自动提交

mysql模型使用自动提交.开发人员应该意思到自动提交的问题。

7.8.3 使用自动回滚

对应开发人员,重要的不仅要知道发生了错误,还要知道发生了什么错误,自动回滚存在这样的问题。

7.9 使用长事务

长事务就是执行时间较长的事务。长事务的回滚代码不可接受,可以拆分为小批量事务来完成。

备份与恢复

8.1 备份与恢复概述

按照不同的备份方法分为:

  • 热备
  • 冷备
  • 温备
    按照备份后的文件内容,又分为:
  • 逻辑备份
  • 裸文件备份

裸文件备份有分为:

  • 完全备份
  • 增量备份
  • 日志备份

8.2 冷备

只需要定期备份备份mysql的文件。

8.3 逻辑备份

8.3.1 mysqldump

8.3.2 select into outfile

8.7 复制

8.7.1 复制的工作原理

复制是mysql提供的一种高可用高性能解决方案。可用分为三个步骤
1.主服务器把数据更改记录到二进制日志中
2…从服务器把主服务器的二进制日志复制到自己的中继日志中。
3.从服务器重做中继日志,把更改应用到自己的服务器上,以达到数据的最终一致性。
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章