13. InnoDB引擎底层原理及Mysql 8.0 新增特性详解

MySQL性能调优

  • 一、InnoDB引擎底层原理
    • 1. 深入理解Redolog日志底层原理
      • 1.1 innodb引擎底层事务原理
        • 1.1.1 WAL
    • 2. redolog日志文件
      • 2.1 为什么要redolog日志文件
      • 2.2 redolog的内部结构
      • 2.3 redolog的刷盘时机
      • 2.4 Log Sequence Number
      • 2.5 innodb_flush_log_at_trx_commit
    • 3. undolog日志
      • 3.1 undolog回滚的方式
      • 3.2 undolog事务id形成机制
      • 3.3 插入操作
        • 3.4 删除操作
      • 3.5 更新操作
        • 3.5.1 不更新主键
        • 3.5.2 更新主键
    • 4. 事务的整体执行流程
    • 5. redo log和bin log的关系
      • 5.1,为什么用redo log恢复数据而不用bin log
      • 5.2,binlog和redolog如何保证数据的一致性
  • 二、MySQL 8.0新增特性
    • 1. 索引
      • 1.1 隐藏索引
      • 1.2 降序索引
      • 1.3 不再对group by操作进行隐式排序
      • 1.4 索引中可以使用函数表达式
    • 2. 系统方面
      • 2.1 原子ddl操作
      • 2.2 配置参数
        • 2.2.1 全局参数持久化
        • 2.2.2 支持会话级别动态调整部分参数
        • 2.2.3 默认参数的调整

本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。

01.redo日志的作用和格式

02.redo日志的写入过程

03.什么是Log Sequence Number?

04.innodb_flush_log_at_trx _commit辨析

05.什么是事务id和trx_id隐藏列

06.增删改的undo日志的格式辨析

07.事务提交的流程

08.事务恢复流程

09.面试题:同时写Redo和Binlog怎么保持一致?

10.MySQL8的增强特性

事务的原子性是通过undo日志来实现的

事务的隔离性是通过MVCC+读写锁实现的·

事务的持久性是通过redo日志来实现的

事务的ACID地位是一样重要的吗?

在这个里面C是最重要的,一致性是目的。AID原子性、隔离性和持久性是手段。事务只有实现了AID才可能实现C一致性。

一、InnoDB引擎底层原理

1. 深入理解Redolog日志底层原理

1.1 innodb引擎底层事务原理

事务的四大特性主要是是ACID,分别是原子性、一致性、隔离性和持久性。

其原子性是 通过这个undolog 来保证的,

持久性是 通过redolog 来实现的,

隔离性是通过事务的 读写锁+mvcc机制 来实现的。

在这四大特性中,一致性C是最重要的,他是最终目的,只有其他三个实现了,才有可能实现这个一致性,即 一致性是最终目的,其他三个就是实现的手段 。

1.1.1 WAL

在事务的具体实现的机制中,mysql是通过这个WAL(Write-ahead logging),即预写日志的方式来实现的。

举个基本的例子,假设一个转钱的操作,如果A给B转钱,此时A减了100,而在B在加100的时候,突然断电了,那么在数据库重启的时候,就得恢复他断电前的状态,此时就需要一个日志来记录他事务操作前的状态了,通过这个日志中的记录来恢复之前的状态,判断其是否需要回滚操作,还是需要重做等操作。而实现这个WBL机制,主要是通过这个redo log和undo log这两种日志的方式实现

mysql中除了使用这种主流的WAL机制之外,还会使用一些Commit Logging、Shadow Paging等。mysql默认使用这个WAL机制。

2. redolog日志文件

2.1 为什么要redolog日志文件

依旧拿一个更新语句举例,如下图,redolog是发生在第4步和第5步,在数据在buffer pool中进行数据更新之后,会把具体的逻辑语句,如哪一页哪一行要更新的字段以及更新的值是啥,先加入到redolog buffer中,随后再然后通过追加的方式持久化到磁盘上的redolog文件中,再不考虑server层的操作,即跳过6,7步,那么就会启动一个后台线程进行一个落盘的操作。

