14 | 当我们更新Buffer Pool中的数据时,flush链表有什么用

1、buffer pool是否会有内存碎片?

2、脏数据页

进行增删改时,操作缓存页,缓存页的数据跟磁盘上的数据页数据不一致,生成脏页
image.png

3、哪些缓存页是脏页

1)不可能所有的缓存页都刷回磁盘,比如查询的时候读到buffer pool里面去,但是根本没修改过,导致一直没往磁盘里写入。
问题1:如果我读取到 buffer pool里,根本没修改过,那跟磁盘上的数据页数据一致啊,会是脏数据吗
答:不是脏数据
问题2:如果我没修改过,就不往磁盘里回写入了吗
答:是的
2)flush链表

  • 数据结构:双向链表
  • 凡是被修改过的缓存页,都把描述数据的地址加入到flush链表中去

image.png
4、flush链表结构伪代码
image.png

15 | 当Buffer Pool中的缓存页不够的时候,如何基于LRU算法淘汰部分缓存?

1、把一个数据页加载到空闲缓存中去,free链表中就减少一个空闲缓存

当free链表中没有空闲缓存怎么办

2、淘汰一些缓存数据,淘汰谁

淘汰缓存页:把一个缓存页里被修改过的数据,刷回到磁盘数据页里去,然后这个缓存页就可以清空了。
问题:应该把哪个被修改过数据的缓存页给刷回去?第3点解答

3、缓存命中率的引入

缓存页A:100次请求,有30次在查询和修改。缓存命中率高
缓存页B:100次请求,有1次在查询。缓存命中率低
如果要刷回磁盘,肯定选命中率低的
问题:如果算命中率?第4点解答

4、引入LRU链表来判断哪些缓存页是不常用的

LRU:Least Recently Used
最近被加载数据的缓存页,都会被放到LRU链表的头部
5、表空间和数据页关系
逻辑概念:表、列、行
物理概念:表空间、数据页

16 | 简单的LRU链表在Buffer Pool实际运行中,可能导致哪些问题


1、Mysql的预读机制

当你从磁盘上加载一个数据页的时候,可能会连带着把这个数据页的相邻数据页,也加载到缓存中去
image.png

2、哪些情况会触发预读机制

1)参数:innodb_read_ahead_threshold,默认值56
顺序访问了一个区里的多个数据页,访问数量超过这个阈值,会触发机制,把相邻的数据页也加载到缓存中去
2)参数:innodb_random_read_ahead,默认OFF
buffer pool 里缓存了一个区里13个连续的数据页,且被频繁访问,会直接触发机制…

3、全表扫描

1)sql :select * from users;
2)执行上面的sql会导致把表里所有的数据页,全部从磁盘加载到buffer pool里去
那么LRU链表里是所有的数据页,表尾的数据页有可能是被频繁访问的,这样子可能会出现频繁访问的数据页被淘汰掉了。

4、总结

LRU链表机制,对于预读机制和 全表扫描 都是不行的

17 | MySQL是如何基于冷热数据分离的方案,来优化LRU算法的

1、mysql为什么设定预读机制

为了提升性能

2、基于冷热分离的思想设计LRU链表

1)16中的所有问题都是因为所有的缓存页都混在一个LRU链表里导致的
2)真正的LRU链表,会被拆成两部分:一部分是热数据,一部分是冷数据
3)冷热数据比例:innodb_old_blocks_pct 参数控制的,默认37(冷数据占37%)
image.png

3、数据页第一次被加载到内存的时候,被放在LRU链表冷数据的链表头部

image.png

4、冷数据区的缓存页什么时候会被放到热数据区域

1)一个数据页被加载到缓存页之后,在1s后,你又去访问了这个缓存页,此时这个缓存页就被会挪动到热数据区域的链表头部
2)时间1s的设定:innodb_old_blocks_time 参数,默认值1000(毫秒)
image.png

问题:那热数据区域的数据会移动到冷区里吗
自我猜测:是通过那个冷热比例?热的占到7成,如果再来往热的移动一个,热区的最后一个就变成冷的?

18 | 基于冷热数据分离方案优化后的LRU链表,是如何解决之前的问题的

1、预读和全表扫描加载进来的一大堆缓存页,放在LRU链表冷区域前面

2、缓存页不够,淘汰缓存页方案

直接淘汰冷区尾部的缓存页

19 | MySQL是如何将LRU链表的使用性能优化到极致的

1、java系统,redis里存放的应该是经常访问的热数据缓存

2、设计缓存机制时,需要考虑热数据的缓存预加载

3、LRU热数据区域,如何进行优化的

