InnoDB总结

InnoDB体系结构


15854876-b15ea8ba0076a329.png

Buffer与磁盘

1.page是InnoDB磁盘I/O的最小单位,数据存放于page中,对应内存为一个个buffer

2.buffer有三种状态

①free buffer:从未被使用过的buffer

②clean buffer:内存中buffer里面的数据和磁盘page的数据一致

③dirty buffer:内存中新写入的,还没有刷新到磁盘,跟磁盘中的数据不一致

3.对应三种不同的Buffer状态,InnoDB是通过三条双向链表来进行管理的

①free list:每次page调用到内存中,都会先判断free buffer的使用情况,如果不够用,就会从flush list链表和lru list中进行释放

②lru list:数据和磁盘一致,最近最少使用的buffer

③flush buffer:dirty buffer串成的链.一般是把最近最少被弄脏的数据串起来

各大刷新线程以及作用

master thread

后台线程中的主线程,优先级最高,内部有四个循环


main loop{


          if( key == background){


          //后台循环线程


          background loop


          }


          if( key == flush ){


          //刷新线程


          flush loop


          }


          if (key == suspend ){


          //暂停线程


          suspend loop


          }



主循环内又分为两种操作

每1s的操作

          //(1)redo log日志缓冲刷新到磁盘,即使这个事务还没提交 备注:binlog的是指写一次 redo log会在过程就开始写

          //(2)刷新脏页到磁盘

          //(3)执行合并插入缓冲的操作

          //(4)产生checkpoint

          //(5)清除无用的table cache

          //(6)如果当前没有用户活动,就可能会切换到background loop

每10s的操作 基本和1s的相同

          //(1)redo log日志缓冲刷新到磁盘,即使这个事务还没提交

          //(2)刷新脏页到磁盘

          //(3)执行合并插入缓冲的操作

          //(4)产生checkpoint

          //(5)删除无用的undo页

然后是四大I/O线程

1.read thread  负责数据库的读,默认4个

2.write thread  负责数据库的写,默认4个

3.redo log thread  负责把日志缓冲区的内容刷新到redo log文件中

4.change buffer thread  负责把change buffer中的内容刷新到磁盘

脏页刷新线程

page cleaner thread

无用undo页删除线程

purge thread

checkpoint推进线程

当redo log发生切换时,推进check point


15854876-9e8c7b03666253fc.png

write pos是当前记录的位置,一边写一边后移,写到3号末尾就回到0号开头.

checkpoint是当前要擦除的位置,也是往后移并且循环的,擦除前需要把记录更新到数据文件.

锁监控线程

lock monitor thread检测死锁

数据库报错的监控线程

erro monitor thread

内存的刷新机制

首先,mysql是先写日志,再写数据文件

其次,redo log,记录的是数据修改后的值

结合之前的图


15854876-3fb822fe027a1636.png

为了保证性能,一般都是写到缓存后就立即返回了,然后异步的将缓存中的数据刷新到磁盘

控制redo log buffer中的数据刷新到磁盘的参数(备注,buffer和cache都可以理解为缓存,只是buffer主要针对的是I/O方面,cache主要针对cpu方面)

innodb_flush_log_at_trx_commit

有三个值0,1,2

0:每隔1s就会将redo log buffer中的数据写入到redo log 文件,同时进行刷盘操作.但有一个问题是,每次事务提交并不会触发redo log thread 将日志缓冲区中的数据写入到redo log 文件中

1:每次事务提交,都会触发redo log thread将日志缓冲区中的数据写入文件,并刷新到磁盘

2:每次事务提交,都会把redo log buffer中的数据写入到redo log文件,但不会同时刷新到磁盘

一般都设为1,和控制Binlog的参数sync_binlog都设置为1

binlog文件

Mysql的二进制文件

binlog和redo log的三点区别

1.redo log是InnoDB引擎特有的;binlog是Mysqlde Server层实现的,所有引擎都可以使用

2.redo log是物理日志,记录的是在"某个数据页上做了什么修改"(同时可以引申出后面要说的double write);binlog是逻辑日志,记录的是语句的原始逻辑,比如"给id=2这一行的c字段加1"

3.redo log 是循环写的,空间固定会用完(所以会有checkpoint和刷新到磁盘);binlog是追加写的,当文件写到一定大小后会切换到下一个,并不会覆盖之前的日志.

他们的使用场景

1.binlog可以作为数据恢复使用,主从复制搭建

2.redo log作为异常宕机或者介质故障后的数据恢复使用

那么他们是怎么配合的?

假设我们执行

updatetableasetc=c+1whereid=2;


15854876-68cf6319bd0d9ec9.jpg

这里主要用到了两阶段提交

两阶段提交分为prepare和commit阶段

准备阶段(transaction prepare):事务SQL先写入redo log buffer,然后做一个准备标记,再将log buffer中的数据刷新到redo log

提交阶段(commit):将事务产生的binlog写入文件,刷入磁盘

再在redo log 中做一个事务提交的标记,并把binlog写成功的标记也一并写入到redo log文件。

那他们是怎么保证一致性的呢?

情况一:准备阶段,redo log刷新到了磁盘,但是binlog写盘前发生了mysql实例crash.

这时我们redo log是没有binlog提交的日志的,所以我们可以通过回滚来保证数据库一致性

情况二:binlog写盘成功,这时mysql实例crash

进行数据恢复时,只需要使用redo log重做一次就好了。

redo log 和binlog的写入时机

binlog的写入时机

事务执行过程中,先把日志写入到binlog cache,事务提交时,再把binlog cache写入到Binlog文件中

系统会给每一个线程分配一个binlog cache,参数binlog_cache_size可以用于控制单个线程内binlog_cache所占内存的大小,如果超过这个数,就要暂存到磁盘(磁盘swap)

一个事务的binlog是不能被拆开的,因此不论事务有多大,也要确保一次性写入.

一般分为write和异步刷新,而write和fsync的时机,是由参数sync_binlog控制的

1.sync_binlog=0时,每次提交事务都只write,不fsync

2.sync_binlog=1时,每次提交事务都会执行fsync

3.sync_binlog=N(n>1),每次提交事务都write,但累计N个事务才fsync

一般都设置为1

redo log的写入时机

可以参考以上的两阶段提交

此外,也是先写入到redo log buffer,再write到磁盘,再进行磁盘持久化(fsync)

这里和binlog不同的是,redo log是事务执行过程中就会直接写入到redo log buffer中,然后会被我们画的主线程里的几个线程持久化到磁盘,也就是事务可能没提交,但我们的redo log已经持久化到磁盘了。

除了每1s的轮询操作会写入外(这种场景已经刷盘了),还有另外两种场景

场景一.

redo log buffer占用的空间即将达到innodb_log_buffer_size一半的时候,后台回主动写盘

,注意这个时候并没有刷盘,只是留在了文件系统的page cache

场景二

并行的事务提交的时候,顺带将这个事务的redo log buffer持久化到磁盘

此外为看保证数据页不会被多次执行重复的redo log,还有一个组提交的概念

使用日志逻辑序列号(log sequence number,LSN)来对应redo log的一个个写入点,每次写入长度为length的redo log,LSN的值就会加上length

假设三个并发事务(trx1,trx2,trx3)在prepare阶段,都写完redo log,持久化到磁盘的过程,对应的LSN分别是50,120和160

假设trx1是第一个到的,那么他会被选为这组的组长leader

等trx1要开始写盘的时候,组内有三个事务,这个时候LSN也变成了160

那么trx1去写盘的时候,带的LSN就是160,等trx1写完返回时,LSN小于等于160的redo log就已经被持久化到磁盘了

那么trx2和trx3就可以直接返回了。

所以,一次组提交了,组员越多,节省的磁盘Iops效果越好.

进一步推导,在并发更新的场景下,第一个事务写完redo log buffer以后,接下来的fsync越晚调用,组员就会越多,就节省i/o时间(感觉和redis的pipeline有点类似,将多个命令执行一次i/o操作,有点异曲同工之妙)

所以Mysql做的优化就是拖时间

它把redo log做fsync的时间拖到了binlog write之后

流程图如下


15854876-b6ecf88cf5cad4bd.png

这样一来,binlog也可以组提交了

所以这里又有个参数可以控制I/O性能瓶颈了

1.binlog_group_commit_sync_delay表示延迟多少微秒后才会调用fsync

2.binlog_group_commit_sync_no_delay_count,表示累积多少次以后才会调用fsync

最后再来看一下

InnoDB的三大特性

插入缓冲(change buffer)

两次写(double write)

自适应哈希索引(adaptive hash index)

(1)插入缓冲

和磁盘打交道,最主要的性能问题就是I/O.

而插入缓冲的作用就是把普通索引上的DML操作从随机I/O变成顺序I/O

原理

先判断插入的普通索引页是否在缓冲池中,如果在就可以直接插入,不在的话就需要先放到change_buffer中,然后进行change_buffer和普通索引的合并操作,可以将多个操作合并为一个操作中

(2)两次写(double write)

它主要用来保证数据写入的安全性

我们前面提到,redo log是物理日志,如果页都损坏了,是没办法进行恢复的,所以我们需要一个数据页的备份.

可以通过innodb_doublewrite为0来关闭双写缓冲.

(3)自适应哈希索引

主要用于监控我们的查询是否可以通过建立哈希索引得到优化,可以的话,它会自动帮助我们完成这件事.

可以通过innodb_adaptive_hash_index来进行控制,默认是开启的

猜你喜欢

转载自blog.csdn.net/weixin_33757911/article/details/87165502