MySQL内存结构
# MySQL内存结构
# Free链表
当需要把磁盘的中的页加载到Buffer Pool中时,先从free链表中获取一个空白页对应的指针,然后就可以把数据页放到指定的位置上了,放到指定位置上再删掉空白节点
# flush链表
当缓存在buffer pool中的数据页发生修改时,通常我们也称之为脏也,就是buffer pool中的数据和磁盘的数据不一致了,简单的做法,就是把buffer pool的数据直接同步到磁盘上,但如果频繁的同步,会导致性能很低,所以mysql并不是立即把修改同步到磁盘,而是在未来某个时间点才进行同步,但mysql怎么知道后面同步哪些数据呢?mysql设计了flush链表,用来记录buffer pool中有哪些脏页
# LRU链表
buffer pool的大小是固定的,如果buffer pool满了,这时有新的数据页想放入buffer pool,这时就得把buffer pool的数据页进行淘汰,就需要一个淘汰算法 LRU,buffer pool中最近被用得比较少的数据页优先淘汰,所以MySQL设计了一个LRU链表,用来记录buffer pool中数据页的访问情况 在访问某页时,如果该页不在buffer pool中,则直接把该页加入到LRU链表的头部,如果该页在buffer pool中,则把该页移到LRU链表的头部,这样LRU链表的尾部就是最近最少使用的数据页,那么优先淘汰
- 如果有人select * from table1;呢? 按上面的分析逻辑,那么一旦执行该SQL就可能把buffer pool中的内容全部淘汰掉,这样本来的缓存的数据就会被淘汰掉 针对这种情况,MySQL把LRU链表按比例分成两截:
- 热数据区域,用来存放使用频率非常高的数据页
- 冷数据区域,用来存放使用频率不高的数据页
- 通过innodb_old_blocks_pct可以调整区域比例
设计升级后,对于一个新的数据页,一开始会加入到冷数据区域的头部,后续此数据页又被访问到,则会把此数据页从冷数据区域移到热数据页头部。
- 升级LRU链表后,还是否存在问题呢?
还是会有的,因为如果是全表扫描,那么实际上相邻的两条或多条记录很可能是同一数据页,而每次都去读取这些记录,都算是在访问数据页,那么还是会出现“换血“的情况
解决办法: 针对每个数据页增加一个访问时间,对于某个在冷数据区域的数据,如果后续的访问时间和添加到冷数据区别的访问时间这之间的间隔小于某个指定时间,则不会把该数据页移到热数据区别
可以通过innodb_old_blocks_time指定时间阈值,默认1000ms
对于热数据区域的数据,相对来说使用频率比较高,如果每次使用该区域的数据都把数据页移动到链表的头部,这样很影响性能,为了提高性能,MySQL实际上只针对热数据区域的后1/4区域的数据页,才会移动到链表头部