MYSQL专题(四):buffer pool

Buffer Pool

  • 我们对数据库执行增删改操作的时候,不可能直接更新磁盘上的数据,如果直接对磁盘进行随机读写操作,那速度是相当慢,随便一个大磁盘文件的随机读写操作,都有可能要几百毫秒。
  • 我们在对数据库执行增删改操作的时候,实际上主要都是针对内存里的Buffer Pool的数据进行的,也就是你实际上主要是对数据库的内存里的数据结构进行增删改操作。
    MYSQL专题(四):buffer pool_第1张图片
Buffer Pool的大小
  • Buffer Pool默认大小是128M,
  • innodb_buffer_pool_size = xxxxx,可以设置大小
buffer pool的结构
数据页:mysql抽象出来的数据单元
  • 数据库的核心数据模型:表+字段+行
  • mysql对数据抽象出来了一个数据页的概念,他是把很多行数据放在一个数据页里面,也就是说我们的磁盘文件就是会有很多的数据页,每一页数据放了很多行数据。

MYSQL专题(四):buffer pool_第2张图片
如果我们要更新一行数据,数据库会找到这行数据所在的数据页,然后把磁盘文件里把这行数据所在的数据页直接给加载到Buffer Pool里面去了;也就是说Buffer Pool中存放的是一个个数据页
MYSQL专题(四):buffer pool_第3张图片

  • 对于每一个缓存页,他实际上都会有一个描述信息,这个描述信息大体可以认为是用来描述这个缓存页的,他包含了:这个数据页所属的表空间,数据页的编号,这个缓存页在buffer Pool中的地址以及别的一些二数据
  • 每个缓存页都会对应一个描述信息,这个描述信息本身也是一块数据,在buffer Pool中,每个缓存页的描述信息放在最前面,每个缓存页放在后面

MYSQL专题(四):buffer pool_第4张图片

  • buffer pool中的描述数据(控制数据)大概相当于缓存页大小的百分之5左右,
初始化buffer pool
  • 数据库一启动,就会按照用户设置的buffer pool的大小,稍微加大一点,然后去找操作系统申请一块内存区域,作为buffer pool的内存区域。
  • 内存申请完毕之后,数据库就会按照默认的缓存页的16KB的大小以及对应的800个字节左右的描述数据的大小,在buffer pool中划分出一个个缓存页和对应的描述信息
如何知道缓存页是空闲的:free链表
  • 数据库会为buffer pool设计一个free链表,他是一个双向链表数据结构,这个free链表里,每个节点就是一个空闲的缓存页的描述数据块地址;也就是如果一个缓存页是空闲的,那么他的描述数据块就会被放如这个free链表中

  • MYSQL专题(四):buffer pool_第5张图片

  • 基础节点:存放free链表的头节点地址,尾节点地址,还有free链表里当前有多少个节点。

  • 每次把数据放到缓存页中,就把这个节点在free链表删除

如何知道数据页有没有被缓存
  • 数据库还会有一个hash表数据结构,他会用表空间号+数据页号作为key,然后缓存页的地址作为value
  • 然后每次读取一个缓存页到缓存之后,都会在这个hash表中写入一个key-value对
    MYSQL专题(四):buffer pool_第6张图片
flush链表

我们对缓存页增删改查后,缓存页上的数据就变成了脏数据
MYSQL专题(四):buffer pool_第7张图片

  • flush链表是存储被修改过的缓存页,用来记录脏页,后面好方便刷到磁盘中
    MYSQL专题(四):buffer pool_第8张图片
buffer Pool CRUD的流程
  • 查询数据和修改数据,都要先把数据页加载到缓存页中
    MYSQL专题(四):buffer pool_第9张图片
  • 一直加载的话 缓存页就会不够
    MYSQL专题(四):buffer pool_第10张图片
  • 要淘汰一些缓存数据—根据淘汰策略、
    MYSQL专题(四):buffer pool_第11张图片
    淘汰策略要考虑缓存命中率的问题
引入LRU链表来判断哪些缓存页不常用

MYSQL专题(四):buffer pool_第12张图片
MYSQL专题(四):buffer pool_第13张图片

  • 每次没有空闲的缓存页的时候,就找出URL链表的尾部,把他刷到磁盘中去,腾出空闲空间。

MYSQL的预读机制

  • 预读在LRU机制中会存在巨大隐患,预读就是当你从磁盘上加载一个数据页的时候,他可能会连带着把这个数据页相邻的其他数据页加载到缓存中。可能最后只有第一个缓存页被访问了,通过预读机制加载的缓存页其实没人访问,但是他排在URL队列的前面。
    MYSQL专题(四):buffer pool_第14张图片
    此时如果没有空闲缓存页了,那么要淘汰的不是没有读过的预读进来的缓存页,而是之前经常读的放在队尾的缓存页。
什么情况会触发MYSQL缓读机制
  • innodb_read_ahead_threshold,默认值是56,也就是如果顺序访问了一个区里的多个缓存页,访问的数据页的数量超过了这个阈值,就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存中去
  • innodb_random_read_ahead,默认是off;如果buffer pool缓存了一个区的13个连续的数据页,而且这些数据页都是比较频繁会被访问的,此时就会直接触发预读机制,把这个区里的其他数据页都加载到缓存里去。
