MySQL源码学习:简述InnoDB的BP LRU策略

本文简要说明InnoDB的Buffer Pool(BP)的结构、基本运行方式和策略。

1、LRU的基本形态

由于涉及到淘汰机制,Buffer Pool (BP)内需要一个LRU链。这个LRU链表的基本形态如下:

从图中看到,LRU是一个链表(双向,图中没有画出反向指针)。

同时有一个LRU_old(buf_pool->LRU_old)指针指向链表中间的一个page。 LRU_old指向的page及之后直到end的page,都被称为”old page”, 内存中bpage->old==1。

LRU_old之前到start的所有page,被称为”young page”, 内存中bpage->old==0.

2、 从头开始

a) 在系统初始化时,所有的page都是空闲的,因此全部放在buf_pool->free链表中,此时buf_pool.LRU={count = 0, start = 0x0, end = 0x0}, 当然buf_pool->LRU_old=0x0.

b) 当有page请求时,从buf_pool->free中取出page,放入LRU中。需要注意的是,在LRU->count小于512(BUF_LRU_OLD_MIN_LEN)时,所有的page都被标为young,插入队头。

c) 当LRU->count达到512时候,依次作如下动作

i. 将buf_pool->LRU_old, 赋值为LRU.start, 将LRU中的所有page都设置为old (buf_LRU_old_init)

ii. 调用buf_LRU_old_adjust_len,调整buf_pool->LRU_old的适当位置,成为上图的基本形态。默认配置下old page数目占3/8.

d) 有新的page再进入LRU时,先插入到LRU_old的next位置,也就是先标为old,下次访问时再调整为LRU.start,再改为young。

e) 当BP满了以后,即LRU.count为page总数,再需要访问新的page时,就只能从LRU末尾删除,再补入。

3、 一点讨论

1) 步骤d中所说的”下次访问”,实际上在放入LRU之后马上会发生。在buf_page_get_gen 调用 buf_page_set_accessed_make_young,若满足条件则将此page调整为LRU.start。

需要说明一个参数buf_LRU_old_threshold_ms。当一个old page距第一次被访问的时间大于等于buf_LRU_old_threshold_ms时,再次被访问的时候,就会被调整为LRU.start.

也就是说,当buf_LRU_old_threshold_ms为默认设置的0时,新插入的page都是先放到LRU_old之后,马上被调整到LRU.start。

而这个“调整”,也不是简单的指针重赋,而是将这个page 从LRU中移除,再插入到LRU头部。而从LRU中删除page的时候,若刚好碰到临界值(<512),会遍历整个队列,全部设置为young。

虽然都是内存操作,但整个过程显得比较粗暴。大多数的系统中并不会修改buf_LRU_old_threshold_ms的默认值,因此这个过程则一直在被重复调用。

实际上,在第一次访问page需要入LRU队列的时候,完全可以先判断一下buf_LRU_old_threshold_ms的值,若为0,则直接插入到LRU头部。对应的代码在buf_page_init_for_read中的两处调用buf_LRU_add_block(bpage, TRUE)。

2) 当前的调用流程

buf_page_set_accessed_make_young(&block->page, access_time); --> buf_LRU_make_block_young --> buf_LRU_remove_block(bpage); buf_LRU_add_block_low(bpage, FALSE);

4、 一点声明

本文基本上是为下周组里要来的小实习生作个介绍,因此写得又细又浅,欢迎拍砖。可以踩,但请果断留下意见。

时间: 2024-11-08 21:24:13

MySQL源码学习:简述InnoDB的BP LRU策略的相关文章

MySQL源码学习:InnoDB的ib_logfile写入策略

ib_logfile是InnoDB的事务日志文件.本文简要说明其写入时机.写入策略及如何保证数据安全. 1. 基本概念 a) ib_logfile文件个数由innodb_log_files_in_group配置决定,若为2,则在datadir目录下有两个文件,命令从0开始,分别为ib_logfile0和ib_logfile. b) 文件为顺序写入,当达到最后一个文件末尾时,会从第一个文件开始顺序复用. c) lsn: Log Sequence Number,是一个递增的整数. Ib_logfil