13. InnoDB引擎底层原理及Mysql 8.0 新增特性详解_第1张图片

这里假设innodb内部没有这个redo log这个日志文件,就是在数据更新之后直接从buffer pool将数据给落盘,如果在落盘的时候数据页没有发生损坏,那么双写机制就触发不了,就没有换页的操作,此时发生断电,那么还没来的及落盘的数据就会直接丢失了,这就会影响数据的安全性和持久化

除了断电这个问题,还有可能因为一个小小的更新语句,就直接将整页数据给进行落盘操作,这样就很浪费成本;或者遇到一个sql很复杂,需要涉及的表很多,并且数据量很大,那么就需要更改很多页目录的数据,如果这些页目录在磁盘上的分布是随机的,那么在刷盘写数据的时候,就会产生大量的随机io,因此也很影响内部的执行效率和浪费空间。

所以为了解决数据的安全性以及innodb的执行效率,就引入了这个redolog日志文件,就是在落盘时先写一份到redolog中,并且写数据是顺序写,在redolog写成功之后,再进行落盘操作,如果出现断电问题,就用这个redolog这个文件进行数据的重做。

2.2 redolog的内部结构

redolog主要是记录了哪些数据做了什么修改,因此这个redolog有一个通用的格式,如下图:

在这里插入图片描述

key value
type 就是值数据的类型,类型总共有53种
Space ID 表空间id,即修改的对应表空间的id
page Number 当前页的页号
data 要修改的内容

2.3 redolog的刷盘时机

在innodb的体系结构中,可以发现在左边的内存结构中是有一个Log Buffer的缓冲区的,该缓冲区就是对应的redolog缓冲区,在该缓冲区中,其默认大小为16M,并且内部分成了多个小块,每一块的大小空间为512kb。在数据写入这个redolog的缓冲区的时候,会从头到尾的以追加的方式将数据写入这些块中,如果后面的块快写满,那么会将前面头部的块的数据删掉,然后再从头到尾开始存储数据,磁盘上面的redolog页结构和这个一样。

13. InnoDB引擎底层原理及Mysql 8.0 新增特性详解_第2张图片

而这个缓冲区刷盘的情况有好几种,如下:

  1. log buffer空间不足时,虽然说redolog缓冲区数据存满会将前面的数据删除再重新存储,但是删除之前也是要考虑数据的持久化的,innodb内部认为,当数据量达到redolog缓冲区空间的一半大小时,就需要强制的进行一次刷盘操作,从而把日志落盘到磁盘上。

  2. 事务提交时,在事务提交时,也是需要将缓冲区中对应的那些日志数据刷新到磁盘的

  3. 后台默认线程,后台也是会启动一个默认的线程进行一个刷盘操作的,如每一秒刷新一次

  4. 服务器手动关闭,在服务器关闭时,也是需要将未刷盘的数据全部的进行一次刷盘操作。

2.4 Log Sequence Number

简称LSN,顾名思义,这个就是一个日志序列号。和undolog一样,在执行一条更新或者插入语句的时候,就会生成一个事务id,这里的序列号和undolog的事务id的作用是一样的,主要是是保证数据的唯一性。该值是一个自增的序列号,初始大小为8704,后面每增加一条数据,其对应的序列号的值就会加1。即LSN的值越小,那么其事务产生的越早。

除了这个日志之外,innodb内部还有其他的LSN用来记录已经刷盘的事务id。可以通过具体的命令来查看系统中各种LSN的值

SHOW ENGINE INNODB STATUS\G

13. InnoDB引擎底层原理及Mysql 8.0 新增特性详解_第3张图片

key value
Log sequence number 已经写入redolog buffer的日志量(累加值)
Log flushed up to 已经写入磁盘的redo日志量

其中:

Log sequence number: 代表系统中的 Isn值,也就是当前系统已经写入的redo日志量,包括写入log buffer中的日志。

Log flushed up to: 代表flushed_to_disk_Isn的值,也就是当前系统已经写入磁盘的redo日志量。

Pages flushed up to: 代表flush链表中被最早修改的那个页面对应的