另一个可能导致频繁被访问的缓存页被淘汰的场景:全表扫描
基于冷热数据分离的思想设计LRU链表
  • 为了解决上面的问题,真正MYSQL在设计LRU链表的时候,采取的实际上是冷热数据分离的思想。
  • 真正的LRU链表会被拆分成两个部分,一部分是热数据,一部分是冷数据,这个冷热数据的比例是由innodb_old_blocks_pct参数控制的,默认是37(冷数据占百分之37)
    MYSQL专题(四):buffer pool_第15张图片
    数据页第一次被加载到缓存的时候,这个时候缓存页会被放到冷数据区域的链表头部,
    MYSQL专题(四):buffer pool_第16张图片
    innodb_old_blocks_time参数,默认值是1000 1000ms,也就是说一个数据页被加载到缓存页之后,在1s之后,你再次访问这个缓存页,他才会被挪动到热数据区域的链表头部去。
    MYSQL专题(四):buffer pool_第17张图片
  • 如果缓存页不够,就淘汰LRU链表中的冷数据区域的尾部的缓存页,他们肯定是之前被加载进来的,而且加载进来1s过后都没人访问过,说明这个缓存页压根没人愿意去访问他,他就是冷数据。
    MYSQL专题(四):buffer pool_第18张图片
  • 这样,实际上冷数据区域放的都是加载进来的缓存页,最多在1s内被访问过,之后就再也没访问过的冷数据缓存页。
  • 而加载进来之后在1s过后还经常被访问的缓存页,都放在热数据区域里,他们进行了冷热数据的隔离。
Redis里面的缓存数据怎么优化
  • 如果有太多的商品(1亿),不可能全部放到缓存里面,可以每天统计出来哪些商品被访问的次数最多,然后晚上的时候,启动一个定时作业,把这些热门商品的数据,预加载到redis里面。
LRU链表的热数据区域的优化
  • 热数据区域里的缓存页都可能进程被访问,所以频繁的移动不是很好。
  • 所以,LRU链表的热数据区域的访问规则被优化了一下,即你只有在热数据区域的后3/4部分的缓存页被访问了,才会给你移动到链表头部去。尽可能减少链表中节点的移动。
buffer pool 的缓存页以及几个链表的使用

buffer pool在运行中被使用的时候,实际上会频繁的从磁盘上加载数据页到他的缓存里去,然后free,flush,lru链表都会在使用的时候同时被使用

定时把LRU尾部的部分缓存页刷入磁盘
  • 第一个时机并不是在缓存页满的时候,才会挑选LRU冷数据区域尾部的几个缓存页刷入磁盘,而是有一个后台线程,他会运行一个定时任务,这个定时任务每隔一段时间就会把LRU链表的冷数据区域的尾部的一些缓存页(free链表还没用完),刷入磁盘里去,清空这几个缓存页,把他加入到free链表去。
    MYSQL专题(四):buffer pool_第19张图片

  • 这个后台线程同时也会在mysql不那么繁忙的时候,找一个时间把flush链表中的缓存页都刷入磁盘中,这样被你修改过的数据迟早会被刷入磁盘的。(不会说lru热数据一直没被刷入磁盘)

  • 如果真的没有空闲的缓存页的时候,此时可能所有的free链表都被使用了;此时会从LRU链表的冷数据区域的尾部找到一个缓存页,把他刷入磁盘和清空,然后把这个数据页加载到这个腾出来的空闲缓存页中去。

通过多个Buffer Pool来优化数据库的并发性能
  • 如果mysql同时接收到多个请求,他自然会用多个线程来处理这么多个请求,每个线程会负责处理一个请求。
    MYSQL专题(四):buffer pool_第20张图片
  • 多个线程并发访问一个buffer pool 必然要加锁,很多线程可能要串行排队,一个一个的依次执行自己要执行的操作。因为是对内存进行处理,而且对链表的更新也是指针操作,所以性能还可以。
  • 但是有时候可能你要对磁盘进行操作,其实也不是很好。所以可以给mysql设置多个buffer pool来优化他的并发能力。
多个buffer pool优化并发能力
  • 一般来说,mysql默认的规则是,如果你给buffer pool分配的内存小于1GB,那么最多就只会给你一个buffer pool
  • 但是如果你的机器内存很大,那么你必然会给buffer pool分配较大的内存,比如你给他8个内存,那么同时你可以设置多个buffer pool
innodb_buffer_pool_size = 8589934592 #总共
innodb_buffer_pool_instances = 4

MYSQL专题(四):buffer pool_第21张图片

通过chunk来支持数据库运行期间的buffer pool动态调整
  • buffer pool如何在数据库运行的期间,动态调整大小。
  • mysql设计了一个chunk机制,一个buffer pool是由很多个chunk组成的,他的大小是通过innodb_buffer_pool_chunk_size控制的,默认是128M
    MYSQL专题(四):buffer pool_第22张图片
    每一个buffer pool里有很多个chunk,每个chunk就是一系列的描述数据块和缓存页,每个buffer pool是共用一套free flush lru链表的。
  • 有了chunk机制,可以支持动态调整buffer pool的大小了。
  • 如果要从8G->16G,只需要申请一系列的128M大小的chunk就可以了,只要每个chunk是连续的128M的内存就行。然后把这些申请到的chunk内存分配个buffer pool就行。
生产环境配置buffer pool大小
  • 建议buffer pool设置为机器内存的50%~60%
  • buffer pool总大小 =(chunk大小*buffer pool数量)的倍数

MYSQL专题(四):buffer pool_第23张图片

你可能感兴趣的:(Mysql,数据库)