【MySQL】buffer pool

140 阅读3分钟

Buffer Pool

Why?

数据存储在磁盘,每次读取速度慢。

How?

  • Buffer Pool

    • Innodb 存储引擎使用的缓冲池,提高数据库的读写性能。
    • 读取数据时:如果数据存在于 Buffer Pool 中就直接读。
    • 修改数据时:先是修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,最后由后台线程将脏页写入到磁盘。
    • innodb_buffer_pool_size:默认配置128MB,建议设置成可用物理内存的 60%~80%。

Buffer Pool 管理

MySQL 启动,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照默认的16KB的大小划分出一个个的页,叫缓存页。

  • MySQL刚启动使用的虚拟空间大,但是物理空间小

  • InnoDB 为每一个缓存页都创建了一个控制块,控制块信息包括「缓存页的表空间、页号、缓存页地址、链表节点」

  • 缓存页分类:

    • 索引页

    • 数据页

    • undo 页

      • 开启事务后,InnoDB 层更新记录前,先记 undo log 到undo 页
    • 插入缓存

    • 自适应哈希索引

    • 锁信息

空闲页管理

Why?

使用空闲页时,如何快速定位?总不能遍历吧。

How?

Free 链表(空闲链表)

  • 头节点:存储Free链表首位位置和计数
  • 控制块:就是Free链表的节点,记录对应缓存页的地址,
  • 使用过程:从 Free链表中取一个空闲的缓存页,把该缓存页对应的控制块的信息填上,控制块从 Free 链表中移除。

脏页管理

Why?

有数据修改时,在缓存页修改,如何控制使其再同步到磁盘?

How?

Flush 链表:元素都是脏页对应的控制块

  • 后台线程就可以遍历 Flush 链表,将脏页写入到磁盘。

脏页刷磁盘时机

  • redo log 日志满:主动触发脏页刷新到磁盘;

  • Buffer Pool 空间不足:需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘;

  • MySQL 认为空闲:后台线程会定期将适量的脏页刷入到磁盘;

  • MySQL 正常关闭之前:把所有的脏页刷入到磁盘;

如何提高缓存命中率?

Why?

Buffer Pool大小有限,如何选择合适的缓存页进行替换,提高缓存命中率?

How?

LRU算法的缺陷:

  • 预读失效

    • 预读取:利用空间局部性原理,MySQL 在加载数据页时,会提前把它相邻的数据页一并加载进来,目的是为了减少磁盘 IO。
    • 失效:被提前加载进来的数据页,并没有被访问,反而把有用的页替换掉了
    • 解决办法:预读页停留时间要尽可能的短
  • Buffer Pool污染

    • SQL扫描了大量的数据时,Buffer Pool 里的所有页都替换出去,导致大量热数据被淘汰了

MySQL设计

  • old 区域:冷数据

  • young 区域:热点数据

    • 只有同时满足「被访问」与「在 old 区域停留时间超过 1 秒」两个条件,才会被插入到 young 区域头部
  • 规则:

    • 预读的页加入 old 头部

    • old数据第一次访问记录时间,第二次访问时:

      • 间隔时间小于1s,不动
      • 大于1000ms,移动到young头
    • young 区域前面 1/4 被访问不会移动到链表头部

      • 防止 young 区域节点频繁移动到头部
    • innodb_old_blocks_time:间隔时间,默认1000ms

  • innodb_old_blocks_pct:默认是 37,young:old 比例63:37。