mysql多列索引的建立和優化
文章目錄
- 0.首先來了解索引的物理結構:
- 1.where 子句中的多列索引
- 1.1 完全使用索引的情況
- 1)where中條件只有col1等於常量
- 2)where中條件只有col1為範圍(>,<,>=,<=)
- 3)where中條件有col1,col2,且col1等於常量,col2等於常量
- 4)where中條件有col1,col2,且col1等於常量,col2等於範圍
- 5)where中條件有col1,col2,col3,且col1,col2,col3均等於常量
- 6)where中條件有col1,col2,col3,且col1,col2均等於常量,col3為範圍
- 1.2 部分使用索引的情況
- 1)where中條件col1等於常量,無col2,col3的常量或範圍,只能用到col1的索引
- 2)where中條件col1等於常量,col2的範圍,col3的常量或範圍,只能用到col1、col2的索引
- 3)where中條件col1的範圍,col2、col3的常量或範圍,只能用到col1的索引
- 1.3 索引的失效
- 2、order by 子句中的多列索引
對於單列索引,沒有太多的話題,但是對於多列索引的建立,一個好的多列索引能使用的場景應可以涵蓋很多,減少其他不必要的索引空間,就有很多事情需要注意。
0.首先來了解索引的物理結構:
http://www.jianshu.com/p/1775b4ff123a
1.where 子句中的多列索引
如果表有多個列索引,可以使用任何左邊的字首索引的優化器來查詢行。如何通過字首索引來查詢行我們通過如下例子來詳細瞭解:
現有表formatting,資料如下圖所示:
在該表上建立多列索引:
create index idx_custid_qty_empid on formatting(custid,qty,empid)
1.1 完全使用索引的情況
例如,如果你有一個三列的索引(col1、col2 col3),有索引搜尋功能(col1),(col1,col2),(col1、col2、col3)。
1)where中條件只有col1等於常量
說明:表中custid為A的資料共4行,這4行資料都使用了索引。
mysql> explain select orderid,orderdate from formatting where custid='A' \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ref
possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 32
ref: const
rows: 4 filtered: 100.00
Extra: Using index
2)where中條件只有col1為範圍(>,<,>=,<=)
說明:表中custid大於A的有5行,這5行資料都使用了索引。
mysql> explain select orderid,orderdate from formatting where custid>'A' \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: range
possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 32
ref: NULL
rows: 5
filtered: 100.00
Extra: Using where; Using index
3)where中條件有col1,col2,且col1等於常量,col2等於常量
mysql> explain select orderid,orderdate from formatting where custid='A' and qty=20 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ref
possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 36
ref: const,const
rows: 1
filtered: 100.00
Extra: Using index
表中custid為A,qty等於20的資料只有一條,兩個欄位都使用了索引。
4)where中條件有col1,col2,且col1等於常量,col2等於範圍
mysql> explain select orderid,orderdate from formatting where custid='A' and qty>=20 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: range
possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 36
ref: NULL rows: 2
filtered: 100.00
Extra: Using where; Using index
表中custid為A,qty大於等於20的資料有2條,兩個欄位都使用了索引。
5)where中條件有col1,col2,col3,且col1,col2,col3均等於常量
說明:表中custid為A,qty等於10,empid等於2的資料有1條。3個欄位都使用了索引。
mysql> explain select orderid,orderdate from formatting where custid='A' and qty=10 and empid=2 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ref
possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 40
ref: const,const,const
rows: 1
filtered: 100.00
Extra: Using index
6)where中條件有col1,col2,col3,且col1,col2均等於常量,col3為範圍
說明:表中 custid 為 A,qty 等於 10,empid 大於 2 的資料有1條,這3個欄位都使用了索引。
mysql> explain select orderid,orderdate from formatting where custid='A' and qty=10 and empid>2 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: range
possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 40
ref: NULL
rows: 1
filtered: 100.00
Extra: Using where; Using index
1.2 部分使用索引的情況
1)where中條件col1等於常量,無col2,col3的常量或範圍,只能用到col1的索引
mysql> explain select orderid,orderdate from formatting where custid='A' and empid=2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ref
possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 32 ref: const
rows: 4 filtered: 11.11
Extra: Using where; Using index
說明:custid='A' and empid=2
的資料僅有一條。由於聯合索引的第2例缺失,導致第3列 empid=2
的索引不生效,僅第1列的 custid='A'
的4條資料使用了索引優化。
2)where中條件col1等於常量,col2的範圍,col3的常量或範圍,只能用到col1、col2的索引
mysql> explain select orderid,orderdate from formatting where custid='A' and qty<20 and empid=2 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: range
possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 36
ref: NULL rows: 2
filtered: 11.11
Extra: Using where; Using index
custid='A' and qty<20 and empid=2
的結果只有一條。
由於第2列 qty<20
為範圍,導致第3列empid=2
索引失效。僅第1、2列的 custid='A' and qty<20
使用索引。
3)where中條件col1的範圍,col2、col3的常量或範圍,只能用到col1的索引
mysql> explain select orderid,orderdate from formatting where custid>'A' and empid=2 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: range
possible_keys: idx_custid_qty_empid
key: idx_custid_qty_empid
key_len: 32
ref: NULL
rows: 5
filtered: 11.11
Extra: Using where; Using index
custid>'A' and empid=2
的資料僅有一條。
由於 custid>'A'
為範圍查詢,故僅第1列 custid>'A'
的5條資料使用索引 ,第2、3列的索引失效。
1.3 索引的失效
下列情況均需要掃描所有行再進行篩選,因為多列索引的 第1列沒有在where條件 中,後面列的索引全部失效。
- 1)col2 常量或範圍;
- 2)col3 常量或範圍;
- 3)col2、col3 常量或範圍;
說明:由於沒有 custid 的條件需要掃描9行資料。為何下面的例子中,卻使用了type: index?
因為:當索引是覆蓋索引並且可以用來滿足需要從表中得到的所有資料時,只有索引樹被掃描。在這種情況下,(explain 命令)返回記錄裡的Extra欄位提示 Using index。此表中的 orderid,orderdate
為聯合主鍵。
mysql> explain select orderid,orderdate from formatting where qty=20 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: index
possible_keys: NULL
key: idx_custid_qty_empid
key_len: 40
ref: NULL
rows: 9
filtered: 11.11
Extra: Using where; Using index
我們新增一列新的列名叫version,並對其賦值,如下圖所示。
再來看一下上面會發生什麼變化,此時要查詢多列避免出現覆蓋索引
,我們發現沒有使用任何索引了:
mysql> desc select orderid,orderdate,empid,custid,qty,version from formatting where qty=20 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 10
filtered: 10.00
Extra: Using where
2、order by 子句中的多列索引
2.1、order by 的欄位滿足索引條件,則使用索引。
1) order by 子句中的欄位順序與索引欄位順序一致
create index idx_custid_qty on formatting(custid,qty);
mysql> desc select orderid from formatting order by custid,qty \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: index
possible_keys: NULL
key: idx_custid_qty
key_len: 36
ref: NULL
rows: 10
filtered: 100.00
Extra: Using index
注意:此時若使用 select *
, 則不會使用索引。
mysql> desc select * from formatting order by custid,qty \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 9
filtered: 100.00
Extra: Using filesort
那麼為何有索引卻不能使用呢,我們結合最初的連結中的索引物理結構來理解:
第一種方法是直接讀取表記錄,然後根據 order by
列去排序。
第二種方法是先掃描 order by
上的索引,由於索引本身就是排好序的,因此根據索引的順序再讀出表的記錄即可,省去排序的過程。
我們可以看出第一種方法直接讀取表記錄就可以了。第二種方法需要先讀取索引,然後根據索引去讀取表記錄,io會變得很隨機,因此這種方法會成本比較高,所以優化器會選擇第一種執行執行劃。
2)按照索引順序的where和order by子句可以使用,但where條件中必須使用欄位名(> < =)某常量
mysql> desc select * from formatting where custid='A' order by qty \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ref
possible_keys: idx_custid_qty
key: idx_custid_qty
key_len: 32
ref: const
rows: 4
filtered: 100.00
Extra: Using index condition
custid、qty 都使用了索引。
2.2、下列情況,order by 索引失效
1)同一個order by 中的欄位有多個索引
# 建立 custid 和 empid 的兩個單獨索引
create index idx_custid on formatting(custid) ;
create index idx_empid on formatting(empid) ;
mysql> desc select orderdate from formatting order by custid,empid \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 10
filtered: 100.00
Extra: Using filesort
上例的例子中,order by custid,empid
使用了 idx_custid
和 idx_empid
兩個索引,結果兩個索引都失效。
2)where 與order by 欄位的順序與索引順序不同,排序索引失效
# 聯合索引
create index idx_custid_qty_empid on formatting(custid,qty,empid)
# 單列索引
create index idx_qty on formatting(qty)
mysql> desc select orderdate from formatting where qty=10 order by custid,empid \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: formatting
partitions: NULL
type: ref
possible_keys: idx_qty
key: idx_qty
key_len: 4
ref: const
rows: 2
filtered: 100.00
Extra: Using index condition; Using filesort
例子中聯合索引沒有生效,只有 where 中的單個索引生效了。