爲應用選擇和建立最佳索引,加速數據讀取

在工做之中,因爲SQL問題致使的數據庫故障層出不窮,索引問題是SQL問題中出現頻率最高的,常見的索引問題包括:無索引,隱式轉換,索引建立不合理。mysql

當數據庫中出現訪問表的SQL沒建立索引致使全表掃描,若是表的數據量很大掃描大量的數據,執行效率過慢,佔用數據庫鏈接,鏈接數堆積很快達到數據庫的最大鏈接數設置,新的應用請求將會被拒絕致使故障發生。sql

隱式轉換是指SQL查詢條件中的傳入值與對應字段的數據定義不一致致使索引沒法使用。常見隱式轉換如字段的表結構定義爲字符類型,但SQL傳入值爲數字;或者是字段定義collation爲區分大小寫,在多表關聯的場景下,其表的關聯字段大小寫敏感定義各不相同。隱式轉換會致使索引沒法使用,進而出現上述慢SQL堆積數據庫鏈接數跑滿的狀況。數據庫

索引使用策略及優化

建立索引

  • 在常常查詢而不常常增刪改操做的字段加索引。
  • order by與group by後應直接使用字段,並且字段應該是索引字段。
  • 一個表上的索引不該該超過6個。
  • 索引字段的長度固定,且長度較短。
  • 索引字段重複不能過多。
  • 在過濾性高的字段上加索引。

使用索引注意事項

  • 使用like關鍵字時,前置%會致使索引失效。
  • 使用null值會被自動從索引中排除,索引通常不會創建在有空值的列上。
  • 使用or關鍵字時,or左右字段若是存在一個沒有索引,有索引字段也會失效。
  • 使用!=操做符時,將放棄使用索引。由於範圍不肯定,使用索引效率不高,會被引擎自動改成全表掃描。
  • 不要在索引字段進行運算。
  • 在使用複合索引時,最左前綴原則,查詢時必須使用索引的第一個字段,不然索引失效;而且應儘可能讓字段順序與索引順序一致。
  • 避免隱式轉換,定義的數據類型與傳入的數據類型保持一致。

無索引案例

無索引案例一

  1. 查看錶結構。oop

     
    1. mysql> show create table customers;
     
    1. CREATE TABLE `customers` (
    2. `cust_id` int(11) NOT NULL AUTO_INCREMENT,
    3. `cust_name` char(50) NOT NULL,
    4. `cust_address` char(50) DEFAULT NULL,
    5. `cust_city` char(50) DEFAULT NULL,
    6. `cust_state` char(5) DEFAULT NULL,
    7. `cust_zip` char(10) DEFAULT NULL,
    8. `cust_country` char(50) DEFAULT NULL,
    9. `cust_contact` char(50) DEFAULT NULL,
    10. `cust_email` char(255) DEFAULT NULL,
    11. PRIMARY KEY (`cust_id`),
    12. ) ENGINE=InnoDB AUTO_INCREMENT=10006 DEFAULT CHARSET=utf8
  2. 執行語句。性能

     
    1. mysql> select * from customers where cust_zip = '44444' limit 0,1 \G;
  3. 執行計劃。優化

     
    1. mysql> explain select * from customers where cust_zip = '44444' limit 0,1 \G;
     
    1. id: 1
    2. select_type: SIMPLE
    3. table: customers
    4. type: ALL
    5. possible_keys: NULL
    6. key: NULL
    7. key_len: NULL
    8. ref: NULL
    9. rows: 505560
    10. Extra: Using where

    執行計劃看到type爲ALL,是全表掃描,每次執行須要掃描505560行數據,這是很是消耗性能的,那麼下面將介紹優化方式。spa

  4. 添加索引。code

     
    1. mysql> alter table customers add index idx_cus(cust_zip);
  5. 執行計劃。索引

     
    1. mysql> explain select * from customers where cust_zip = '44444' limit 0,1 \G;
     
    1. id: 1
    2. select_type: SIMPLE
    3. table: customers
    4. type: ref
    5. possible_keys: idx_cus
    6. key: idx_cus
    7. key_len: 31
    8. ref: const
    9. rows: 4555
    10. Extra: Using index condition

    執行計劃看到type爲ref,基於索引的等值查詢,或者表間等值鏈接。ip