2.5 innodb_flush_log_at_trx_commit

在数据进行刷盘的时候,数据也不是直接的从redolog缓冲区将数据直接刷到磁盘内部,而是需要调用操作系统,通过操作系统进行一个落盘的操作。在操作系统中,会有一个操作系统的缓冲区,数据会先在缓冲区中存放,然后再通过操作系统的调度将数据进行一个落盘的操作。

这个系统变量的名称就是innodb_flush_log_at_trx_commit,可以提供给外部调用,如kafka,mysql都对这个变量进行了调用。这个变量总共有三个参数,分别是0、1、2,不同的参数代表着不同的含义:

该值为0时,表示在事务提交时不立即向磁盘中同步redo日志,这个任务是交给后台线程做的。

该值为1时,表示在事务提交时需要将redo日志同步到磁盘,可以保证事务的持久性,这个是mysql设置的系统默认值,当事务提交时,会进行一个强制刷盘的操作。

该值为2时,表示在事务提交时需要将redo日志写到操作系统的缓冲区中,但并不需要保证将日志真正的刷新到磁盘。如果数据还在操作系统缓存中突然断电,那么未落盘的数据也会丢失。

13. InnoDB引擎底层原理及Mysql 8.0 新增特性详解_第4张图片

3. undolog日志

上面主要了解redolog日志,redolog主要是为了保证这个数据的持久性,ACID中的D就是靠这个redolog来保证的。而接下来要讨论的undolog日志,undolog日志主要是为了保证数据的原子性,在mvcc那篇https://blog.csdn.net/zhenghuishengq/article/details/127889365,中也详细的分析了这个undolog,也详细的描述了undolog日志版本链路和readview结合来保证事务的隔离性,接下来再对这个undolog分析一波。

3.1 undolog回滚的方式

undolog日志中主要是记录被修改或者新增的值的记录,当需要回滚时,则将这个存储undolog的值用来回滚。其回滚情况主要有如下几种:

  1. 当插入一条记录时,需要把这条记录的主键值记下来,回滚的时候只需要把这个主键值对应的记录删掉。

  2. 当删除了一条记录,需要要把这条记录中的内容都记下来,回滚时再把由这些内容组成的记录插入到表中。

  3. 当修改了一条记录,需要要把修改这条记录前的旧值都记录下来,回滚时再把这条记录更新为旧值。

3.2 undolog事务id形成机制

在mysql中,主要有只读事务和读写事务。在只读事务中,普通的表是不能进行增删改操作的,而临时的表是可以进行增删改的;而读写事务中,是都可以进行增删改操作的。同时在只读事务中,由于临时表可以进行增删改,因此只读事务的临时表中也是可以产生这个事务id的。

undolog产生的事务id和这个LSN这个序列号是一样的,也是一个自增的全局变量id,在innodb中,每一行数据都会有一个存储这个事务id的地方,在之前的行格式中也谈到过,这些隐藏聚簇索引id,事务id,回滚指针等都存储在这些行格式,中。如下图中的row_id,trx_id,roll_ptr等

13. InnoDB引擎底层原理及Mysql 8.0 新增特性详解_第5张图片

3.3 插入操作

在数据进行插入的操作时,如果此时表中有着二级索引,那么此时除了向主键索引所在的B+树中存放整行数据,还要向二级索引中存放对应的列以及主键id这个字段。如果出现数据回滚的情况,那么不仅要删除主键索引中的数据,也要删除二级索引中对应的数据,但是这个id即存在一级索引中,也存在二级索引中,因此undolog在记录这个插入操作的数据的时候,直接将该行数据的主键id值存放在这个undolog的版本链路中,如果出现回滚的话,是那么直接通过id删除,就能把数据全部回滚回去了。

3.4 删除操作

在数据进行删除时, 会需要用到行格式中的一个delete_mask 字段,这个字段在记录头信息里面,如果该字段为0,则表示没有删除,该字段为1,表示已经删除。

