如果你只做能力范围之内的事,你就永远不会有进步!|

navyum

园龄:4个月粉丝:0关注:0

16.缓冲池-BufferPool

buffer pool

常识:

  • Buffer Pool 是一片连续的内存空间,通过innodb_buffer_pool_size在服务器运行过程中调整buffer pool大小,默认为128MB
  • Free Page(空闲页),此页未被使用,位于 Free 链表
  • Clean Page(干净页),此页已被使用,但是页面未发生修改,位于 LRU 链表
  • Dirty Page(脏页),表示此页已被使用已经被修改,其数据和磁盘上的数据已经不一致。
  • 脏页上的数据写入磁盘后,内存数据和磁盘数据一致,那么该页就变成了干净页。脏页同时存在于LRU链表和Flush链表。
  • 提高读性能: 读取数据的时候,先从buffer pool LRU链表(干净页)读取数据,如果没有从磁盘读取并把它相邻的数据页一并加载进来。
  • 提高写性能: 更新数据的时候,不需要每次都要写入磁盘,而是将 Buffer Pool 对应的缓存页标记为脏页,然后再由后台线程将脏页写入到磁盘

buffer pool的作用:

  • 本质上就是个缓存池。为了降低磁盘和内存的IO差异,提高数据库的读写性能而设计
  • 存储的数据包括:
    • 索引页
    • 数据页
    • undo页
    • 插入缓冲区change buffer
    • 自适应哈希索引
    • 其他

结构:

Img * 基本构成: * 控制块: * 存储信息:缓存页的表空间、页号、缓存页地址、链表节点 * 控制块跟缓存页一一对应 * 缓存页: * 大小16KB,跟页的大小一致

管理空闲页:

  • 如何查找空闲页?
    • 当需要从磁盘中加载一个页到 Buffer Pool 中时,通过Free链表取一个空闲的缓存页,并且填写该缓存页对应的控制块的信息,然后把该缓存页对应的控制块从Free 链表中移除 Img

管理脏页:

  • 如何查找脏页并写入磁盘?
  • 在需要刷盘时,使用Flush链表,后台线程就可以遍历脏页,写入到磁盘 Img
  • 脏页刷盘时机
    • 核心:既要保证缓存、磁盘的一致性,又要确保刷盘的效率
      • 空闲时后台线程定期将适量的脏页刷入到磁盘:
        • BUF_FLUSH_LRU方式:定时LRU链表``尾部扫描脏页刷新到磁盘
        • BUF_FLUSH_LIST方式:定时Flush链表刷新部分脏页到磁盘
      • Mysql服务关闭前,会把所有的脏页刷入到磁盘
      • Buffer Pool 空间不足时,如果需要淘汰脏页,此时会先将脏页刷入磁盘,再进行淘汰
      • redo log 日志满了的情况下,会触发脏页刷入磁盘

缓存淘汰和命中率:

  • 基于LRU(Least recently used)
  • 如何管理缓存页的过期,提高命中率
  • 缓存相关的问题
    1. 避免预读导致的缓存失效
      • 预读:在加载数据页时,为了减少磁盘IO,会提前把它相邻的数据页一并加载进来
      • 失效:预读数据占据了缓存头部,一直未被访问,一些可能访问更频繁缓存反而被迫淘汰,导致失效
      • 解决方案:
        • 区分预读数据真正被访问到的数据,分别存到old区young区
        • 进入young区新增条件:在 old 区域被真正访问过
      • 具体方法:
        • young区:在LRU前部分,缓存时间更长,占63%
        • old区:在LRU后部分,缓存时间相对短,占37%
        • 通过innodb_old_blocks_pct设置young区和old区占比
        • 这样如果预读的页就只加入到 old区域的头部,当页被真正访问的时候,才将页插入 young 区域的头部,否则会被优先淘汰 Img
    2. 避免Buffer Pool污染
      • 污染的含义:扫描了大量的数据,导致全部页被替换,大量热数据被淘汰
      • 触发:
        • 结果集比较离散,大量的页被访问
        • 全表扫描,所有页被访问
      • 解决方案:
        • 进入young区新增条件:在 old 区域停留时间超过 1 秒
        • 控制块中记录该页第一次被真正访问的时间
        • 通过innodb_old_blocks_time设置停留时间
      • 具体方法:
        • 当前访问时间与控制块中该页第一次被访问的时间相比
          • 大于1s, 则可以移动到young区域头部
          • 小于1s,则继续停留在old区域
    3. 其他优化:
      • 防止young区域内部移动过于频繁
        • 处于young区域前1/4的数据被访问时,不会移动到头部
        • 处于young区域后3/4的数据被访问时,移动到头部

Buffer Pool的其他优化:

多实例:

  • Buffer Pool特别大而且多线程并发访问特别高的情况下,单一的Buffer Pool可能会影响请求的处理速度。(多线程下,访问Buffer Pool中的各种链表都需要加锁
  • 多实例解决了锁的粒度问题 Img

以chunk为单位申请内存:

  • 在重新调整Buffer Pool大小的时候,需要重新向操作系统申请一块连续的内存空间,然后将旧的Buffer Pool中的内容复制到这一块新空间,这个操作极其耗时
  • 改为以chunk为单位向操作系统申请连续内存,这样一个Buffer Pool实例其实是由若干个chunk组成的。调整大小时,只需要申请新的chunk或者减少chunk即可 Img
  • 参数配置:innodb_buffer_pool_chunk_size,默认为128M。只能在服务器启动时指定,在服务器运行过程中是不可以修改的

Change buffer/Insert buffer(插入缓冲区)

  • 作用:避免频繁磁盘IO
  • 触发时机:
    • 当对 非唯一索引 进行插入、更新或删除操作时:
    • 如果相关的索引页不在Buffer Pool中,InnoDB会将这些操作暂存在插入缓冲区;等到系统有空闲资源时,InnoDB会将插入缓冲区中的操作应用Buffer Pool中的索引页。
    • 如果相关索引页在Buffer Pool中,则直接更新Buffer Pool
  • 注意点:只针对非唯一索引生效,原因:唯一索引有唯一性约束,必须要从磁盘取出数据才能校验唯一性是否正确;非唯一索引没有这个限制,所以可以这么做

本文作者:navyum

本文链接:https://www.cnblogs.com/navyum/p/18509408

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   navyum  阅读(19)  评论(0编辑  收藏  举报
//自己上传到博客园的js
点击右上角即可分享
微信分享提示