问题:在热数据区域,如果访问了一个缓存页,是否会立马移动到热区域的头部
答:只有在热数据区域的3/4部分的缓存页被访问了,才会被移动到链表头部
前1/4不会移动,如问题所问,会频繁移动,性能不好

20 | 对于LRU链表中尾部的缓存页,是如何淘汰他们刷入磁盘的

1、定时把LRU尾部的部分缓存页刷入磁盘

后台线程,运行一个定时任务,每隔一段时间就会把LRU链表的冷数据区域尾部缓存页,刷入磁盘里,
清空这几个缓存页,加入到free链表里去

2、把flush链表的缓存页定时刷入磁盘(被修改的数据迟早刷入磁盘)


21 | 生产经验:如何通过多个Buffer Pool来优化数据库的并发性能

1、buffer pool 在访问时需要加锁

mysql同时收到了多个请求,多个线程处理多个请求,每个线程负责一个。
多线程并发访问一个buffer pool ,加锁,让一个线程先完成一系列操作,然后释放锁,接着下一个线程接着再操作
image.png

2、多线程并发访问,数据库的性能如何

一个buffer pool 即使多个线程加锁串行着排队执行,性能也ok
3、如何提高性能:多个buffer pool 优化并发能力
image.png


22 | 生产经验:如何通过chunk来支持数据库运行期间的Buffer Pool动态调整?

1、buffer pool 能在运行期间动态调整大小吗

以上的理论不能。但是后面可以用chunk来支持动态
比如,buffer pool本来是8G,调整为16G,此时需要向操作系统申请一块16G的连续内存,然后把buffer pool中的缓存页、描述数据、各种链表 ,都拷贝到新的内存中去,极为耗时

2、如何基于chunk机制把buffer pool给拆小

1)buffer pool 是由很多chunk组成的
2)chunk大小是innodb_buffer_pool_chunk_size参数控制的,默认128MB
3)chunk里面就是一系列的描述数据块和缓存页,每个buffer pool 里的多个chunk共享一套free\flush\lru链表
image.png

3、基于chunk机制,动态调整buffer pool大小

buffer pool 8G,动态加到16GB,申请一系列128MB的chunk就可以。每个chunk是连续的128内存。然后把这些chunk分配给buffer pool

23 | 生产经验:在生产环境中,如何基于机器配置来合理设置Buffer Pool

1、生产环境应该给buffer pool 多少内存

机器内存的 50%~60%

2、设置多少个buffer pool,以及chunk大小

buffer pool总大小=(chunk大小*buffer pool数量)的倍数


24| 我们写入数据库的一行数据,在磁盘上是怎么存储的?

1、行格式

可以对这个表指定行存储的格式是什么样的,比如COMPACT格式、
例如:CREATE TABLE table_name(columns) ROW_FORMAT= COMPACT

25 | (变长字段的长度列表)对于VARCHAR这种变长字段,在磁盘上到底是如何存储的


1、一行数据在磁盘上存储时,都包含哪些数据

变长字段的长度列表,null值列表,数据头,column01的值,column02的值,column0N的值

2、变长字段在磁盘中怎么存储的

假设

COL_A | VARCHAR(10) COL_B | CHAR(1) COL_C | CHAR(1)
hello a a
hi b b

把上述两行数据写入一个磁盘文件,两行数据是挨在一起的,有可能是:
hello a a hi b b

3、存储在磁盘文件里的变长字段,为什么难以读取

hello a a hi b b 哪些是第一行数据,难以判断

4、引入变长字段的长度列表,解决一行数据的读取问题

在存储 hello a a 这行数据的时候,带上附加信息:
比如 hello 长度是5,16进制:0x05。
所以这行数据在磁盘文件里存储的时候,格式是 :
0x05 null值列表 数据头 hello a a 0x02 null值列表 数据头 hi b b

5、多个变长字段,如何存放他们的长度

逆序存放
0x03 0x02 0x05 null值列表 数据头 hello hi hao a a

26 | (null值列表)一行数据中的多个NULL字段值在磁盘上怎么存储


假设 列B 列C 列 D允许为空

COL_A | VARCHAR(10) NOT NULL COL_B | CHAR(1) NULL COL_C | CHAR(1) NULL COL_D | VARCHAR(2) NULL
hello a cc
hi b b

1)每个定义可以为空的字段,都有一个二进制bit位的值,
2)bit = 1,为NULL
bit = 0,不是NULL
3)例子中的bit位应该是 :100
4)bit实际存放在null值列表时,是逆序存放 : 001
整体一行数据:
0x02 0x05 001 数据头 hello NULL a cc