mysql——InnoDB 的 Buffer Pool
即使我们只需要访问⼀个⻚的⼀条记录,那也需要先把整个⻚的数据加载到内存中
Buffer Pool是啥
为了缓存磁盘中的⻚,在MySQL服务器启动的时候就向操作系统申请了⼀⽚连续的内存,他们给这⽚内存起了个名,叫做Buffer Pool(中⽂名是缓冲池)。
- Buffer Pool中默认的缓存⻚⼤⼩和在磁盘上默认的⻚⼤⼩是⼀样 的,都是16KB。
- 每⼀个缓存⻚都创建了⼀些所谓的控制信息, 这些控制信息包括该⻚所属的表空间编号、⻚号、缓存⻚在Buffer Pool中的地址、链表节点信息、⼀些锁信息以及LSN信息。
- 控制块和缓存 ⻚是⼀⼀对应的,它们都被存放到 Buffer Pool 中,其中控制块被存 放到 Buffer Pool的前边,缓存⻚被存放到 Buffer Pool 后边
每个控制块⼤约占⽤缓存⻚⼤⼩的5%。
查找缓存大小:
-- 缓存大小
show variables like 'innodb_buffer_pool_size';
innodb_buffer_pool_size并不包含这部分控制块占⽤的内存空 间⼤⼩。
free链表
free链表:所有空闲的缓存⻚对应的控制块作为⼀个节点组成的⼀个链表。
在MySQL5.7.21这个版本⾥,每个基节点只占⽤40字节⼤⼩,并不包含在为 Buffer Pool申请的⼀⼤⽚连续内存空间之内。(之后其他链表与之一样)
缓存⻚的哈希处理
表空间号 + ⻚号是⼀个key,缓存⻚就是对应的value
flush链表的管理
如果我们修改了Buffer Pool中某个缓存⻚的数据,那它就和磁盘 上的⻚不⼀致了,这样的缓存⻚也被称为脏⻚(英⽂名:dirty page)。
flush链表:所有脏⻚对应的控制块作为⼀个节点组成的⼀个链表。
刷新脏⻚到磁盘
- 从LRU链表的冷数据中刷新⼀部分⻚⾯到磁盘。
- 从flush链表中刷新⼀部分⻚⾯到磁盘。
LRU链表的管理
目的:当缓存内存不够时,需要移除一些缓存,利用最近最少访问规则。尽量⾼效的提⾼ Buffer Pool 的缓存命中率。
InnoDB的优化实现,按照⼀定⽐例分成两截,分别是:
- ⼀部分存储使⽤频率⾮常⾼的缓存⻚,所以这⼀部分链表也叫做热数据,或者称young区域。
- 另⼀部分存储使⽤频率不是很⾼的缓存⻚,所以这⼀部分链表也叫做冷数据,或者称old区域。
我们是按照某个⽐例将LRU链表分成两半 的,不是某些节点固定是young区域的,某些节点固定是old区域 的,随着程序的运⾏,某个节点所属的区域也可能发⽣变化。
-- LRU链表old区域比例,默认37
show variables like 'innodb_old_blocks_pct';
优化:
- 当磁盘上的某个⻚⾯在初次加载到 Buffer Pool中的某个缓存⻚时,该缓存⻚对应的控制块会被放 到old区域的头部。
- 在对某个处在old区域的缓存⻚进⾏第⼀次访问时就在它对应的控制块中记录下来这个访问时间,如果后续的访问时间与第⼀次访问的时间在某个时间间隔内,那么该⻚⾯就不会被 从old区域移动到young区域的头部,否则将它移动到young区 域的头部。
- 只有被访问的缓存⻚位于young区域的1/4的后边,才会被移动到LRU链表头部
多个Buffer Pool实例
在Buffer Pool特别⼤的时候,我们可以把它们拆分成 若⼲个⼩的Buffer Pool,每个Buffer Pool都称为⼀个实例,它们都是独⽴的,独⽴的去申请内存空间,独⽴的管理各种链表,所以在多线程并发访问时并不会相互影响,从⽽提⾼并发处理能⼒。
总结
- 磁盘太慢,⽤内存作为缓存很有必要。
- Buffer Pool本质上是InnoDB向操作系统申请的⼀段连续的 内存空间,可以通过innodb_buffer_pool_size来调整它 的⼤⼩。
- Buffer Pool向操作系统申请的连续内存由控制块和缓存⻚ 组成,每个控制块和缓存⻚都是⼀⼀对应的,在填充⾜够多的 控制块和缓存⻚的组合后,Buffer Pool剩余的空间可能产 ⽣不够填充⼀组控制块和缓存⻚,这部分空间不能被使⽤,也 被称为碎⽚。
- InnoDB使⽤了许多链表来管理Buffer Pool。
- free链表中每⼀个节点都代表⼀个空闲的缓存⻚,在将磁盘中 的⻚加载到Buffer Pool时,会从free链表中寻找空闲的缓 存⻚。
- 为了快速定位某个⻚是否被加载到Buffer Pool,使⽤表空 间号 + ⻚号作为key,缓存⻚作为value,建⽴哈希表。
- 在Buffer Pool中被修改的⻚称为脏⻚,脏⻚并不是⽴即刷 新,⽽是被加⼊到flush链表中,待之后的某个时刻同步到磁 盘上。
- LRU链表分为young和old两个区域,可以通过 innodb_old_blocks_pct来调节old区域所占的⽐例。⾸次 从磁盘上加载到Buffer Pool的⻚会被放到old区域的头部,在innodb_old_blocks_time间隔时间内访问该⻚不会把它 移动到young区域头部。在Buffer Pool没有可⽤的空闲缓 存⻚时,会⾸先淘汰掉old区域的⼀些⻚。
- 我们可以通过指定innodb_buffer_pool_instances来控 制Buffer Pool实例的个数,每个Buffer Pool实例中都有 各⾃独⽴的链表,互不⼲扰。
- ⾃MySQL 5.7.5版本之后,可以在服务器运⾏过程中调 整Buffer Pool⼤⼩。每个Buffer Pool实例由若⼲ 个chunk组成,每个chunk的⼤⼩可以在服务器启动时通过启 动参数调整。
- 可以⽤下边的命令查看Buffer Pool的状态信息
SHOW ENGINE INNODB STATUS
参考文献:MySQL 是怎样运行的:从根儿上理解 MySQL