MySQL 定位用戶記錄的過程可以描述為:打開索引 -> 根據(jù)索引鍵值逐層查找 B+ 樹 branch 結點 -> 定位到葉子結點,將 cursor 定位到滿足條件的 rec 上;如果樹高為 N, 則需要讀取索引樹上的 N 個結點并進行比較,如果 buffer_pool 較小,則大量的操作都會在 pread 上,用戶響應時間變長;另外,MySQL中 Server 層與 Engine 之間的是以 row 為單位進行交互的,engine 將記錄返回給 server 層,server 層對 engine 的行數(shù)據(jù)進行相應的計算,然后緩存或發(fā)送至客戶端,為了減少交互過程所需要的時間,MySQL 做了兩個優(yōu)化:

  • 如果同一個查詢語句連續(xù)取出了 MYSQL_FETCH_CACHE_THRESHOLD(4) 條記錄,則會調用函數(shù) row_sel_enqueue_cache_row_for_mysql 將 MYSQL_FETCH_CACHE_SIZE(8) 記錄緩存至 prebuilt->fetch_cache 中,在隨后的 prebuilt->n_fetch_cached 次交互中,都會從prebuilt->fetch_cache 中直接取數(shù)據(jù)返回到 server 層,那么問題來了,即使是用戶只需要 4 條數(shù)據(jù),Engine 層也會將 MYSQL_FETCH_CACHE_SIZE 條數(shù)據(jù)放入 fetch_cache 中,造成了不必要的緩存使用。另外, 5.7 可以根據(jù)用戶的設置來調整緩存用戶記錄的條數(shù);

  • Engine 取出數(shù)據(jù)后,會將 cursor 的位置保存起來,當取下一條數(shù)據(jù)時,會嘗試恢復 cursor 的位置,成功則并繼續(xù)取下一條數(shù)據(jù),否則會重新定位 cursor 的位置,從而通過保存 cursor 位置的方法可以減少 server 層 & engine 層交互的時間;

???Server 層 & engine 層交互的過程如下,由于 server & engine 的 row format 不一樣,那么 engine row format -> server row format 在讀場景下的開銷也是比較大的。

while (rc == NESTED_LOOP_OK && join->return_tab >= join_tab)
{    int error;    if (in_first_read)
    {    
      in_first_read= false;
      error= (*join_tab->read_first_record)(join_tab);
    }    
    else 
      error= info->read_record(info);           /* load data from engine */

    rc= evaluate_join_record(join, join_tab);   /* computed by server */
}

AHI 功能作用

????由以上的分析可以看到 MySQL 一次定位 cursor 的過程即是從根結點到葉子結點的路徑,時間復雜度為:height(index) + [CPU cost time],上述的兩個優(yōu)化過程無法省略定位 cursor 的中間結點,因此需要引入一種可以從 search info 定位到葉子結點的方法,從而省略根結點到葉子結點的路徑上所消耗的時間,而這種方法即是 自適應索引(Adaptive hash index, AHI)。查詢語句使用 AHI 的時候有以下優(yōu)點: