1. 程式人生 > >mysql多列索引的建立和優化

mysql多列索引的建立和優化

文章目錄


對於單列索引,沒有太多的話題,但是對於多列索引的建立,一個好的多列索引能使用的場景應可以涵蓋很多,減少其他不必要的索引空間,就有很多事情需要注意。

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_custididx_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 中的單個索引生效了。

3) 排序同時包含 asc 與 desc,則排序索引不生效,排序