MySQL源码学习:InnoDB关于group commit的简单QA

    前天同事问了个问题,今天又再翻了下group commit.关于这个话题Kristian Nielsen有一个很详尽的系列文章(http://kristiannielsen.livejournal.com/12254.html), 有四个页面,文中有链接.这里列出一些细节,主要是对上面文章补充一下. Q:什么是group commit. A:1) 简单说就是:好几个线程写文件,然后一个线程fsync: 2) 只有事务日志(ib_logfile)用到: 3) 注意是多个线程(多用户).一个

Mysql源码学习笔记 偷窥线程_Mysql

感觉代码有些凌乱,注释代码都写的比较随意,好像没有什么统一的规范,不同的文件中代码风格也有差异,可能Mysql经过了很多牛人的手之后,集众牛人之长吧.也可能是我见识比较浅薄,适应了自己的代码风格,井底之蛙了,总之还是怀着敬畏的心情开始咱的源码之旅吧.本人菜鸟,大神轻拍. Mysql可以启动起来了,应该怎么学习呢?总不能从main开始一步一步的看吧,Mysql作为比较底层的大型软件,涉及到数据库实现的方方面面,没有厚实的数据库理论基础和对Mysql各个模块相当的熟悉,从main开始势必会把自己引入

MySQL源码学习: concat + outfile的bug 原因分析

项目中碰到一个bug,需要将MySQL表中的数据导出,字段中间用逗号隔开. 1.复现 步骤: 版本 5.1.48 a) 准备数据 CREATE TABLE `test` ( `id` int(11) DEFAULT NULL, `data` char(10) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=gbk; insert into tad2 values (1,'丁\\奇'); b) select concat(id, data) from te

MySQL源码学习:ib_logfile、bin-log与主从同步

今天研究MySQL主从同步的同事问了一个问题,如果InnoDB写完ib_logfile后,服务异常关闭.会不会由于主库能够根据ib_logfile恢复数据,而由于bin-log没写导致从库同步时少了这个事务?或者反之,bin-log写成功,而ib_logfile没有写完,导致从库执行事务,而主库不执行? 这会导致主从不一致. 本文简要说明下这个问题. 1. 写入流程 源码sql/handler.cc: ha_commit_trans { - if ((err= ht->prepare(ht, t

MySQL源码学习:关于整型判断的一个bug

问题: 这个bug来源于官方的一个bug报告,感谢@印风_小希 . 现象很容易描述,直接上例子. 5.1以后的版本都有此问题. CREATE TABLE `tb` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, KEY `a` (`a`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into tb values (1,2),(2,5),(3,8),(4,6); select * from t

MySQL源码学习:索引使用统计功能

今天刚刚知道Oracle有个索引统计的功能,可以统计每个索引的使用次数.作为一个Oracle的门外汉,我还是再次感叹人家做的是真细致.第二个想法就是给MySQL也加上. Percona版本的information_schema.innodb_index_stats 已经有索引的统计信息,我们就在巨人的肩膀上踩一脚了. 先来看下原来的表结构. CREATE TEMPORARY TABLE `INNODB_INDEX_STATS` ( `table_schema` varchar(192) NOT

MySQL源码学习:关于 &#039;A&#039; ==&#039;A &#039;的问题

  昨天一位同事问到一个问题,他的MySQL中导入数据的时候,发现唯一索引冲突,原因是有两行记录,区别只是有一条记录多了最后的一个空格. 希望有方法将他们设置不同. 复现: CREATE TABLE `t` ( `c` varchar(20) NOT NULL DEFAULT '', PRIMARY KEY (`c`) ) ENGINE=InnoDB DEFAULT CHARSET=gbk; insert into t(c) values("A"); insert into t(c)

MySQL源码学习:关于慢查询日志中的Rows_examined=0

最近在一个项目中DBA同学问了一个问题:为什么很多慢查询日志中显示 Rows_examined : 0? 需要说明的是, 这类慢查询语句都是类似 select count(*) from (-)t; 在说明这个问题之前,我们先指出两个相关背景: 1.MySQL的临时表,都是MyISAM的. 2.MyISAM表中的记录总数是额外存储的,count(*)的时候不需要遍历数据. 3.把count(*)转换为取一个const值这件事情,是在优化(optimize)阶段作的. 问题分析: 这个值对应于代码