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

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

B+ 樹索引的管理

咱們在InnoDB引擎中經常使用的索引基本都是B+ 樹索引。mysql

建立和刪除索引

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

# 方式一: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沒有提供修改索引的命令,咱們通常先刪掉索引,再重建同名索引達到修改的目標。sql

查看索引

咱們在查看數據表描述時能夠看到都有哪些索引,有三種方法:數據庫

# 方法一:查看建立表的語句
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)segmentfault

# 方法二:查看錶的描述
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: 索引註釋。

聯合索引

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

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.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章:索引與算法
相關文章
相關標籤/搜索