您好,欢迎来到测品娱乐。
搜索
您的当前位置:首页一篇文章带你读懂MySQL的InnoDB存储引擎Buffer Pool原理

一篇文章带你读懂MySQL的InnoDB存储引擎Buffer Pool原理

来源:测品娱乐

一个不变的原则:网络连接必须让线程来处理

一条SQL语句的执行

  MySQL内部的工作线程从一个网络连接中取出来一个SQL语句,然后交给SQL接口去执行SQL语句,SQL接口将SQL语句交给查询解析器去解析SQL,解析完SQL,查询解析器再将SQL语句交给查询优化器,去获取一个最优的执行计划,最后交由执行器,执行器根据这个执行计划去调用存储引擎的接口,完成SQL语句的执行。

一、InnoDB存储引擎

1.1 基于InnoDB存储引擎完成一条更新语句的执行

更新语句:update users set name = ‘lisi’ where id = 10

(1)redo log 是InnoDB存储引擎特有的,而binlog是MySQL Server自己的日志文件。

(2)redo日志的是那种刷盘策略到底选择哪一种?

  其实对于redo日志的三种刷盘策略,我们通常建议是设置为1,也就是说,提交事务的时候,redo日志必须是刷入磁盘文件里的,这样严格保证事务提价后数据不会丢失,因为有redo日志在磁盘文件里,可以恢复你做的所有修改。

  如果选择0的话,可能你提交事务之后,MySQL宕机,那么此时redo日志没有刷盘,导致内存里的redo日志丢失,你提交的事务更新的数据就丢失了。

(3)最后一步redo日志写入commit标记的意义是什么?

  说白了,它其实是用来保持redo logbinlog日志一致的。

  我们举个例子,假设我们在提交事务的时候,一共有图中的5、6、7三个步骤,必须是三个步骤都执行完,才算是提交了事务。那么我们在刚完成步骤5的时候,也就是 redo log 刚刚刷入磁盘文件的时候,MySQL宕机了,此时因为没有最终的事务commit标记在redo日志里,所以此次事务可以判定为不成功,不会说 redo 日志文件里有这个更新的日志,但是 binlog 日志文件里没有这次更新的日志,不会出现数据不一致的问题

  如果要是完成步骤6的时候,也就是 binlog 写入磁盘,此时MySQL宕机了,这时候因为 redo log 中没有最终的 commit 标记,因此这次事务提交也是失败的。

  必须是 redo log 中写入最终的事务 commit标记,此时事务才算提交成功。这时候 redo log 里有本次更新对应的日志,binlog 里也有本次更新对应的日志,redo log 和 binlog 完全是一致的。

(4)执行更新操作的时候,为什么不能直接修改磁盘上的数据?

  因为直接修改磁盘上的数据,相当于是对磁盘进行随机读写,那速度是相当的慢,随便一个大磁盘文件的随机读写性能,可能都要几百毫秒,如果是这么搞的划,可能我们数据库每秒也就只能处理几百个请求了。

(5)一句话总结Buffer Pool

  在这里我们简单对Buffer Pool这个东西做一下总结:它其实是数据库中我们第一个必须要搞清楚的核心组件,因为增删改查操作首先就是针对这个内存中的Buffer Pool里的数据执行的,同时配合了后续的undo logredo log刷盘等机制和操作。

  所以Buffer Pool就是数据库的一个内存组件,里面缓存了磁盘上的真实数据,然后我们的Java系统对数据库执行的增删改查操作,其实主要就是对这个Buffer Pool中的缓存数据执行的。

1.2 Buffer Pool

