目录
buffer pool
change Buffer
Log Buffer
redo log
随机IO/顺序IO
redo log刷盘时机
redo logt特点
redo log结构
Adaptive Hash Index
磁盘区域
undo log
总结更新流程
BInlog
基于上一章sql执行原理基础上,我们来深入探讨sql更新的整个原理。
buffer pool
那么buffer pool他又可以分为几块。
让我们看到他除了有一个buffer pool 之外,他还有一个Change Buffer 和侧边的自适应的hash index(hash索引):就是意味着他把一部分的索引也放到了内存里面来了。
然后,我们还可以看到,内存里面,还有一个Log buffer。
那么我么来看看,他们到底是干什么的呢?
Buffer Pool:他缓存的是数据信息。包括我们的数据本身和索引,因为索引本身也是一种数据,所以我们说磁盘跟内存交互的最小的单位叫做页。所以它里面存的就是数据页,data page和索引页 index page。
那么我们怎么知道buffer pool他是怎样的一个存在的状态呢?
我们可以通过show status 查看服务器状态的命令。
Buffer pool他默认的大小是128兆。也就意味找他占用128兆的内存。当然这个值是可以调整的。
如果说,我们把我们要用的或者要写的数据放到内存里面的话,又会存在一个问题,这个内存他写满了怎么办呢?
他采用看和redis同样的算法LRU算法。就是他把最近没有使用的数据从内从剔除,最终留下来的都是热点数据。
我们的bufferpool 对我们提升读写性能是非常有帮助的。
所以我们要去思考一个问题:
如果我们要去更新一个数据页的时候,而我们的数据页他也加载到我们的缓冲池里面的话,那我们是不是直接更新就好了呢。
但是如果我们更新的数据正好不在buffer pool里面,怎么办呢?
那我们是不是只能从磁盘里面去读取到内存里面,然后再到内存里面去对他进行修改。
那么这种更新数据的方式有没有优化的方案呢?
因为他不在内存中,我们是不是至少要发生一次IO.
所以我们就要涉及到第二个区域:
change Buffer
如果我们要更新的数据他不是唯一索引;并且没有数据重复的情况;也就是说你不是唯一索引,我在修改数据的时候,我并不需要来跟我们磁盘里的数据去进行对比;来判断他的唯一性。如果不是唯一性的索引,我不需要去最终到磁盘去确认这个数据在我修改了之后,或者插入了之后,会不会重复。那么这种情况我们就可以直接把我们要修改的数据记录在change buffer里面。最终在把这个change buffer一次性的同步到我们的磁盘。通过这样的方式来一次性的来更新我们的语句。
在5.5版本下,change buffer 还叫做Insert buffer。现在已经改成change buffer了。因为以前只能缓冲这种插入的数据,就是如果你插入的数据不需要去判断他的唯一性的时候,就把他放在Insert buffer里面。他和chanage buffer是同一个概念。现在chanage buffer他也支持delete 和update操作。
最后我们会把change buffer会同步到我们的磁盘当中。而这个动作我们把他叫做marge.
那么什么时候会有这个marge的动作呢?
就是把我们这种不需要去检查唯一性的数据最终写入都我们磁盘里面。
那么有多少的情况发生merge呢?:比如以下一些情况
- 访问数据页:我们要去访问数据页的时候,如果他要访问的数据还在change buffer里面,他会merge到我们的磁盘
- 后台线程: 刚刚说了,在我们的内存和磁盘之间有很多工作的后台线程,这些后台线程他其实会去定时的把我们内存中的数据同步到我们的磁盘。来减少我们内存和磁盘之间的数据的不一致性。
- 系统shut down:在我们系统值正常关机的时候,他会把我们的内存数据同步到磁盘里面去。
- redo log写满的时候
那么这些就是我们的change buffer.
如果说我们数据库里面的大部分索引,他们都是非唯一性的索引,不需要去检查他的唯一性,并且不是写多读少的业务场景 就可以用我们的change buffer。
那么ChangeBuffer 他也有一个参数来控制。
我们可以看到他默认占用buffer pool大小比例25%。
如果是我们写多读少的情况,我么可以去调大我们的这个比例。
他是为了提高我们修改数据效率而设计的。
然后我么再来看
Log Buffer
那又是干什么的呢?
如果我们 buffer Pool里面的数据,也就是我们称之为脏页的数据,还没有写入磁盘。这个时候,我们数据库宕机了。或者说重启了。
那么这个时候,我们的buffer Pool里面的内存数据是不是丢失了呢?
如果,我们往磁盘写数据,写到一半的时候,他会不会破坏我们的数据文件,最终会不会导致我们的数据库不可用呢?
所以为了避免这个问题,InnDB 里面针对于我们页面修改操作,把他们写入了一个日志文件,而且在我们数据库启动的时候,他会尝试从我们的日志文件中来进行恢复操作。这个也是Inndb的一个特性:崩溃恢复的能力
所以我们把他叫做 redo log.
redo log
他也是实现事务持久性的一个日志文件。
那么有了这个日志文件之后,我们是怎么去做的呢?
如图;
这个文件就是磁盘的 redo log(叫做重做日志),对应于/var/lib/mysql/目录下的 ib_logfile0 和 ib_logfile1,每个 48M。
所以这种先写日志,在把内存的数据从同步到磁盘的操作,在mysql 他是叫做WAL(Write-Ahead Logging)的一个技术。
但是到了这里我们又会有一个问题:
我们把数据写入到redo log时写入到磁盘,而写入到DB file也是写入到磁盘,那这么做有什么意义呢?这不是多此一举吗?
所以我们来了解一下:什么叫做随机IO以及顺序IO。
随机IO/顺序IO
首先看一张磁盘结构图:
磁盘的最小的单元组成是扇区(一个弧度),通常是512个字节。
而操作系统他跟内存去打交道,他们之间最小的单位是页。
那么操作系统跟磁盘去打交道,他们之间读写磁盘最小的单位是块。
如果我们的要去操作的数据他是随机分散在这个磁盘里面的不同的页的,不同的扇区里面的话,那么我们怎么取找到这个数据的呢?
他是等到磁壁的旋转,旋转到一个指定的页之后,等我们的盘片找到了这扇区,他才能去找到我们需要的数据。然后一直找,直到所有的数据都拿出来,我的操作才能完成。
而这个就叫做:随机的IO。
所以我们可以看到他读取数据的速度是非常慢的,他要等到我们的磁壁一直旋转,直到找到所有的数据。
如果说,我们读取的数据,已经找到了第一块数据,他所需要的数据都是相邻的,我们需要的数据都在我们找到的数据的后面,那么我不需要去重新的寻址了,我只需要依次的往下去访问,然后拿到我需要的所有数据,
那么这种情况:我们就叫做他是顺序IO。
所以很明显,顺序IO的效率比我们随机IO的小路高很多。
所以答案就在这里。为什么我们还要把数据写入到redo log的日志文件,而不是直接把数据同步到我们的DB file里面。
就是因为,我们的内存到磁盘的操作,他是随机的IO,而我们记录日志呢,他是顺序的IO。
顺序的IO效率更高。
所以通过这个来提升我们系统的吞吐量。
当然,我们写入redo log的操作,也不是每一次都需要去操作磁盘,在我们的Buffer Pool里面他还有一块内存区域。是用来专门保存即将要写入日志文件里的数据。
他就是我们log Buffer的这个东西。
他其实就是我们redo Log在内存中的一个缓冲区域。
现在他又有了log buffer了。就是在我们写入redo log里面是先写入我们的log buffer里面的。
我们的log buffer默认的大小是16兆。
还有一点,我们需要注意,redo log他主要是用来做崩溃恢复的。
所以我们磁盘内数据主要还是来自我们内存中来的,
这里我们又有疑问,
我们logbuffer 他又是什么时候,把数据写入到我们的redo log的磁盘文件里面的呢?
就是我们操作系统本身,他也是有缓存的,所以我们把他叫做:os cache 或者 OS buffer。
在我们每一次内存数据写入磁盘文件的时候,必然也要经过我们操作系统的缓存,。所以我们来看看他是怎么写入redo log里面的呢?
redo log刷盘时机
首先,他是跟事务相关的,有一个参数来控制,这参数默认值是1。
0(延迟写) log buffer 将每秒一次地写入 log file 中,并且 log file 的 flush 操作同时进行。 该模式下,在事务提交的时候,不会主动触发写入磁盘的操作
1(默认,实时 写,实时刷) 每次事务提交时 MySQL 都会把 log buffer 的数据写入 log file,并且刷到磁盘 中去。
2(实时写,延 迟刷) 每次事务提交时 MySQL 都会把 log buffer 的数据写入 log file。但是 flush操作并不会同时进行。该模式下,MySQL 会每秒执行一次 flush 操作。
这是我们内存区域里面第四块内容。
那么redo log他有一个什么样的特点呢?
redo logt特点
- 他是在InnDB层面去实现的,也就是说,其他存储引擎是没有办法去使用这个日志文件的。仅仅在我们的InnDB中去实现了。
- 它属于一种物理日志,为什么叫做物理日志呢?是因为他记录的不是我们数据更新之后页的状态,他是记录了这个page页做了什么样的改动。
- redo log大小是固定的,默认的都是48兆。也就是说,我们写入到后面的时候,他会把前面的数据进行覆盖,
redo log结构
check point:他是写入文件最起始的位置。是当前要覆盖的位置
write pos代表循环写入已经写到那个位置。
如果他们俩碰到一起了,就说明我们redo log写满了。
那么他就会触发沙盘的操作,并且前面的内容会被覆盖。
Adaptive Hash Index
下节深入研究
那么看到这里,我们来看看InnDB他的磁盘结构是什么样的。
磁盘区域
既然是磁盘文件,那么在我们的磁盘里面都是能找到的。
最主要的是他里面是各种各样的tablespace。不同的表空间。
在InnerDB里面表空间他分成的五大类。
一个是系统的表空间,一个是独占的表空间,通用的表空间,临时的表空间,undo表空间。
具体看官网资料。
undo log和我们redo log是一起的,因为他们都是跟我们事务有关系的。他俩组成起来,就叫做我们innerDB的事务日志。
我们已经知道redo log的作用:他是为了实现崩溃恢复,为了提升我们修改数据的性能。
undo log
那么undo log是干嘛用的呢?
他叫做撤销或者回滚的日志。他记录的是事务发生之前的状态。如果我们在修改数据的时候,修改到一半,出现了异常,那么我们就可以用undo log 来实现一个回滚的操作,他是用来保证我们事务的原子性的。
那么我们执行一个undo,执行一个回滚的操作之后, 他是把我们的事务从逻辑上,恢复到事务之前的一个状态。
他跟redo log是不一样的。
redo log他是一个物理的日志。
undo log他是一个逻辑的日志。
这两个加起来,就叫做事务日志。
undo log他默认在系统的表空间,当然我们也可以给他去创建一个表空间。
那么我们知道几个日志文件,
那么我们来简单总结一下更新的流程。
总结更新流程
那么我们修改语句他做了什么事情呢?
- 从内存或磁盘读取数据:那么我们事务开启的时候,他是不是先要从磁盘去加载数据,然后加载到内存,返回给我们server端的执行器。
- 然后我们的执行器,他会把我们这行值的数据改成我们更新的值。
- 然后我们要去记录两个日志文件,一个是undo log 是他用来执行我们事务的回滚,撤销操作的。还要记录我们的redo log,他是用来崩溃恢复用的。
- 然后我们内存修改了,然后日志也记录了;之后呢 他会在我们buffer pool里面把修改的值进行修改。
- 最后我们事务提交,我们事务提交之后,我们后台有一部分线程,把我们的buffer pool里面的数据同步写入到,我们的磁盘里面。
我们要知道一个概念,在内存和磁盘之间它是由很多的后台工作线程的。
那么在我们的mysql架构中三个层里面:
首先存储引擎层,他有两个事务日志:一个是undo 一个redo。
在我们的服务层呢,他也有一个日志文件,binlog,他是在我们的服务层去实现的。他是可以被所有的存储引擎共用的。
而我们的redo log 和undo log 他们都是在存储引擎层去实现的。
BInlog
那么binlog 他又是干嘛用的呢?
他实际是以事件的机制去记录了我们所有的DDL ,DML这些语句。也是一个逻辑的日志。
他的作用:
- 一个是用来做主从,为什么可以做主从?我们从服务器会去请求拿到主服务器上的执行日志,然后Binlog去解析出来在从服务器去执行一遍。这个才是mysql主从的一个原理。他是通过binlog实现的。
- 数据的恢复:因为他里面记录之前的所有SQL语句,然后我么可以拿出来直接执行,进行恢复
那么在加上了binlog之后,我们一条sql他到底是怎样的更新的呢?
有了我们innDb层redo log和undo log以及加上我们的server 层上的bin log那么他的执行语句到底是什么样呢?
如图:
那么我们来总结下,这张图的重点:
- 我们更新的语句他是,先记录到内存,然后再写日志文件的,
- 然后记录redo log他有分成了两个阶段,第一个是 prepare阶段,第二个是commit阶段,因为他要等待先要写入bin log之后,才能进行commit.
- 在我们的存储引擎层和server层,他们是基于记录不同的日志的。在存储引擎层,他会去记录redolog和undo log。在我们的server 层,他会记binlog 而且这两个他们的先后顺序,是先记录redo log在记录bin log。
数据库-mysql架构与sql执行原理(上)(一)_平凡之路无尽路的博客-CSDN博客
数据库-深度剖析mysql索引原理(上)(三)_平凡之路无尽路的博客-CSDN博客