InnoDB引擎B+樹索引使用和新特性

我們已經講過了MySQL InnoDB索引原理和算法,這裏來說下如何管理和使用B+樹索引以及一些新的特性。

B+ 樹索引的管理

我們在InnoDB引擎中常用的索引基本都是B+ 樹索引。

創建和刪除索引

它的創建和刪除有兩種方法:

# 方式一:alter table, 此時index和key均可以,如果要求所有值均不重複,加上unique
alter table tbl_name add [unique] index|key index_name (index_col_name,...);
alter table tbl_name drop index|key index_name;

# 方式二:create/drop index, 此時只能用index
create index index_name on tbl_name (index_col_name,...);
drop index index_name on tbl_name;

修改索引

MySQL沒有提供修改索引的命令,我們一般先刪掉索引,再重建同名索引達到修改的目標。

查看索引

我們在查看數據表描述時可以看到都有哪些索引,有三種方法:

# 方法一:查看創建表的語句
mysql> show create table serviceHost;
| Table       | Create Table 
| t     | CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a` varchar(20) DEFAULT NULL,
  `b` varchar(20) DEFAULT NULL,
  `c` varchar(20) DEFAULT NULL,
  `d` varchar(20) DEFAULT NULL,
  `e` varchar(20) DEFAULT NULL,
  `f` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `a` (`a`),
  KEY `idx_b` (`b`),
  KEY `idex_cde` (`c`,`d`,`e`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
1 row in set (0.00 sec)

可以看到該表中有4個索引,主鍵集合索引(id),唯一輔助索引(a),單列輔助索引(b),組合輔助索引(c,d,e)

# 方法二:查看錶的描述
mysql> describe t;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| a     | varchar(20) | YES  | UNI | NULL    |                |
| b     | varchar(20) | YES  | MUL | NULL    |                |
| c     | varchar(20) | YES  | MUL | NULL    |                |
| d     | varchar(20) | YES  |     | NULL    |                |
| e     | varchar(20) | YES  |     | NULL    |                |
| f     | varchar(20) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
7 rows in set (0.01 sec)

在這個表裏可以更明顯的看到各個字段的屬性,注意Key這一列就代表索引,分別有4種值:
- PRI: 代表該列爲主鍵
- UNI: 代表該列是唯一索引的第一列(唯一索引允許多個空值,非空值必須唯一)
- MUL: 代表非唯一索引的第一列
- 空: 代表該列沒有索引

其中的d,e也爲組合索引的一部分,爲什麼沒有標識呢?是因爲由於索引的特性,除了主鍵組合外,其餘組合索引我們只標註第一個。

# 方法三:查看詳細索引信息
mysql> show index from t;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| t     |          0 | PRIMARY  |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| t     |          0 | a        |            1 | a           | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| t     |          1 | idx_b    |            1 | b           | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| t     |          1 | idex_cde |            1 | c           | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| t     |          1 | idex_cde |            2 | d           | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| t     |          1 | idex_cde |            3 | e           | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.00 sec)

這裏的每一列的含義:

  • Table: 索引所在表名
  • Non_unique: 該列值是否唯一,但是NULL可以存在多個
  • Key_name: 索引的名字,drop時使用該名字
  • Seq_in_index: 索引中該列的位置
  • Column_name: 索引列的名稱
  • Collation: 在索引中列存儲的方式,值爲A或者NULLB+樹索引總是A,即排序的(asc)。如果是Heap存儲引擎,並且建立了Hash索引,則爲NULL
  • Cardinality: 該索引中值去重後的估計數量。
  • Sub_part: 是否是列的部分被索引,如果部分索引,則爲數字,否則爲NULL
  • Packed: 關鍵字如何被壓縮。
  • Null: 索引列中是否包含Null值。
  • Index_type: 索引類型。InnoDB引擎中只支持B+ 樹索引,所以均爲BTREE
  • Comment: 索引註釋。

聯合索引

聯合索引就是對多個列進行索引,它按照索引列次序進行依次有序。
圖片描述
我們直接查詢索引中的字段顯然能夠直接使用這個索引:

mysql> explain select * from t where c='10' and d='10';
+----+-------------+-------+------+---------------+----------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref         | rows | Extra                 |
+----+-------------+-------+------+---------------+----------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | t     | ref  | idex_cde      | idex_cde | 126     | const,const |    1 | Using index condition |
+----+-------------+-------+------+---------------+----------+---------+-------------+------+-----------------------+
1 row in set (0.00 sec)

我們在需要通過該索引排序時也會用到:

mysql> explain select c from t order by c desc limit 10;  
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | t     | index | NULL          | idex_cde | 189     | NULL |   10 | Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
1 row in set (0.00 sec)

覆蓋索引

InnoDB支持覆蓋索引(covering index),即從輔助索引中可以查詢的記錄,就不需要查詢聚集索引的記錄。由於輔助索引不包含整行數據,因此大小原小於聚集索引,而每頁可以讀取更多的數據,減少了大量頁的置換,也就是減少大量的I/O操作,即提高了效率。

但注意到,如果我們要選擇的是整行數據,因爲還要通過書籤查詢到聚集索引,所以優化器很可能不會使用我們的索引:

# 不僅僅使用了輔助索引,這個index condition我們後面會說,還使用了聚集索引
mysql> explain select * from t where c='10' and d='10';
+----+-------------+-------+------+---------------+----------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref         | rows | Extra                 |
+----+-------------+-------+------+---------------+----------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | t     | ref  | idex_cde      | idex_cde | 68      | const,const |    1 | Using index condition |
+----+-------------+-------+------+---------------+----------+---------+-------------+------+-----------------------+
1 row in set (0.00 sec)

# use index代表使用了輔助索引
mysql> explain select c,d from t where c='10' and d='10'; 
+----+-------------+-------+------+---------------+----------+---------+-------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref         | rows | Extra                    |
+----+-------------+-------+------+---------------+----------+---------+-------------+------+--------------------------+
|  1 | SIMPLE      | t     | ref  | idex_cde      | idex_cde | 68      | const,const |    1 | Using where; Using index |
+----+-------------+-------+------+---------------+----------+---------+-------------+------+--------------------------+
1 row in set (0.00 sec)

索引提示

如覆蓋索引中的情況(Using where),可以理解爲優化器沒有選擇使用索引而是使用了表掃描。如果使用索引掃描,後面還需要通過一次書籤訪問查找整行數據,通過書籤查找的數據是無序的,成爲離散讀操作。而順序讀速度遠遠快於離散讀,因此選擇表掃描。但這也不是絕對的,當訪問的數據量很小時,還是會選擇輔助索引,而如果超過了20%(估值),則會通過表掃描。

# 我們改下a值爲數字類型,可以選中範圍
mysql> alter table t modify a int(11);
Query OK, 230 rows affected (0.07 sec)
Records: 230  Duplicates: 0  Warnings: 0

mysql> explain select * from t where a>10;                          
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t     | ALL  | a             | NULL | NULL    | NULL |  230 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

mysql> explain select * from t where a<10;  
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
|  1 | SIMPLE      | t     | range | a             | a    | 5       | NULL |    8 | Using index condition |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
1 row in set (0.00 sec)

數據庫優化器大部分時候都工作的很有效和正確,但是專業的DBA可以強制使用某些索引,但有兩種情況可以使用提示:

  • 數據庫錯誤使用了索引,導致語句執行很慢,概率極低。
  • 索引非常多,優化器選擇執行計劃的時間會大於SQL執行時間,比如分析range查詢就很耗時。此時提示可以省去成本分析的過程,直接使用指定索引來執行。

索引提示有兩種程度:

# 索引提示(index hint),只是告訴優化器可以選擇,但優化器還會自己判斷,不一定使用
mysql> explain select * from t use index(a) order by a; 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | t     | ALL  | NULL          | NULL | NULL    | NULL |  230 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)

# 強制使用索引(index force),保證最終和用戶選擇的是一致的
mysql> explain select * from t force index(a) order by a;   
+----+-------------+-------+-------+---------------+------+---------+------+------+-------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | t     | index | NULL          | a    | 5       | NULL |  230 | NULL  |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)

索引參數

上面我們對索引的所有參數都進行了說明,但有幾個參數需要我們格外注意。

列頭部分索引:Sub_part

我們一般對整列數據進行索引,但如果內容很長,我們也可以只對列的開頭部分進行索引:

mysql> drop index idx_b on t;
mysql> create index idx_b on t (b(10));
mysql> show index from t where Key_name='idx_b';
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| t     |          1 | idx_b    |            1 | b           | A         |           0 |       10 | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

可以看到我們只對b這列的前10個字符進行索引,Sub_part字段也變爲了10

唯一值估計數目:Cardinality

Cardinality(基數)非常關鍵,常常是我們判斷是否使用這個索引的依據。它表示索引中不重複記錄數量的預估值。也可以成爲散列程度。

我們在剛纔的表中隨機插入一些數據到列b,從100-1000之間,其餘列均爲NULLid列自增:

mysql> select count(b) from t;
+----------+
| count(b) |
+----------+
|      110 |
+----------+
1 row in set (0.00 sec)
mysql> select distinct(b) from t;
+------+
| b    |
+------+
| 100  |
| 200  |
| 300  |
| 400  |
| 500  |
| 600  |
| 700  |
| 800  |
| 900  |
| 1000 |
+------+
10 rows in set (0.00 sec)
mysql> show index from t;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| t     |          0 | PRIMARY  |            1 | id          | A         |         110 |     NULL | NULL   |      | BTREE      |         |               |
| t     |          0 | a        |            1 | a           | A         |           2 |     NULL | NULL   | YES  | BTREE      |         |               |
| t     |          1 | idex_cde |            1 | c           | A         |           2 |     NULL | NULL   | YES  | BTREE      |         |               |
| t     |          1 | idex_cde |            2 | d           | A         |           2 |     NULL | NULL   | YES  | BTREE      |         |               |
| t     |          1 | idex_cde |            3 | e           | A         |           2 |     NULL | NULL   | YES  | BTREE      |         |               |
| t     |          1 | idx_b    |            1 | b           | A         |          20 |       10 | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.00 sec)

可以看到id這列的散列值爲110,b這列的散列值爲20,其餘都爲2。這裏理論上應該爲101,至於爲什麼翻倍,我沒有找到答案。如果找到後續會更新。

該值的計算方法爲:

  1. InnoDB引擎取得B+ 樹索引中葉子節點的數量,即爲A
  2. 隨機取得8個葉子節點,統計每頁不同記錄的個數,爲P1,p2,...P8.
  3. Cardinality = (P1+P2+...+P8) / 8 * A

由於是抽樣的方法,所以可能每次取的值不同。

另外我們在上一節中說過,由於索引的更新操作非常頻繁,因此我們通過一定的策略來更新該值:

  • 表中1/16的數據已發生過變化。
  • stat_modified_counter>2 000 000 000(M, 二十億次CUD操作)

當我們想要手動更新該值時,可以執行下面的任何一種:

analyze table;(推薦)
show table status;
show index;
訪問information_schema下的tables/statistics;

數據庫索引統計時,有可能會發生問題,導致值爲NULL,此時手動更新下即可。由於統計這個會影響統計時刻的性能,如果我們能在低峯期對關鍵表批量手動更新,可以讓索引更好的爲你服務:ananlyze tables;

相關參數

# 設置統計時的採樣頁數量,默認爲8
innodb_stats_sample_pages
# 判斷如何處理NULL值,默認爲nulls_equal,視爲相等記錄。
innodb_stats_method = nulls_equal|nulls_unequal|nulls_ignored

優化索引的使用

我們前面說過,由於輔助索引不存儲行數據,輔助索引除非是隻查詢索引列,否則還是要索引到聚集索引(主鍵索引)上來查找。而同時主鍵索引的連續讀(雙向鏈表)比輔助索引的跳讀效率更高,因此如果存儲引擎認爲該索引並不能有效提升效率的話,會直接使用全表掃描。

B+樹的特性決定了只有在訪問表中很少一部分數據時纔有意義。比如性別有兩種,我們通過該索引查詢時,一般均爲50%的數據,此時成爲低選擇性的字段,這時的索引是完全沒有意義的。而如果是姓名,則重複的很少,此時屬於高選擇性,則使用該索引是非常合適的。

我們可以通過Cardinality字段來決定是否使用索引,取Cardinality/rows_in_table,如果該值接近1,則很適合建立索引,而如果該值接近於0,則應該考慮刪掉該不必要的索引。

根據上面的實例,測試下:

mysql> describe select * from t where id=10;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | t     | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)
mysql> describe select * from t where b=10;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t     | ALL  | idx_b         | NULL | NULL    | NULL |  230 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

注意possible_keys字段,代表可能採用的索引,即該列存在的索引。而key字段代表實際採用的索引。最後一個Using where值代表採用了全表掃描。

索引優化新功能

5.5、5.6版本中加入了一些新的特性,來從引擎內部優化SQL執行速度。
先說下數據庫的三種操作類型:

  • DDL(Data Definition Languages): 數據定義語句,如數據庫、表、列、索引的變化,命令爲create/drop/alter等。
  • DML(Data Manipulation Languages): 數據操縱語句,如數據庫的增刪改查,命令爲insert/update/delete/select等。
  • DCL(Data Control Languages): 數據控制語句,如設置權限,命令爲grant/revoke等。

Fast Index Creation (快速索引創建)

5.5之前版本中索引的添加或刪除之類的DDL操作過程爲:

  1. 創建一張新表,表結構爲通過alter table新定義的結構
  2. 然後把原表中數據導入到臨時表
  3. 刪除原表
  4. 最後把臨時表重命名爲原表名

可以看到,如果表的數據非常大,這個過程也會非常耗時,而此時的數據庫服務會因爲鎖而不可用。而5.5之後支持了一種FIC的索引創建方式,對於輔助索引的創建,會對該表加上一個S(shared)共享鎖,不需要重建表,因此速度提高很多,可用性也得到了提高。

但注意此時只能讀操作,寫操作依然不可用,同時該方法只針對輔助索引,對主鍵依然需要重建。刪除索引操作,只需要InnoDB刪除內部視圖對該表的索引定義,並將索引空間標記爲可用即可。

Online Schema change(在線架構改變)

OSC最早是FacebookPHP腳本實現的,可以在有讀寫事務對錶進行操作的時候執行其他事務,提高了數據庫在DDL操作時的併發性。其步驟:

  1. init:初始化階段,對錶做驗證工作,如果表沒有主鍵或者存在觸發器或外鍵,則無法使用。
  2. createCopyTable:創建和原始表結構一樣的新表
  3. alterCopyTable: 對新表進行操作,如添加索引或列等
  4. createDeltasTable: 創建deltas臨時表,之後所有的DML操作會被記錄到該表中
  5. createTriggers: 對原表創建觸發器,所有DML操作產生的記錄會被寫入到deltas
  6. startSnapshotXact: 開始OSC操作的事務
  7. selectTableIntoOutfile: 將原表數據寫入到新表。爲了減少原表的鎖定時間,通過分片將數據輸出到多個外部文件,然後將文件數據導入到新表中。
  8. dropNCIndexs: 在導入到新表前,刪除新表中所有的輔助索引。
  9. loadCopyable: 將導出的分片文件導入到新表
  10. replayChanges: OSC過程中存在deltas表中的DML操作記錄應用到新表。
  11. recreateNCIndexes: 重新創建輔助索引
  12. replayChanges: 重放日誌,將在創建輔助索引過程中的新日誌回放。
  13. swapTables: 原表和新表交換名字,需要鎖定兩張表,由於改名很快,因此阻塞很短。

這個功能有些侷限性,同時由於可以在執行過程中設置set sql_bin_log=0,導致主從不一致的情況發生。

Online DDL(在線數據定義)

前面兩種功能,FIC還是會阻塞一些DML操作,OSC有很大侷限性,因此5.6之後支持了Online DDL功能。它允許創建輔助索引的時候,允許DML的操作,極大提高了可用性。它支持的幾種變更操作:

  • 輔助索引的增刪
  • 自增長值的變更
  • 添加和刪除外鍵約束
  • 重命名列

它支持3種算法和4種鎖:

 alter table t add key (f), ALGORITHM=DEFAULT|INPLACE|COPY, LOCK=DEFAULT|NONE|SHARED|EXCLUSIVE;

算法:

  • COPY:老版本方法,即創建臨時表
  • INPLACE:不需要創建臨時表
  • DEFAULT:會根據old_alter_table參數來判斷是否採用老方法,默認爲off
mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 5.6.39    |
+-----------+
1 row in set (0.00 sec)

mysql> show variables like 'old_alter_table';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| old_alter_table | OFF   |
+-----------------+-------+
1 row in set (0.00 sec)

4種鎖:

  • NONE:不加鎖,獲得最大併發度。
  • SHARE:FIC一樣,可併發讀,不可併發寫。
  • EXCLUSIVE:加上一個X鎖(排它鎖),阻塞所有線程。
  • DEFAULT:會依次判斷是否可用,爭取最大併發度,NONE > SHARE > EXCLUSIVE

Online DDL的原理是在DDL操作時,將DML操作日誌寫在緩存中,待操作完成後,在重做到表上達到數據的一次性。緩存的大小由innodb_online_alter_log_max_size參數來控制。

Multi-Range Read優化

MRR優化是爲了減少磁盤的隨機訪問,並將其轉換爲順序的數據訪問,能夠帶來極大的性能提升。主要體現在減少緩存池中頁被替換的次數和批量處理對鍵值的查詢操作。對於InnoDBMyISAM存儲引擎的範圍查詢和JOIN查詢,其原理爲:

  1. 將查詢得到的輔助索引鍵值存在緩存中。
  2. 將緩存的鍵值根據RowID來進行排序。
  3. 根據RowID的排序順序來訪問實際的數據文件。

該優化主要體現在使用索引的範圍查詢,但是是查詢整行數據:

mysql>  explain select * from t where a<50 and a > 30; 
+----+-------------+-------+-------+---------------+------+---------+------+------+----------------------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                            |
+----+-------------+-------+-------+---------------+------+---------+------+------+----------------------------------+
|  1 | SIMPLE      | t     | range | a             | a    | 5       | NULL |   18 | Using index condition; Using MRR |
+----+-------------+-------+-------+---------------+------+---------+------+------+----------------------------------+
1 row in set (0.00 sec)

該特性效率可以提高數十倍,但有時候這個特性可能並未啓動,通過optimizer_switch中的標記來控制。當mrr=on時,表示啓用MRR優化;當mrr_cost_based=off時,表示總啓用,=on時,根據優化器計算的代價來選擇是否啓用。

mysql> select @@optimizer_switch;
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| @@optimizer_switch                                                                                                                                                                                                                                                                                                                               |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,subquery_materialization_cost_based=on,use_index_extensions=on |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> set @@optimizer_switch='mrr_cost_based=off';
Query OK, 0 rows affected (0.00 sec)

Index Condition Pushdown(索引合併過濾)

ICP優化是提前進行索引條件的過濾,將部分過濾放在存儲引擎層,減少對行數據的讀取,從而提高性能。

# 在執行前需要將強制執行MRR優化關閉
mysql> set @@optimizer_switch='mrr_cost_based=on';       
Query OK, 0 rows affected (0.00 sec)

mysql>  explain select * from t where c='50' and d > 30; 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
|  1 | SIMPLE      | t     | range | idex_cde      | idex_cde | 68      | NULL |    1 | Using index condition |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
1 row in set (0.00 sec)

可以看到,我們使用了ICP優化,在5.6版本之前,會先過濾c='50'的條件,在讀取所有符合的行記錄,然後過濾d>30。而在新特性中,會檢測到c和d有聯合索引,因此提前過濾,並一次性讀取行記錄。該特性可以提高23%的性能,可以與MRR共同使用。

參考資料

  1. mysql 5.6 原生Online DDL解析:http://seanlook.com/2016/05/2...
  2. mysql X鎖和S鎖:https://www.jianshu.com/p/342...
  3. 詳解MySQL---DDL語句、DML語句與DCL語句:https://www.cnblogs.com/zhang...
  4. MySQL技術內幕 InnoDB存儲引擎 第2版》第5章:索引與算法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章