Mysql 范圍查詢優化


  Range查詢:用單獨的Index的一個或多個index值來檢索表的子集行數據,當然包含多個index。

  1:一個index (單一部分)的range access 方法:(eg : 指的這種key (column1 ))

  單獨的index,index值間隔可以方便的由對應的where子句的條件表示,所有我們稱值為range條件而不是間隔;

  單獨index的range條件的定義:

     1.1:對於btree和hash索引,index和一個常量值通過 =, <=>,in(),is null,或者 IS not null操作符做比較;

     1.2:另外,對於Btree索引,index和一個常量值通過 <,>,<=,>=,between,!=,或者<>操作符做比較;

     1.3: 對於所有類型的index,多范圍條件通過 or and關鍵字組合形式;

 

  '常量值'在之前的描述中意味着:

    2.1: 查詢字符串的常量形式;

    2.2: const 或者system表的一列(也只有一列)的自連接(join);

    2.3: 不相關子查詢的結果;

    2.4: 有上面類型子表達式完全組成的任意表達式;

 

   where子句范圍查找的例子:

SELECT * FROM t1
  WHERE key_col > 1
  AND key_col < 10;

SELECT * FROM t1
  WHERE key_col = 1
  OR key_col IN (15,18,20);

SELECT * FROM t1
  WHERE key_col LIKE 'ab%'
  OR key_col BETWEEN 'bar' AND 'foo';

  一些非常量值可能在傳播階段轉換為常量;

  Mysql嘗試對於在where子句中的任何可能的index提取range condition,在提取過程中,不能構造的范圍條件被舍去,產生重疊的條件被組合,產生空范圍的條件被舍去。

  如下:

SELECT * FROM t1 WHERE
  (key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
  (key1 < 'bar' AND nonkey = 4) OR
  (key1 < 'uux' AND key1 > 'z');

key1為index, nonkey非index;

key1的提取過程如下:

  1:從原始的where子句開始:

(key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
(key1 < 'bar' AND nonkey = 4) OR
(key1 < 'uux' AND key1 > 'z')

 2:舍去nokey = 4和key1 like ‘%d’,因為他們不能用作范圍scan,正確的舍去方法是用true代替她們,

(key1 < 'abc' AND (key1 LIKE 'abcde%' OR TRUE)) OR
(key1 < 'bar' AND TRUE) OR
(key1 < 'uux' AND key1 > 'z')

3:  以下條件總是true or false;

   1:(key1 LIKE 'abcde%' OR TRUE) is always true

   2:(key1 < 'uux' AND key1 > 'z') is always false

     

 (key1 < 'abc' AND TRUE) OR (key1 < 'bar' AND TRUE) OR (FALSE)

 4: 去掉不必要的true 和false條件:

(key1 < 'abc') OR (key1 < 'bar')

 5:組合那些重疊的區間成一個:

(key1 < 'bar')

 

   范圍條件的提取算法可以處理內嵌的and /or任意深度的構造,他的數去不依賴與他們出現在where子句中的順序;

   

2:多part index的范圍查詢:

   index的多個部分的范圍條件是上面的擴展,被一個或幾個key元組來限制條件:

  eg: 一個聯合索引定義:key1(key_part1,key_part2,key_part3),key元組以key order顯示如下:

key_part1  key_part2  key_part3
  NULL       1          'abc'
  NULL       1          'xyz'
  NULL       2          'foo'
   1         1          'abc'
   1         1          'xyz'
   1         2          'abc'
   2         1          'aaa'

 

     條件 key_part1 = 1 定義的區間:

(1,-inf,-inf) <= (key_part1,key_part2,key_part3) < (1,+inf,+inf)

    這區間包括第四,第五,第六元組。

    相對,條件 key_part3 = 'abc'不能定義一個單獨的區間並且不能被區間scan方法使用(最左前綴index);

   

 3:多值的區間優化:

   以下col_name 是index 列:

col_name IN(val1, ..., valN)
col_name = val1 OR ... OR col_name = valN

    如果col_name 等於這些值中的任意一個返回true,

   1:如果col_name 為unique index,則范圍的行評估為1,因為最多一個值只能對應一行;

    2:否則,優化器估計范圍行數得使用index潛入和index的統計信息;

    index潛入,優化器植入一個潛入在每個range的結尾並且用該范圍的行數來估計,eg:col_name IN (10, 20, 30) ,優化器植入兩個潛入在每個區間中來估計行數;index潛入提供精確的行估計,但是當表達式中的比較值越多,每個潛入對需要話費更多時間。用index統計信息缺乏精確但是較快。

30727 rows in set (5.37 sec)

mysql> explain select * from employees where first_name like 'm%' ;
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | employees | ALL  | idx_fn_ln     | NULL | NULL    | NULL | 299290 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)

mysql> explain select * from employees where first_name like 'mart%' ;
+----+-------------+-----------+-------+---------------+-----------+---------+------+------+-----------------------+
| id | select_type | table     | type  | possible_keys | key       | key_len | ref  | rows | Extra                 |
+----+-------------+-----------+-------+---------------+-----------+---------+------+------+-----------------------+
|  1 | SIMPLE      | employees | range | idx_fn_ln     | idx_fn_ln | 16      | NULL | 1635 | Using index condition |
+----+-------------+-----------+-------+---------------+-----------+---------+------+------+-----------------------+
1 row in set (0.00 sec)

第一個條件查找從執行計划上看走的掃表(all),where條件得到的結果為所以數據行的1/10;第二條增加like條件的匹配精度,走的ICP range查找,得到的數據行大約是1/30,mysql

會根據查找的數據范圍(多少)決定走index range查找還是直接掃表(all)

mysql> desc select * from employees where first_name like 'ma%' and last_name like 'he%';
+----+-------------+-----------+-------+---------------+-----------+---------+------+-------+-----------------------+
| id | select_type | table     | type  | possible_keys | key       | key_len | ref  | rows  | Extra                 |
+----+-------------+-----------+-------+---------------+-----------+---------+------+-------+-----------------------+
|  1 | SIMPLE      | employees | range | idx_fn_ln     | idx_fn_ln | 34      | NULL | 38174 | Using index condition |
+----+-------------+-----------+-------+---------------+-----------+---------+------+-------+-----------------------+
1 row in set (0.04 sec)
mysql> desc select * from employees where first_name like 'ma%' or last_name like 'he%';
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | employees | ALL  | idx_fn_ln     | NULL | NULL    | NULL | 299290 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.02 sec)

 

mysql> desc select * from employees where first_name in('mart','mori','moon');
+----+-------------+-----------+-------+---------------+-----------+---------+------+------+-----------------------+
| id | select_type | table     | type  | possible_keys | key       | key_len | ref  | rows | Extra                 |
+----+-------------+-----------+-------+---------------+-----------+---------+------+------+-----------------------+
|  1 | SIMPLE      | employees | range | idx_fn_ln     | idx_fn_ln | 16      | NULL |  710 | Using index condition |
+----+-------------+-----------+-------+---------------+-----------+---------+------+------+-----------------------+
1 row in set (0.02 sec)

 

mysql> explain select * from employees where emp_no = 11;
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                                               |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
1 row in set (0.01 sec)

Mysql發現where條件不可能成立 ,返回null


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM