我們在前面討論了一些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(
?????? ulintmax_size,
?????? ulintcurr_size,
?????? ulintn_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(
?????? ulintmax_size,
?????? ulintcurr_size,
?????? ulintn_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(
?????? ulintmax_size,
?????? ulintcurr_size,
?????? ulintn_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,晚安。”
?