無索引案例二

  1. 表結構同上案例相同,執行語句。

     
    1. mysql> select cust_id,cust_name,cust_zip from customers where cust_zip = '42222'order by cust_zip,cust_name;
  2. 執行計劃。

     
    1. mysql> explain select cust_id,cust_name,cust_zip from customers where cust_zip = '42222'order by cust_zip,cust_name\G;
     
    1. id: 1
    2. select_type: SIMPLE
    3. table: customers
    4. type: ALL
    5. possible_keys: NULL
    6. key: NULL
    7. key_len: NULL
    8. ref: NULL
    9. rows: 505560
    10. Extra: Using filesort
  3. 添加索引。

     
    1. mysql> alter table customers add index idx_cu_zip_name(cust_zip,cust_name);
  1. 執行計劃。

     
    1. mysql> explain select cust_id,cust_name,cust_zip from customers where cust_zip = '42222'order by cust_zip,cust_name\G;
     
    1. id: 1
    2. select_type: SIMPLE
    3. table: customers
    4. type: ref
    5. possible_keys: idx_cu_zip_name
    6. key: idx_cu_zip_name
    7. key_len: 31
    8. ref: const
    9. rows: 4555
    10. Extra: Using where; Using index

    order by使用字段,並且字段應該是索引字段。

隱式轉換案例

隱式轉換案例一

 
  1. mysql> explain select * from customers where cust_zip = 44444 limit 0,1 \G;
 
  1. id: 1
  2. select_type: SIMPLE
  3. table: customers
  4. type: ALL
  5. possible_keys: idx_cus
  6. key: NULL
  7. key_len: NULL
  8. ref: NULL
  9. rows: 505560
  10. Extra: Using where
 
  1. mysql> show warnings;
  2. Warning: Cannot use range access on index 'idx_cus' due to type or collation conversion on field 'cust_zip'

上述案例中因爲表結構定義cust_zip字段是字符串數據類型,而應用傳入的是數字,致使了隱式轉換,沒法使用索引。

解決方案:

  1. 將cust_zip字段修改成數字數據類型。
  2. 將應用中傳入的字符類型改成數據類型。

隱式轉換案例二

  1. 查看錶結構。

     
    1. mysql> show create table customers1;
     
    1. CREATE TABLE `customers1` (
    2. `cust_id` varchar(10) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
    3. `cust_name` char(50) NOT NULL,
    4. KEY `idx_cu_id` (`cust_id`)
    5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    6. mysql> show create table customers2;
    7. CREATE TABLE `customers2` (
    8. `cust_id` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
    9. `cust_name` char(50) NOT NULL,
    10. KEY `idx_cu_id` (`cust_id`)
    11. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  2. 執行語句。

     
    1. mysql> select customers1.* from customers2 left join customers1 on customers1.cust_id=customers2.cust_id where customers2.cust_id='x';
  3. 執行計劃。

     
    1. mysql> explain select customers1.* from customers2 left join customers1 on customers1.cust_id=customers2.cust_id where customers2.cust_id='x'\G;
     
    1. *************************** 1. row ***************************
    2. id: 1
    3. select_type: SIMPLE
    4. table: customers2
    5. type: ref
    6. possible_keys: idx_cu_id
    7. key: idx_cu_id
    8. key_len: 33
    9. ref: const
    10. rows: 1
    11. Extra: Using where; Using index
     
    1. *************************** 2. row ***************************
    2. id: 1
    3. select_type: SIMPLE
    4. table: customers1
    5. type: ALL
    6. possible_keys: NULL
    7. key: NULL
    8. key_len: NULL
    9. ref: NULL
    10. rows: 1
    11. Extra: Using where; Using join buffer (Block Nested Loop)
  4. 修改COLLATE。

     
    1. mysql> alter table customers1 modify column cust_id varchar(10) COLLATE utf8_bin ;
  5. 執行計劃。

     
    1. mysql> explain select cust_id,cust_name,cust_zip from customers where cust_zip = '42222'order by cust_zip,cust_name\G;
     
    1. id: 1
    2. select_type: SIMPLE
    3. table: customers2
    4. type: ref
    5. possible_keys: idx_cu_id
    6. key: idx_cu_id
    7. key_len: 33
    8. ref: const
    9. rows: 1
    10. Extra: Using where; Using index
     
    1. id: 1
    2. select_type: SIMPLE
    3. table: customers1
    4. type: ref
    5. possible_keys: idx_cu_id
    6. key: idx_cu_id
    7. key_len: 33
    8. ref: const
    9. rows: 1
    10. Extra: Using where

    字段的COLLATE一致後執行計劃使用到了索引,因此必定要注意表字段的collate屬性的定義保持一致。

總結

在使用索引時,咱們能夠經過explain查看SQL的執行計劃,判斷是否使用了索引以及發生了隱式轉換,建立合適的索引。索引太複雜,建立需謹慎。

相關文章
相關標籤/搜索