思考mysql内核之初级系列4--innodb缓冲区管理
我们在前面讨论了一些mysql的基础知识,现在将要开始进入innodb引擎,从这里开始我们将开始代码的结构分析,innodb的内容分析之后,将反过来分析查询优化引擎。今天,我们先来讨论innodb缓冲区管理。
文件:
D:\mysql-5.1.7-beta\storage\innobase\include\buf0buf.h
D:\mysql-5.1.7-beta\storage\innobase\buffer \buf0buf.c
Bingxi和alex开始交流innodb缓冲区结构(不考虑AWE的情况)。
Bingxi:“alex,咱们都知道所谓缓冲区就是将文件缓存,避免重复操作数据文件,这样可以有效地减少io。”
Alex:“是的,没错。缓冲区的大小是根据配置文件生成,配置文件中innodb_buffer_pool_size文件,除以16k就得到了对应的页面数。”
Bingxi:“嗯,是的。我们现在在debug的情况进行调试,显示的缓冲的页数为512页。也就是我们能够缓存的数据大小为512*16k=8M。这我们可以通过命令行来验证下。我们可以看到设置的大小为8388608,也就是8M,以16k一页计算,也就是512页。
以下是代码片段: mysql> show variables like ’innodb_buffer_pool_size’; +-------------------------+---------+ | Variable_name | Value | +-------------------------+---------+ | innodb_buffer_pool_size | 8388608 | +-------------------------+---------+ 1 row in set (0.00 sec) |
执行show innodb status\G;查看其中的片段。从中可以看出buffer pool size果然为512,不过呢,我怎么看到free buffers为493,也就是有19页是使用。这个就奇怪,我没有执行查询语句啊。
以下是代码片段: ---------------------- BUFFER POOL AND MEMORY ---------------------- Total memory allocated 13244152; in additional pool allocated 176384 Buffer pool size 512 Free buffers 493 Database pages 19 Modified db pages 0 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages read 19, created 0, written 0 0.00 reads/s, 0.00 creates/s, 0.00 writes/s No buffer pool page gets since the last printout |
”
Alex:“因为innodb会有自己的一些系统表需要加载,也就是所谓的字典表。这个内容我们在以后讨论”
Bingxi:“嗯,好的,alex。咱们继续看buf0buf.h文件,我看buf_pool_struct是缓冲区的总结构。在其中记录了缓冲数据页管理、访问计数、LRU列表管理等等。我们先讨论下该结构的下面4个变量吧。
以下是代码片段: struct buf_pool_struct{ …… byte* frame_mem; byte* frame_zero; byte* high_end; ulint n_frames; …… }; |
”
Alex:“好吧,我们对着代码看吧。其实frame_mem就是分配的缓冲区的指针,但是这个指针不一定是16k对齐的,为了提升性能,进行了16k对齐,并将该值赋给frame_zero。high_end作为标识缓冲区的结尾。n_frames表示缓冲页的大小。
以下是代码片段: buf_pool_t* buf_pool_init( ulint max_size, ulint curr_size, ulint n_frames) //这三个值,在这里都是相等的。为了方便查看去掉了英文注释,建议对照代码 { …… //果然buf_pool_t是全局缓冲区管理结构,分配全局值buf_pool buf_pool = mem_alloc(sizeof(buf_pool_t)); …… //UNIV_PAGE_SIZE=16k,n_frames=512 //奇怪的是为什么分配了513个页,而不是512个页? buf_pool->frame_mem = os_mem_alloc_large( UNIV_PAGE_SIZE * (n_frames + 1), TRUE, FALSE); //如果分配失败,则返回 if (buf_pool->frame_mem == NULL) { return(NULL); } //调整字节,也就16k字节对齐,也就是frame是16k的整数倍。 //如果buf_pool->frame_mem是16k的整数倍,那么frame=buf_pool->frame_mem //否则frame>buf_pool->frame_mem and frame<buf_pool->frame_mem+16k,且frame能被frame整除 frame = ut_align(buf_pool->frame_mem, UNIV_PAGE_SIZE); //frame作为缓冲区的起点 buf_pool->frame_zero = frame; //buf_pool->high_end作为缓冲区的结尾 buf_pool->high_end = frame + UNIV_PAGE_SIZE * n_frames; …… } |
”
Bingxi:“我明白了,也缓冲的第0页的指针地址为frame_zero,第n页为frame_zero+n*16k(n从0开始)。”
Alex:“是的,是这样的。问你个问题,怎么知道这些数据缓冲页块当中哪些是空闲的,哪些是正在用的,哪些是被修改过的?”
Bingxi: “啊,我先看下代码。厄,我找到了,应该是另外一个结构体进行控制。从下面这个结构体中,我们可以看出,该结构指向了frame地址,也就是我们刚刚提到的缓冲页块。Space与offset标识着实际的硬盘文件,这样建立起来一个映射关系。也就是space与offset对应的硬盘页,映射到了frame缓冲块。因此在这里需要512(数据缓冲页块数量)个这样的结构。
以下是代码片段: /* The buffer control block structure */ struct buf_block_struct{ …… byte* frame; /* pointer to buffer frame which …… ulint space; /* space id of the page */ ulint offset; /* page number within the space */ …… } |
”
Alex:“是的,我们继续看buf_pool_init函数的代码片段,果然将第n个block与第n个frame进行关联。
以下是代码片段: buf_pool_t* buf_pool_init( ulint max_size, ulint curr_size, ulint n_frames) //为了方便讲解,这三个值,在这里都是相等的。为了方便查看去掉了英文注释,建议对照代码。差异性,留给读者去阅读。 { …… //分配了512个控制块,这里正好一个控制块,控制一个数据缓冲页块。 buf_pool->blocks = ut_malloc(sizeof(buf_block_t) * max_size); //如果分配失败则返回 if (buf_pool->blocks == NULL) { return(NULL); } //对应每一个控制块进行赋予对应的缓冲页指针 //第n个对应的指针为buf_pool->frame_zero + i * UNIV_PAGE_SIZE for (i = 0; i < max_size; i++) { //这行代码等价于:block=i + buf_pool->blocks block = buf_pool_get_nth_block(buf_pool, i); frame = buf_pool->frame_zero + i * UNIV_PAGE_SIZE; //通过另外一个数组管理block数组,这里可以不考虑 *(buf_pool->blocks_of_frames + i) = block; //调用函数,将第n个block与第n个frame进行关联 buf_block_init(block, frame); } …… } |
buf_block_init函数比较简单,我们跟踪进去看下。果然进行block与frame的关联了,但是呢,没有放入空闲列表。
以下是代码片段: static void buf_block_init( /*===========*/ buf_block_t* block, /* in: pointer to control block */ byte* frame) /* in: pointer to buffer frame, or NULL if in the case of AWE there is no frame */ { block->state = BUF_BLOCK_NOT_USED; //在这里进行block与frame的关联 block->frame = frame; block->awe_info = NULL; block->modify_clock = ut_dulint_zero; block->file_page_was_freed = FALSE; block->check_index_page_at_flush = FALSE; block->index = NULL; //特别注意这里,该块此时还没有放入空闲列表。 block->in_free_list = FALSE; block->in_LRU_list = FALSE; block->n_pointers = 0; //创建锁 rw_lock_create(&(block->lock)); ut_ad(rw_lock_validate(&(block->lock))); #ifdef UNIV_SYNC_DEBUG rw_lock_create(&(block->debug_latch)); rw_lock_set_level(&(block->debug_latch), SYNC_NO_ORDER_CHECK); #endif /* UNIV_SYNC_DEBUG */ } |
”
Bingxi:“哈哈,alex,你弱了吧。你再看看,在buf_pool_init函数中紧跟着就将这些block放入了空闲列表。
以下是代码片段: buf_pool_t* buf_pool_init( ulint max_size, ulint curr_size, ulint n_frames) //这三个值,在这里都是相等的。为了方便查看去掉了英文注释,建议对照代码 { …… for (i = 0; i < curr_size; i++) { //获得第n个block block = buf_pool_get_nth_block(buf_pool, i); if (block->frame) { //添加到空闲列表 UT_LIST_ADD_LAST(free, buf_pool->free, block); //并设置in_free_list状态为真 block->in_free_list = TRUE; } …… } |
”
Alex:“嗯,差不多,就先打住了,也该睡觉了。”
Bingxi:“ok,晚安。”
建议继续学习:
- Innodb IO优化-配置优化 (阅读:6755)
- Innodb分表太多或者表分区太多,会导致内存耗尽而宕机 (阅读:6202)
- gen_tcp发送缓冲区以及水位线问题分析 (阅读:5268)
- Innodb 表和索引结构 (阅读:4861)
- InnoDB线程并发检查机制 (阅读:4214)
- Innodb如何使用内存 (阅读:4075)
- 快速预热Innodb Buffer Pool的方法 (阅读:4036)
- Innodb文件表空间结构 (阅读:3836)
- InnoDB的缓存替换策略及其效果 (阅读:3710)
- 多版本并发控制:PostgreSQL vs InnoDB (阅读:3703)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:yzyangwanfu 来源: 杨万富的专栏
- 标签: innodb mysql内核 缓冲区
- 发布时间:2010-07-25 09:51:57
- [69] Twitter/微博客的学习摘要
- [67] IOS安全–浅谈关于IOS加固的几种方法
- [65] 如何拿下简短的域名
- [65] android 开发入门
- [63] find命令的一点注意事项
- [62] Go Reflect 性能
- [61] 流程管理与用户研究
- [60] Oracle MTS模式下 进程地址与会话信
- [59] 图书馆的世界纪录
- [57] 读书笔记-壹百度:百度十年千倍的29条法则