1.2.1 Buffer Pool 内存数据结构到底长什么样?

  Buffer Pool其实本质就是一大块内存数据结构,由一大堆的缓存页描述数据块组成的,然后加上了各种链表freeflushlru)来辅助它的运行。

  1. Buffer Pool 的大小:默认是128M,有一点偏小了,我们实际生产环境下完全可以对Buffer Pool进行整改。比如设置为2GB,在MySQL配置文件 my.cnf 中修改 innodb_buffer_pool_size = 21474838
  2. 缓存页 :我们都知道数据库的核心数据模型就是 表+行+字段 的概念,那么我们的数据是一行一行的放在Buffer Pool中吗?
    不是的,实际上MySQL对数据抽象出来了一个数据页的概念,它把很多行数据放在了一个数据页里,也就是说我们的磁盘文件中就是会有很多的数据页,每一页里面放了很多行数据。默认情况下,磁盘中存放的一个数据页的大小是16KB,而Buffer Pool中存放的叫缓存页,一个缓存页和磁盘上的数据页是一一对应的,缓存页的大小也是16KB
  3. 描述数据块:对于每个缓存页,它实际上都会有一个描述信息,这个描述信息大体可以认为是用来描述这个缓存页的,比如包含如下的一些东西:这个数据页所属的表空间数据页的编号、这个缓存页在Buffer Pool中的地址等。这个描述信息本身也是一块数据,在Buffer Pool中,每个缓存页的描述信息放在最前面,然后各个缓存页放在后面。
    而且在这里我们要注意一点,Buffer Pool中的描述数据块大概相当于缓存页大小的5%左右,也就是每个描述数据块大概800个字 节左右的大小,然后假设设置你的 Buffer Pool 大小是 128M,实际上 Buffer Pool 真正的最终大小会超出一些,可能有个130多MB的样子,因为它里面还要存放每个缓存页的描述数据。
  4. free链表:存放空闲的缓存页
  5. flush链表:存放脏的缓存页,也就是脏页
  6. lru链表,最近最少使用链表,通过它可以淘汰那些很少被访问的缓存页

1.2.2 数据库启动的时候,是如何初始化Buffer Pool的?

  其实这个也很简单,数据库只要一启动,就会按照你设置的Buffer Pool大小,稍微再加大一点,去找操作系统申请一块内存区域,作为Buffer Pool的内存区域,然后当内存区域申请完毕后,数据库就会按照默认的缓存页的16KB的大小以及对应的800字节左右的描述数据块的大小,在Buffer Pool中划出来一个一个的缓存页和一个一个的它们对应的描述数据。只不过这个时候,Buffer Pool中的一个一个缓存页都是空的,里面什么都没有,要等数据库运行起来,当我们对数据执行增删改查的操作的时候,才会把数据对应的数据页从磁盘文件里读取出来,放到Buffer Pool中的缓存页里去

1.2.3 free链表

(1)我们怎么知道哪些缓存页是空闲的呢?

  我们在执行增删改查的时候,此时就需要不停的从磁盘上读取一个一个数据页放到Buffer Pool中对应的缓存页里去,把数据缓存起来,那么以后就可以对这个数据在内存里执行增删改查了。但是此时在从磁盘上读取数据页放入Buffer Pool中的缓存页的时候,必然涉及到一个问题,那就是哪些缓存页是空闲的?

  刚开始启动数据库的时候,可能所有的缓存页都是空的,因为此时可能是一个空的数据库,一条数据都没有,所以此时所有缓存页的描述数据块,都会被加入free链表中。

  每个free链表是一个双向链表,并且这个free链表有一个基础节点,它会引用链表的头节点尾节点,里面还存储了链表中有多少个描述数据块的节点,也就是有多少个空闲的缓存页。

(2)free链表占用多少内存空间?

  可能有的人会以为这个描述数据块,在Buffer Pool里有一份,在free链表里也有一份,好像在内存里有两个一模一样的描述数据块,是这样的吗?其实这么想就大错特错了。

  这里要给大家讲明白一点,这个free链表,它本身其实就是由Buffer Pool里的描述数据块组成的,你可以认为每个描述数据块里都有两个指针,一个是free_pre,一个是free_next分别指向自己的上一个free链表节点,以及下一个free链表的节点

  通过Buffer Pool中的描述数据块的 free_pre 和 free_next 两个指针,就可以把所有的描述数据块串成一个free链表,上面为了画图需要,所以把描述数据块单独画了一份出来,表示它们之间的指针引用关系。

(3)如何将磁盘上的数据读取到Buffer Pool的缓存页中去?

  首先,我们需要从free链表里获取一个描述数据块,然后就可以对应的获取到这个描述数据块对应的空闲缓存页了。

(4)这个描述数据块是怎么从free链表里移除

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- cepb.cn 版权所有 湘ICP备2022005869号-7

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务