在innodb中,为了保证mvcc的多版本并发控制机制,redolog在删除时引入了一个中间状态,即在删除时先引用逻辑删除,将delete_mask这个字段将值变为1,随后在事务提交之后,再通过后台启动一个线程,通过轮询方式去收集这个字段为1的那些行数据,然后再将值给删除。

由于所有的数据都是通过双向链表的方式串在一起,因此在删除的时候,只需要将链表的前驱和后继修改即可,并且会将这个需要删除的数据,挂载这个Page Free页的上面。Page Free页面上的数据表示该空间上面的页面可以被重复利用。

3.5 更新操作

在update方面,就会相对的比新增和删除两种方式更加的麻烦一些,主要分为更新主键和不更新主键。

3.5.1 不更新主键

在不更新主键时,又分为就地更新和物理删除再插入。如果在对某个字段做更新时,其要更新的值的长度不变,那么就会直接选择就地更新,如原来的值为"张三",现在将值改为"李四",改变的值的长度不变,那么就不会影响整页数据的结构,其选择就地更新;如果更新值的长度和原来的长度不一样,如原来是一个"张三",现在变成了"张三丰",他的值很明显不一样,那么就会选择先删除旧记录,再插入新的记录。换句话说,就是看空间大小是否发生改变,不变则就地更新,变则先删除后插入。

3.5.2 更新主键

更新主键操作和删除操作有点类似,为了保证mvcc机制,也是通过记录头信息中的delete mask字段来作为一个中间状态,即将这个字段的值改为1,然后再事务提交之后再将这条数据删除。删除之后再新增一条数据,根据新的主键值在B+树中找到新的定位,然后将数据新增到此地。就是更新主键可以看成是两步操作,先delete操作,再insert操作,因此在更新主键时,就会产生两条undo日志。

4. 事务的整体执行流程

通过上面的redolog和undolog的可知,redolog是为了保证事务的持久性,他具有重做的功能,undolog是为了保证事务的原子性,他具有撤回的功能。因此在开启一个事务时,可以总结一下各个日志中的作用已经整个事务的执行流程。

13. InnoDB引擎底层原理及Mysql 8.0 新增特性详解_第6张图片

  1. 以一个更新语句为例,在开启事务之后,会将磁盘中要更新的数据以页的方式加载到buffer pool缓冲区中

  2. 在执行更新语句时,会通过undo log记录被修改的值。在innodb中,为了保证undolog日志本身的持久性,因此也会通过一个redo log日志来记录里面新增的记录,如undo log的版本控制链路新增一个结点时,redo log就会记录在这条链表的什么位置加入了哪个结点,记录完之后将记录加入到Redo log的缓冲区中,再通过一定的方式进行刷盘持久化,所以在整个事务的过程中,redo log比undo log先刷盘。随后将要改变的值加入到buffer pool中

  3. 在buffer pool中执行sql的更新,将要更新逻辑语句也加入到Redo log缓冲区中,此时也会有一个redo log刷盘操作。

  4. 在提交事务时,也会让对应的redo log日志记录进行一个刷盘操作。

  5. 随后undo log会进行刷盘操作,buffer pool中的数据也会通过后台的线程进行一个数据刷盘的操作。

  6. 在undo log日志和redo log日志,数据页全部刷盘完成之后,事务执行完毕。

5. redo log和bin log的关系

从下图来看,redo log是属于引擎层的,即属于innodb存储引擎特有的,而bin log是属于server层的,即所有的引擎层共有,innodb有,MyIsam也有。从整个流程来看,bin log和redo log都是对一份数据进行的存储,并且在存储过程中,需要通过事务标记来保证两个日志中记录的值一样。

bin log主要用于人工恢复数据的时候使用。是逻辑日志,记录的是这个sql语句的原始逻辑,也就是数据变化的业务逻辑,比如c=c+1;但是bin log并不能记录哪些数据可以落盘,哪些数据没有落盘。

比如第一条语句id = 2 c+1
都二条语句id = 2 c+1如果是bin log他是不知道这个c到底是加了几次的,但是redo log是可以的。

