美文网首页
MySql--InnoDB 的 Buffer Pool

MySql--InnoDB 的 Buffer Pool

作者: 简书徐小耳 | 来源:发表于2019-05-21 16:34 被阅读0次

具体细节 请去掘金购买《MySQL 是怎样运行的:从根儿上理解 MySQL》

InnoDB 的 Buffer Pool

  • 1.innodb存储数据,都是存放在表空间,表空间实际对应着一个或者几个的实际文件。
  • 2.访问数据的时候,innodb只能以页为单位。
  • 3.innodb通过Buffer pool 把加载进入内存的页,缓存起来,避免立即释放。

Buffer Pool的简介

  • 1.Mysql服务器启动的时候向OS申请一片连续的内存---这个就是Buffer Pool
  • 2.默认情况下Buffer Pool只有128M大小,可以通过innodb_buffer_pool_size修改,该参数的单位是字节。
  • 3.最小值只能是5M。

Buffer Pool内部组成

  • 1.Buffer Pool中默认的缓存页大小和在磁盘上默认的页大小是一样的,都是16KB
  • 2.为了管理Pool中的缓存页,Innodb为每一个缓存页创建了一些所谓的控制信息。--控制信息也是写在页上面的。
  • 3.控制信息包括该页所属的表空间编号、页号、缓存页在Buffer Pool中的地址、链表节点信息、一些锁信息以及LSN信息。
  • 4.因为每个缓存页对应的控制信息占用的内存大小是相同的,因此从buffer pool中分配一块内存专门记录控制信息--控制块
  • 5.控制块和缓存页是一一对应的,它们都被存放到 Buffer Pool 中,其中控制块被存放到 Buffer Pool 的前边,缓存页被存放到 Buffer Pool 后边
  • 6.控制块和缓存页之间是有碎片的--当然如果大小分配合理也有可能没有碎片。
  • 7.每个控制块大约占用缓存页大小的5%,innodb_buffer_pool_size设置的大小并不包含控制块的大小,也就是说InnoDB在为Buffer Pool向操作系统申请连续的内存空间时,这片连续的内存空间一般会比innodb_buffer_pool_size的值大5%左右。

free链表的管理

  • 1.Buffer pool中的属于free的控制块单独组成一个链表(方便寻找free页)---free链表(或者说空闲链表)
  • 2.刚初始化完的buffer pool 就是一个free链表控制块+碎片+缓存页
  • 3.为了管理好这个free链表,特意为这个链表定义了一个基节点,含着链表的头节点地址,尾节点地址,以及当前链表中节点的数量等信息
  • 4.基节点的大小不算在bufferpool里面
  • 5.当从磁盘中加载一个页到Buffer pool中的时候,从free链表中找到控制块,控制块初始化会指向一些空白的缓存页。
  • 6.当缓存页被使用之后,该控制块就被移除了。当然在移除之前,会把该缓存页的信息填写到控制块上,比如缓存的数据所在
    的表空间,页号之类的。--控制块和缓存页是钉死的,移除控制块的意思就是把该控制块的上一个控制块的free_next指向该控制块的下一个控制块。
  • 7.对应的缓存页还是留在原来的位置

缓存页的哈希处理

  • 1.当我们想要访问某个页中数据的时候,这个时候如果缓存存在就访问缓存,不存在就去磁盘访问
  • 2.如何检查缓存是否存在?由于表空间和页号是固定的(在生成数据的时候划分好的,持久化到文件)。然后根据这个去hash寻找缓存页
  • 3.当找不到缓存,再去free链表分配一个页给磁盘加载数据

flush链表的管理

  • 1.当缓存页被修改了,这就导致数据和磁盘上的页不一样了。
  • 2.innodb选择在一定的时候进行同步
  • 3.每个被修改的缓存页最后都会被加入到flush链表。
  • 4.flush链表和free链表相似 都包含控制块。
  • 5.在flush链表中的页面肯定在LRU链表中

