MySQL:如何對待分佈偏移的資料

樸所羅門發表於2020-07-31

btr_estimate_n_rows_in_range_low 函式用於非唯一訪問的行數評估也就是在執行計劃生成階段會去訪問實際的資料,儘可能的得到正確的執行計劃,這個過程叫做dive。其和引數eq_range_index_dive_limit有關,如果引數設定1那麼將禁用dive功能,使用統計資料進行執行計劃的生成。但某些時候MySQL 5.7根據統計資料並不能得到正確的執行計劃,因此才有了dive,但是dive會產生額外的代價,這個過程是需要實際訪問資料塊的。

常用的比如非唯一索引的資料訪問,還有> <等這些範圍掃描都是需要用到這個函式。下面來簡單的看一下。其輸入的引數包含了index 索引的相關的資訊,範圍的起始tuple和結束tuple首先透過btr_cur_search_to_nth_level函式進行定位獲取起點記錄
然後透過btr_cur_search_to_nth_level函式定位到終點記錄透過cursor獲取到path,假設起點的path為path1 終點的path為path2path為一個陣列,其中儲存了查詢的路徑,然後會迴圈的從頂層節點到葉子節點進行判斷,其中包含了

/** A slot in the path array. We store here info on a search path down thetree. Each slot contains data on a single level of the tree. */struct btr_path_t {    /* Assume a page like:    records:             (inf, a, b, c, d, sup)    index of the record:    0, 1, 2, 3, 4, 5    */    /** Index of the record where the page cursor stopped on this level    (index in alphabetical order). Value ULINT_UNDEFINED denotes array    end. In the above example, if the search stopped on record 'c', then    nth_rec will be 3. */    ulint    nth_rec;    /** Number of the records on the page, not counting inf and sup.    In the above example n_recs will be 4. */    ulint    n_recs;    /** Number of the page containing the record. */    ulint    page_no;    /** Level of the page. If later we fetch the page under page_no    and it is no different level then we know that the tree has been    reorganized. */    ulint    page_level;};

最終獲取最後的估計得行數,如果它們起點和終點的行數過多,那麼只會精確統計10個塊的資料(N_PAGES_READ_LIMIT),這個在btr_estimate_n_rows_in_range_low呼叫的btr_estimate_n_rows_in_range_on_level函式中,剩下就是取得平均值了大概為:

        n_rows = n_rows_on_prev_level            * n_rows / n_pages_read;

n_rows_on_prev_level就是實際的指向的塊數,因為每一個上層節點記錄指向葉子節點的一個block,n_pages_read就是大概10個塊,n_rows就是10個塊包含的行數。最終得到的n_rows就是一個大概的統計值了。這還是很有代表性的。
最終我們看到對於2層結構的索引,可能需要訪問10(取樣塊)+2(leaf)+1(root)個塊,這是有一定代價的。預設MySQL是開啟了這個功能的,因此我們會發現如下的表可以得到正確的執行計劃,比如性別列索引,其中30行,29行為男性,1行為女性,下面是執行計劃示例,可以看到禁用dive後不能得到正確的執行計劃:

mysql> set eq_range_index_dive_limit=100;Query OK, 0 rows affected (0.00 sec)mysql> desc select * from testdvi3 where sex='M';+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+|  1 | SIMPLE      | testdvi3 | NULL       | ALL  | sex           | NULL | NULL    | NULL |   30 |    96.67 | Using where |+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+1 row in set, 1 warning (2.74 sec)mysql> desc select * from testdvi3 where sex='W';+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+|  1 | SIMPLE      | testdvi3 | NULL       | ref  | sex           | sex  | 9       | const |    1 |   100.00 | NULL  |+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+1 row in set, 1 warning (2.00 sec)mysql> set eq_range_index_dive_limit=1;Query OK, 0 rows affected (0.00 sec)mysql> desc select * from testdvi3 where sex='W';+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+|  1 | SIMPLE      | testdvi3 | NULL       | ref  | sex           | sex  | 9       | const |   15 |   100.00 | NULL  |+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+1 row in set, 1 warning (0.00 sec)mysql> desc select * from testdvi3 where sex='M';+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+|  1 | SIMPLE      | testdvi3 | NULL       | ref  | sex           | sex  | 9       | const |   15 |   100.00 | NULL  |+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------+

原文連結:http://blog.itpub.net/7728585/viewspace-2707569/

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69978212/viewspace-2708260/,如需轉載,請註明出處,否則將追究法律責任。

相關文章