5.1,为什么用redo log恢复数据而不用bin log

  1. binlog 会记录表中所有更改操作,包括更新删除数据,更改表结构等等,主要用于人工恢复数据,如开了binlog删库也不用跑路;而redolog主要是mysql内部使用的,在数据库突然崩溃mysql内部会自动的通过这个redolog进行一个重做的操作。

  2. redolog是Innodb引擎层特有的,binlog是Server层实现的

  3. redolog记录的是物理日志,如在哪一页哪一行哪个字段做了什么修改,其效率更高;binlog就是原始的逻辑,和原来的sql差不多

  4. redolog默认大小为48M,其内存是有限的,因此其内部是通过循环写的方式去保存数据,并且其内部只记录为刷盘的数据,已刷盘的数据会自动的从这个redolog中删除;binlog采用的是追加写,所有的记录都会保存。而在恢复数据的时候只需要恢复这部分未刷盘的即可,不需要全部就行一个对比再操作。

  5. 如果再一个事务中出现两条相同的sql,如set age = age + 1 where id = 2,如果binlog一条刷盘成功一条刷盘失败,那么他是不能区分这两条是有没有全部刷盘成功或者失败的,换句话说就是由于已经有一条成功了,那么就不能保证是第一条成功了还是第二条成功了,因此不管是全部恢复还是全部不恢复,内部的数据肯定不对;而redolog就不一样了,只需要把redolog内部未刷盘的进行一个刷盘操作就好了。

5.2,binlog和redolog如何保证数据的一致性

mysql中主要使用2pc,两阶段提交来保证数据的一致性

第一阶段就是先做一个准备工作,开启一个事务提交器,让所有的资源准备好,然后会去收集所有的资源状态,当所有资源的状态都准备好了之后,

再进入第二阶段,发出一个commit的命令,所有的资源进行一个commit提交。

二、MySQL 8.0新增特性

1. 索引

1.1 隐藏索引

我们在看一个索引是不是有用的时候,我们可以建一个索引,我们运行有这个索引的时候时间使用情况,当我们把他删了之后时间的使用情况。这样就很麻烦,一直在增删索引。这个时候我们就可以创建一个隐藏索引。

就是这个索引是真实存在的,然后我只是把他隐藏起来了,到时候mysql不会使用它。其实在增删改修改数据的的时候是有变化的,如果没有变化说明这个索引是多余的。方便调优用的。

1.2 降序索引

8.0以后就可以声明desc降序排列,之前都是升序的。就是8.0之前即使你声明的是desc,他还是升序,不起作用,但是8.0以后就会真真正正的降序了。

1.3 不再对group by操作进行隐式排序

在8.0之前,就是缺省条件下都会对group by操作进行隐式排序,如果不想让其排序就需要使用order by null。

8.0以后就不会对group by操作进行隐式排序。

1.4 索引中可以使用函数表达式

在之前一个索引比如上面+1,这个索引就会失效,但是8.0不会。只要你创建的时候使用了这个函数,你查询的时候仍然使用这个函数,这个索引就不会失效。

2. 系统方面

2.1 原子ddl操作

drop t1,t2 如果t1存在,t2不存在。

在8.0之前他会提示你t2不存在,但是会把t1删除。

在8.0之前他会提示你t2不存在,但是不会把t1删除。

2.2 配置参数

2.2.1 全局参数持久化

MySQL8.0版本支持在线修改全局参数持久化,通过加上PERSIST关键字,以将调整持久化到新的配置文件中,再次里后 db 还可以应用到取剂的多效。

2.2.2 支持会话级别动态调整部分参数

有利于提升语句性能。

select /*+ SET_VAR(sort_buffer_size = 16M)*/ id from test order id ;
insert /*+ SET_VAR(foreign_key_checks=OFF)*/ into test(name) values(1);
2.2.3 默认参数的调整

调整back_log的默认值,保持和 max_connections 一致,增强突发流量带来的连接处理容量。

修改event_scheduler 默认为ON,之前默认是关闭的。

调整max_allowed_packet的默认值,从4M增加到64M

调整bin_log,log_slave_updates 默认值为on。

你可能感兴趣的:(性能调优MySQL,mysql,java,数据库)