LRU链表的进化

  • 1.我们塞入LRU链表的都是缓存页对应的控制块(即从Free进入了LRU)
  • 2.简单版本的LRU,因为INNODB的预读功能和扫描全表的查询语句所以被淘汰。
  • 3.简单LRU进化为--划分冷(old)热(young)区域的LRU,该LRU是按照链表比例来区分的。
  • 4.通过innodb_old_blocks_pct确定old区域在LRU链表中所占的比例,默认值是37%--全局变量
  • 5.划分区域的LRU,初始的缓存页放在old区域头部,不会影响young区域。
  • 6.通过innodb_old_blocks_time来控制放入old区域头部的数据,会在多久时间之内被再次访问才会放入young
  • 7.如果innodb_old_blocks_time=0,代表数据直接被放入young。
  • 8.上述的LRU还存在一个缺点,每次访问一个缓存页就需要把控制块移动到LRU链表头部,开销太大,因此提出了被访问的缓存页位于young区域的1/4的后边,才会被移动到LRU链表头部

线性预读

  • 1.统变量innodb_read_ahead_threshold,如果顺序访问了某个区(extent)的页面超过这个系统变量的值,就会触发一次异步读取下一个区中全部的页面到Buffer Pool的请求
  • 2.innodb_read_ahead_threshold默认值是56,是全局变量。

随机预读

  • 1.如果Buffer Pool中已经缓存了某个区的13个连续的页
  • 2.不论这些页面是不是顺序读取的,都会触发一次异步读取本区中所有其的页面到Buffer Pool的请求

刷新脏页到磁盘

  • 1.后台有专门的线程每隔一段时间负责把脏页刷新到磁盘
  • 2.从LRU链表的冷数据中刷新一部分页面到磁盘---定时从LRU链表尾部开始扫描一些页面,扫描的页面数量可以通过系统变量innodb_lru_scan_depth来指定
  • 3.从flush链表中刷新一部分页面到磁盘--定时从flush链表中刷新一部分页面到磁盘,刷新的速率取决于当时系统是不是很繁忙
  • 4.如果刷新较慢导致没有缓存页可用只能替换掉LRU尾部数据,先去寻找没有修改记录的,找不到再去找脏页并同步到磁盘。
  • 5.如果系统特别繁忙 也可能出现用户线程批量的从flush链表中刷新脏页
  • 6.注意这边的刷新到磁盘还是会遇到断电因素,因此原子性是redo和undo的事情(他们都是顺序写的)

多个Buffer Pool实例

  • 1.在多线程情况下访问buffer pool中的链表是需要加锁的,因此可以通过配置多个pool,来降低锁开销。
  • 2.多个bufferpool的实例之间大小都是一样的。通过innodb_buffer_pool_instances ,来设置bufferpool的个数。
  • 3.当innodb_buffer_pool_size的值小于1G的时候设置多个实例是无效的,InnoDB会默认把innodb_buffer_pool_instances 的值修改为1

innodb_buffer_pool_chunk_size

  • 1.为了支持mysql运行时候可以调整buffer pool的大小(通过innodb_buffer_pool_size)
  • 2.动态调整的话如果要在运行过程中重新申请一块连续的内存,负责老的数据到新的pool,太损耗性能了。
  • 3.因此mysql改为以一个chunk为单位去申请内存,若干个chunk就是bufferpool。所以动态调整的时候只需要
    增减chunk就行。
  • 4.innodb_buffer_pool_chunk_size就是指定chunk的大小。
  • 5.innodb_buffer_pool_chunk_size的值只能在服务器启动时指定,在服务器运行过程中是不可以修改的。
  • 6.因此在运行过程中通过innodb_buffer_pool_size来调整bufferpool的大小。

配置Buffer Pool时的注意事项

  • 1.innodb_buffer_pool_size必须是innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances的倍数

Buffer Pool中存储的其它信息

  • 1.Buffer Pool的缓存页除了用来缓存磁盘上的页面以外,还可以存储锁信息、自适应哈希索引等信息

总结

  • 1.不论是LRU,FLUSH还是Free链表,其都是逻辑链表。
  • 2.底层的物理内存中控制块和数据页都是在一起的。
  • 3.SHOW ENGINE INNODB STATUS可以查看到Buffer Pool的信息

相关文章

网友评论

      本文标题:MySql--InnoDB 的 Buffer Pool

      本文链接:https://www.haomeiwen.com/subject/ooqdzqtx.html