MySQL优化甜点之_调节磁盘和CPU速率不匹配的缓冲池
2019-08-11 20:53:21 33 举报
AI智能生成
MySQL优化甜点之_调节磁盘和CPU速率不匹配的缓冲池
作者其他创作
大纲/内容
缓存的重要性
问题的出现
磁盘的速度太慢啦,如果每一次读取记录都要将数据页从磁盘读入内存,那可太蠢了
解决思路
在进行读写访问后不立即释放该数据页所占的内存,而是建立缓存。这样下一次访问就可以直接从内存读取了
InnoDB的 Buffer Pool
调节缓冲池的大小
在启动服务器的时候配置innodb_buffer_pool_size参数的值
Buffer Pool的内部组成
向操作系统申请的一片连续的内存区域,用于缓存页面
一些数据结构
FREE链表
标志Buffer Pool中哪些缓存页是空闲的
缓存页的哈希处理
如何定位某个页面是否在Buffer Pool中
以表空间+页号(唯一定位一个页)为key,页面为Value
FLUSH链表
标志Buffer Pool哪些缓存页是脏页(被修改过,但是没有刷新到磁盘)
作用
每次修改并不是立即刷新到磁盘,而是在某个checkpoint的时候将脏页刷新到磁盘
LRU链表的管理
(Least recently used)
(Least recently used)
最近最久未使用
由于内存比较小,不能缓存所有的页,所以使用这个经典的算法,提高缓存命中率
简单的LRU链表
如果该页不在Buffer Pool中,在把该页从磁盘加载到Buffer Pool中的缓存页时,就把该缓存页对应的控制块作为节点塞到链表的头部。
如果该页已经缓存在Buffer Pool中,则直接把该页对应的控制块移动到LRU链表的头部。
简单LRU链表存在的问题
预读的情况
(根据局部性原理预先读取一些未访问的页面)
(根据局部性原理预先读取一些未访问的页面)
线性预读
系统变量innodb_read_ahead_threshold
如果顺序访问了某个区(extent)的页面超过这个系统变量的值,
就会触发一次异步读取下一个区中全部的页面到Buffer Pool的请求
就会触发一次异步读取下一个区中全部的页面到Buffer Pool的请求
随机预读
提供innodb_random_read_ahead系统变量进行开关
如果Buffer Pool中已经缓存了某个区的13个连续的页面,
不论这些页面是不是顺序读取的,都会触发一次异步读取本区中所有其的页面到Buffer Pool
不论这些页面是不是顺序读取的,都会触发一次异步读取本区中所有其的页面到Buffer Pool
问题所在
在预读这些页面进入LRU链表头部,然后将LRU链表尾部的页面淘汰掉的情况下。
如果这些预读的页面很多都用不上,那会大大降低缓存命中率,即劣币驱逐良币
如果这些预读的页面很多都用不上,那会大大降低缓存命中率,即劣币驱逐良币
全表扫描的情况
如果按照简单LRU链表的算法,这些扫描的页将统统加入到缓冲池中,将大量缓存页淘汰。
这会严重影响其他查询对Buffer Pool的使用,从而大大降低缓存命中率。
这会严重影响其他查询对Buffer Pool的使用,从而大大降低缓存命中率。
问题总结
加载到Buffer Pool中的页不一定用到
如果非常多的使用频率偏低的页被同时加载到Buffer Pool时,可能会把那些使用频率非常高的页从Buffer Pool中淘汰。
如何解决简单LRU链表存在的问题
1. 将LRU链表按照一定比例分为两截
具体比例可参考innodb_old_blocks_pct(old区域占比)系统变量
一部分存储使用频率非常高的缓存页,所以这一部分链表也叫做热数据,或者称young区域。
另一部分存储使用频率不是很高的缓存页,所以这一部分链表也叫做冷数据,或者称old区域。
如何对问题进行解决
针对预读的页面可能不进行后续访问情况的优化
当磁盘上的某个页面在初次加载到Buffer Pool中的某个缓存页时,
该缓存页对应的控制块会被放到old区域的头部,
而不会影响young区域中被使用比较频繁的缓存页。
该缓存页对应的控制块会被放到old区域的头部,
而不会影响young区域中被使用比较频繁的缓存页。
针对全表扫描时,短时间内访问大量使用频率非常低的页面情况的优化
由于访问一个页面的所有记录相当于访问一个页面多次。
规定在对某个处在old区域的缓存页进行第一次访问时就在它对应的控制块中记录下来这个访问时间,
如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该页面就不会被从old区域移动到young区域的头部,
否则将它移动到young区域的头部。
规定在对某个处在old区域的缓存页进行第一次访问时就在它对应的控制块中记录下来这个访问时间,
如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该页面就不会被从old区域移动到young区域的头部,
否则将它移动到young区域的头部。
时间间隔由系统变量系统变量innodb_old_blocks_time决定
2. 更进一步的优化
降低调整LRU链表的频率
只有被访问的缓存页位于young区域的1/4的后边,才会被移动到LRU链表头部
刷新脏页到磁盘的规则
后台有专门的线程每隔一段时间负责把脏页刷新到磁盘,这样可以不影响用户线程处理正常的请求。
1. 从LRU链表的冷数据中刷新一部分页面到磁盘。
2. 从flush链表中刷新一部分页面到磁盘。
3. 准备加载一个磁盘页到Buffer Pool时没有可用的缓存页,并且LRU链表尾部有没有可以直接释放掉的未修改页面
则将LRU链表尾部的一个脏页同步刷新到磁盘
刷新单个页面到磁盘中的刷新方式被称之为BUF_FLUSH_SINGLE_PAGE
则将LRU链表尾部的一个脏页同步刷新到磁盘
刷新单个页面到磁盘中的刷新方式被称之为BUF_FLUSH_SINGLE_PAGE
多个Buffer Pool实例
Buffer Pool本质是InnoDB向操作系统申请的一块连续的内存空间,
在多线程环境下,访问Buffer Pool中的各种链表都需要加锁处理啥的
在多线程环境下,访问Buffer Pool中的各种链表都需要加锁处理啥的
如何提高并发能力?
多个实例,提高并行性
每个Buffer Pool实例中都有各自独立的链表,互不干扰。
为了能在运行时修改Buffer Pool大小。而设计的Chunck
不再一次性为某个Buffer Pool实例向操作系统申请一大片连续的内存空间,而是以一个所谓的chunk为单位向操作系统申请空间。
也就是说一个Buffer Pool实例其实是由若干个chunk组成的,一个chunk就代表一片连续的内存空间,里边儿包含了若干缓存页与其对应的控制块
通过系统变量innodb_buffer_pool_chunk_size指定Chunk的大小
配置Buffer Pool时的注意事项
innodb_buffer_pool_size必须是innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances的倍数
如果在服务器启动时,innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances的值已经大于innodb_buffer_pool_size的值,
那么innodb_buffer_pool_chunk_size的值会被服务器自动设置为innodb_buffer_pool_size/innodb_buffer_pool_instances的值。
那么innodb_buffer_pool_chunk_size的值会被服务器自动设置为innodb_buffer_pool_size/innodb_buffer_pool_instances的值。
可以用下边的命令查看Buffer Pool的状态信息:
SHOW ENGINE INNODB STATUS
0 条评论
下一页
为你推荐
查看更多