轉載請附原文鏈接:http://www.cnblogs.com/wingsless/p/5582063.html
昨天寫到了InnoDB緩沖池的預讀:《InnoDB源碼分析--緩沖池(二)》,最後因為著急看歐洲杯,沒有把線性預讀寫完,今天接著寫。
線性預讀是由這個函數實現的:buf_read_ahead_linear,和隨機預讀一樣,首先是要確定區域邊界,這個邊界內被訪問過的page如果達到一個阈值(BUF_READ_AHEAD_LINEAR_THRESHOLD),就會觸發預讀操作。邊界的算法由BUF_READ_AHEAD_LINEAR_AREA決定:
low = (offset / BUF_READ_AHEAD_LINEAR_AREA)
* BUF_READ_AHEAD_LINEAR_AREA;
high = (offset / BUF_READ_AHEAD_LINEAR_AREA + 1)
* BUF_READ_AHEAD_LINEAR_AREA;
if ((offset != low) && (offset != high - 1)) {
/* This is not a border page of the area: return */
return(0);
}
注意,如果offset不在邊界上,就不會進行預讀了,這一點和隨機預讀是不一樣的。線性預讀其實是順序性讀取的,如果offset在low位置,逆序讀取page,如果offset在high位置,正序讀取page。讀取的每個頁,都要進行判斷,如果被訪問過的頁的數量到達上面提到的阈值,就滿足了線性預讀的條件,達不到阈值,就不進行預讀,代碼如下:
asc_or_desc = 1; //默認正序
if (offset == low) {
asc_or_desc = -1; //如果offset在low位置,變成逆序
}
fail_count = 0;
for (i = low; i < high; i++) {
block = buf_page_hash_get(space, i); //遍歷邊界范圍內的頁
if ((block == NULL) || !block->accessed) {
/* Not accessed */
fail_count++; //未讀取的頁計數
} else if (pred_block
&& (ut_ulint_cmp(block->LRU_position,
pred_block->LRU_position)
!= asc_or_desc)) {
/* Accesses not in the right order */
fail_count++;
pred_block = block;
}
}
if (fail_count > BUF_READ_AHEAD_LINEAR_AREA
- BUF_READ_AHEAD_LINEAR_THRESHOLD) { //不滿足預讀條件,退出
/* Too many failures: return */
mutex_exit(&(buf_pool->mutex));
return(0);
}
我之前在一本書上看到過一句話,大概意思是內存裡的頁可以不是物理上連續的,邏輯上卻是連續的。這裡的線性預讀要求這些頁在物理上也是必須連續的:
pred_offset = fil_page_get_prev(frame);
succ_offset = fil_page_get_next(frame);
mutex_exit(&(buf_pool->mutex));
if ((offset == low) && (succ_offset == offset + 1)) {
/* This is ok, we can continue */
new_offset = pred_offset; //滿足了條件,繼續
} else if ((offset == high - 1) && (pred_offset == offset - 1)) {
/* This is ok, we can continue */
new_offset = succ_offset; //這是正序情況下,滿足條件
} else {
/* Successor or predecessor not in the right order */
return(0);
}
這個地方是這樣的,首先利用fil_page_get_prev和fil_page_get_next函數讀取offset->frame之後或者之前的4個bytes,如果結果滿足順序條件,可以繼續進行線性預讀。
for (i = low; i < high; i++) {
/* It is only sensible to do read-ahead in the non-sync
aio mode: hence FALSE as the first parameter */
if (!ibuf_bitmap_page(i)) {
count += buf_read_page_low(
&err, FALSE,
ibuf_mode | OS_AIO_SIMULATED_WAKE_LATER,
space, tablespace_version, i);
if (err == DB_TABLESPACE_DELETED) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Warning: in"
" linear readahead trying to access\n"
"InnoDB: tablespace %lu page %lu,\n"
"InnoDB: but the tablespace does not"
" exist or is just being dropped.\n",
(ulong) space, (ulong) i);
}
}
}
線性預讀還是利用了buf_read_page_low函數,這一點和隨機預讀一樣,而且是異步方式。
至此便完成了線性預讀。
不管是隨機預讀還是線性預讀,都會有一些條件不進行預讀,比如系統壓力大的時候不預讀,這個的實現:
if (buf_pool->n_pend_reads
> buf_pool->curr_size / BUF_READ_AHEAD_PEND_LIMIT) {
mutex_exit(&(buf_pool->mutex));
return(0);
}
這裡規定了pend讀取數大於buf_pool->curr_size一半的時候,就不預讀了,相似的還有很多條件,都在代碼裡,這裡就不寫了。