暂无图片
暂无图片
暂无图片
暂无图片
暂无图片
MySQL性能优化InnoDB buffer pool flush策略.pdf
175
35页
13次
2023-09-16
免费下载
MySQL · 性能优化· InnoDB buffer pool flush 策略漫谈
背景
我们知道 InnoDB 使用 buffer pool 来缓存从磁盘读取到内存的数据页。buffer pool 通常由数
个内存块加上一组控制结构体对象组成。内存块的个数取决于 buffer pool instance 的个数,
不过在 5.7 版本中开始默认以 128M(可配置)的 chunk 单位分配内存块,这样做的目的是为了支
buffer pool 的在线动态调整大小。
Buffer pool 的每个内存块通过 mmap 的方式分配内存,因此你会发现,在实例启动时虚存很高,
而物理内存很低。这些大片的内存块又按照 16KB 划分为多个 frame,用于存储数据页。
虽然大多数情况下 buffer pool 是以 16KB 来存储数据页,但有一种例外:使用压缩表时,需要在
内存中同时存储压缩页和解压页,对于压缩页,使用 Binary buddy allocator 算法来分配内存
空间。例如我们读入一个 8KB 的压缩页,就从 buffer pool 中取一个 16KB block取其中 8KB
剩下的 8KB 放到空闲链表上;如果紧跟着另外一个 4KB 的压缩页读入内存,就可以从这 8KB 中分裂
4KB,同时将剩下的 4KB 放到空闲链表上。
为了管理 buffer pool,每个 buffer pool instance 使用如下几个链表来管理:
LRU 链表包含所有读入内存的数据页;
Flush_list 包含被修改过的脏页;
unzip_LRU 包含所有解压页;
Free list 上存放当前空闲的 block
另外为了避免查询数据页时扫描 LRU,还为每个 buffer pool instance 维护了一个 page hash
通过 space id page no 可以直接找到对应的 page
一般情况下,当我们需要读入一个 Page 时,首先根据 space id page no 找到对应的 buffer
pool instance。然后查询 page hash,如果 page hash 中没有,则表示需要从磁盘读取。在
读盘前首先我们需要为即将读入内存的数据页分配一个空闲的 block free list 上存在空闲的
block 时,可以直接从 free list 上摘取;如果没有,就需要从 unzip_lru 或者 lru 上驱逐 page
这里需要遵循一定的原则(参考函数 buf_LRU_scan_and_free_block , 5.7.5):
1. 首先尝试从 unzip_lru 上驱逐解压页;
2. 如果没有,再尝试从 Lru 链表上驱逐 Page
3. 如果还是无法从 Lru 上获取到空闲 block,用户线程就会参与刷脏,尝试做一次 SINGLE
PAGE FLUSH,单独从 Lru 上刷掉一个脏页,然后再重试。
Buffer pool 中的 page 被修改后,不是立刻写入磁盘,而是由后台线程定时写入,和大多数数据
库系统一样,脏页的写盘遵循日志先行 WAL 原则,因此在每个 block 上都记录了一个最近被修改时
Lsn,写数据页时需要确保当前写入日志文件的 redo 不低于这个 Lsn
然而基于 WAL 原则的刷脏策略可能带来一个问题:当数据库的写入负载过高时,产生 redo log
速度极快,redo log 可能很快到达同步 checkpoint 点。这时候需要进行刷脏来推进 Lsn。由于
这种行为是由用户线程在检查到 redo log 空间不够时触发,大量用户线程将可能陷入到这段低效
的逻辑中,产生一个明显的性能拐点。
Page Cleaner 线程
MySQL5.6 中,开启了一个独立的 page cleaner 线程来进行刷 lru list flush list
默认每隔一秒运行一次,5.6 版本里提供了一大堆的参数来控制 page cleaner flush 行为,
包括:
innodb_adaptive_flushing_lwm,
innodb_max_dirty_pages_pct_lwm
innodb_flushing_avg_loops
innodb_io_capacity_max
innodb_lru_scan_depth
这里我们不一一介绍,总的来说,如果你发现 redo log 推进的非常快,为了避免用户线程陷入刷
脏,可以通过调大 innodb_io_capacity_max 来解决,该参数限制了每秒刷新的脏页上限,调 大
该值可以增加 Page cleaner 线程每秒的工作量。如果你发现你的系统中 free list 不足,总
需要驱逐脏页来获取空闲的 block 时,可以适当调大 innodb_lru_scan_depth 该参数表示从
每个 buffer pool instance lru 上扫描的深度,调大该值有助于多释放些空闲页,避免用户
线程去做 single page flush
为了提升扩展性和刷脏效率, 5.7.4 版本里引入了多个 page cleaner 线程,从而达到并行刷脏
的效果。目前 Page cleaner 并未和 buffer pool 绑定,其模型为一个协调线程 + 多个工作线
程,协调线程本身也是工作线程。因此如果 innodb_page_cleaners 设置为 4,那么就是一个协
调线程,加 3 个工作线程,工作方式 为生产者-消费者。工作队列长度为 buffer pool instance
的个数,使用一个全局 slot 数组表示。
协调线程在决定了需要 flush page 数和 lsn_limit 后,会设置 slot 数组,将其中每 slot
的状态设置为 PAGE_CLEANER_STATE_REQUESTED, 并设置目 page 数及 lsn_limit然后唤
醒工作线程 (pc_request)
工作线程被唤醒后,从 slot 数组中取一个未被占用的 slot,修改其状态,表示已被调度,然后对
slot 所对应的 buffer pool instance 进行操作。直到所有的 slot 都被消费完后,才进入
下一轮。通过这种方式,多个 page cleaner 线程实现了并发 flush buffer pool,从而提升
flush dirty page/lru 的效率。
MySQL5.7 InnoDB flush 策略优化
在之前版本中,因为可能同时有多个线程操作 buffer pool page (在刷脏时会释放 buffer
pool mutex),每次刷完一个 page 后需要回溯到链表尾部,使得扫描 bp 链表的时间复杂度最差
ON*N)。
of 35
免费下载
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文档的来源(墨天轮),文档链接,文档作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

关注
最新上传
暂无内容,敬请期待...
下载排行榜
Top250 周榜 月榜