浅浅理解Mysql 的 buffer pool
看来还是得提出来到一个单体文章,内容挺多,放在持久化里太耦合了。
虽然没参考多少,但是也可以看看:详解MySQL中的Buffer Pool,深入底层带你搞懂它!-腾讯云开发者社区-腾讯云 (tencent.com)
用途
我们在 redo log
文件辅助持久化时提到过,是在数据库对数据修改的时候,需要先把数据读取到 buffer pool
,对其进行修改得到脏数据,提交事务后进行落盘持久化的操作。
以及鉴于 buffer pool
是在内存中,能够极大的提高数据查询的效率,就类似 CPU 和磁盘之间的关系一样,在数据库磁盘数据和客户端之间,有一个大小远小于磁盘大小的内存,也就是 buffer pool
,先把一定的数据页读取到内存,然后再由客户端进行读取,这样就能加快数据的读取操作。
最后就是提高对事务提交前的脏数据的查询速度。如下:
buffer pool
实际上还有一个作用就是加速查询的功能。
- 怎么优化的呢,因为我们修改数据之前,需要先读数据到 buffer pool 嘛,然后改完变成脏数据,最后落盘。 可以看到,事务在提交前可以通过直接读取
buffer pool
中的脏数据来满足查询需求,而不用查询数据库中的旧数据。这里用到了缓存,所以速度就提上来了。
buffer pool 大小
Mysql 启动时就会根据设置的 innodb_buffer_pool_size
进行分配一片内存作为 buffer pool
。
buffer pool
的大小一般默认是 128 M,当然也可以手动调参数啊。所以呢说 buffer pool 的大小是有限的,能存储的数据页也是有限的(一般数据页带大小就是 16 kb)。假如说我们一次性读取了大量数据(查询 20 G 的数据),那肯定是不可能放下的。
由于能存放的数据页数量有限,所以会在 buffer pool
满的时候采用一些淘汰策略,比如 LRU 算法,淘汰最近未使用的页,加入新的页。
但是呢,在有些情况下,存粹的 LRU 算法并不合理,因为可能涉及到旧数据读取导致数据页全部失效的情况。
buffer pool 采用纯LRU 算法的问题
#纯LRU的不足
比如现在要读取一个很少没有查询过的表,这个表的数据很大。你读取的话,像刚才讲的,会替换掉最近未使用的页,那最终肯定会导致其他业务需要查询的数据会被换出去,也就是说我们这些正常业务的查询内存中数据的命中率降低了,甚至可以说基本不走内存查询,那肯定会导致 sql 响应过慢,以及增大磁盘的读取压力。
改进方案:LRU 算法改进。
因此呢,Innodb 给出了解决方案,通过把 LRU 链表(LRU 不是通过哈希表和双向链表实现的嘛),划分成两个区域,大概是 5:3,分别为 young 区和 old 区。
这里可以联想到 JVM 内存区域回收的时候,把垃圾回收区域划分为 Eden 新生代和老年代嘛,采用不同的策略进行垃圾回收就是为了适应不同类型对象存活的规律,而采用不同的回收算法,提高垃圾回收的效率。
回来看,LRU 改进后的具体划分如下:
其中还有几点要注意:
- 插入数据页时,会先插入到 LRU_OLD 指针所在的位置,也就是 old 区的链表头部。经常使用的数据页记录,在之后会以一定的策略提到整个链表的头部,避免被直接替换掉。
- 而淘汰数据页,都是淘汰 tail 指针的数据页,也就是整个链表的尾部。
具体策略:对于 old 区的数据页,每次访问的时候,会判断当前页是否存在时间超过了规定时间,比如说 1 秒,超过了的话,你想想 Mysql 查询数据进行是数据页替换的时候速度肯定非常快,1秒之内还在读取这个数据页的数据,说明它就是活跃数据,直接把它放到整个链表的头部。如果没超过,就说明不常用,留在原地,当数据页存满的时候,会优先淘汰 old 区的数据页,也就是可能很少的数据。
至此呢,就会尽量避免正常业务数据被不常使用的数据全部替换掉,导致缓存命中率太低,造成磁盘压力增大以及 SQL 响应缓慢。
转载自:https://juejin.cn/post/7372235558220824617