MySQL索引分析



查閱書籍及文章,做下記錄吧,方便熟悉,以免忘了。

索引

  • 索引是什么?能幫助我們解決什么問題?
    索引是對數據庫的一列或多列的值進行排序定位的存儲結構,索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表里所有記錄的引用指針解決大數據下的快速查詢。就像是書的目錄一樣,通過索引查找到正文的頁數。

1. 索引結構方法

B-Tree

按順序存貯數據。

  • 索引的最左列查找,若不是,無法使用索引。如:'like name=%Ja%'。
  • 若查詢中有某一列的范圍查詢,則最右邊所有列都不能使用索引優化查找。如:WHERE last_name = 'Smith' and first_name LIKE 'J%' AND dob='1976-12-23',like是一個范圍查找,前兩列可使用索引查找,后面列無法使用索引。
  • 不能跳過索引中的列。

hash index

哈希索引基於哈希表實現,只有精確匹配索引所有列的查詢才有效。對每行數據,存貯引擎都會對所有的索引列計算一個哈希碼(較小值)。哈希索引將所有的哈希碼存儲到索引中,同時在哈希表中保存指向的數據行的指針。

R-Tree(空間索引)

和B-Tree不同,這類索引無需前綴查詢。空間索引會從所有維度來索引數據,查詢時可以使用任意維度組合查詢。 MyISAM支持空間索引,可用作地理數據存貯。

全文索引

全文索引是一種特殊類型的索引,查找的文本中的關鍵詞,而不是直接比較索引中的值。

在相同列上同事建全文索引和B-Tree索引不會有沖突,全文索引適用於MATCH AGAINST操作,而不是普通通的WHERE條件操作。

Mysql5.6版本之后InnoDB存儲引擎開始支持全文索引

2. 索引類型

B+tree索引分為聚集索引與非聚集索引兩大類。

理解聚集索引和非聚集索引可通過對比漢語字典的索引。漢語字典提供了兩類檢索漢字的方式,第一類是拼音檢索(前提是知道該漢字讀音),比如拼音為cheng的漢字排在拼音chang的漢字后面,根據拼音找到對應漢字的頁碼(因為按拼音排序,二分查找很快就能定位),這就是我們通常所說的字典序;第二類是部首筆畫檢索,根據筆畫找到對應漢字,查到漢字對應的頁碼。拼音檢索就是聚集索引,因為存儲的記錄(數據庫中是行數據、字典中是漢字的詳情記錄)是按照該索引排序的;筆畫索引,雖然筆畫相同的字在筆畫索引中相鄰,但是實際存儲頁碼卻不相鄰。

聚集索引

並不是一種單獨的索引類型,而是一種數據存儲方式。InnoDB的聚集索引實際是在同一個結構中保存了B-Tree索引和數據行。

一種索引,該索引中鍵值的邏輯順序決定了表中相應行的物理順序。
數據行的物理順序與列值(一般是主鍵的那一列,mysql默認主鍵就是聚集索引)的邏輯順序相同,一個表中只能擁有一個聚集索引。
image
特性:

MySQL如果定義主鍵,那么主鍵就是聚集索引;

MySQL如果沒有定義主鍵,表中第一個唯一非空索引就作為聚集索引;

MySQL如果沒有主鍵也沒有合適的唯一索引,那么innodb內部會生成一個隱藏的主鍵作為聚集索引,這個隱藏的主鍵是一個6個字節的列,改列的值會隨着數據的插入自增;

使用UUID聚集索引,主鍵值是亂序的,B-Tree的頁數分行,重新排列,頻繁的頁分裂,變得復雜。

image

MySQL的索引分為B+樹索引和hash索引與全文索引。MyISAM數據庫引擎和InnoDB數據庫引擎的索引都是基於B+樹的。

MyISAM、InnoDB引擎、Memory三個常用引擎類型比較

索引 MyISAM引擎 InnoDB引擎 Memory引擎
B-Tree 索引 支持 支持 支持
HASH 索引 不支持 支持 支持
R-Tree 索引 支持 不支持 不支持
Full-text 索引 不支持 暫不支持 不支持

非聚集索引

一種索引,該索引中索引的邏輯順序與磁盤上行的物理存儲順序不同。

聚集索引與非聚集索引區別

  • 聚集索引一個表只存在一個,而非聚集索引一個表可以存在多個;
  • 聚集索引的葉節點是最終的數據節點,而非聚集索引(二級索引)的葉節點仍然是索引節點,但它有一個指向最終數據的指針;所以獲取數據速度聚集索引比較快。(二級索引子節點保存不是指向行的物理位置的指針,而是行的主鍵值。)
  • 使用聚集索引來做查詢操作時速度很快,但是做插入操作較為費事;
  • InnoDB支持聚集索引,MyISAM不支持聚集索引。
  • 更新聚集索引的列代價很高,InnoDB會強制將每一個更新的行移動到新的位置;

1). 唯一索引

數據列不允許重復,允許為NULL值,一個表允許多個列創建唯一索引。

#創建唯一索引
ALTER TABLE table_name ADD UNIQUE (column);

#創建唯一組合索引
ALTER TABLE table_name ADD UNIQUE (column1,column2);

2). 主鍵索引

不允許有空值的唯一索引。(在B+TREE中的InnoDB引擎中,主鍵索引起到了至關重要的地位)

3). 全文索引

是目前搜索引擎使用的一種關鍵技術。

ALTER TABLE table_name ADD FULLTEXT (column);

4). 普通索引

MySQL中基本索引類型,沒有什么限制,允許在定義索引的列中插入重復值和空值,純粹為了查詢數據更快一點。

ALTER TABLE table_name add INDEX (column)

5). 組合索引

在表中的多個字段組合上創建的索引,只有在查詢條件中使用了這些字段的左邊字段時,索引才會被使用,使用組合索引時遵循最左前綴集合

ALTER TABLE table_name INDEX ( column1,column2 )

6). 空間索引

空間索引是對空間數據類型的字段建立的索引,MySQL中的空間數據類型有四種,GEOMETRY、POINT、LINESTRING、POLYGON。在創建空間索引時,使用SPATIAL關鍵字。要求,引擎為MyISAM,創建空間索引的列,必須將其聲明為NOT NULL。

7). 前綴索引

有時候需要索引很長的字符列,這會讓索引變得大且慢。索引開始的部分字符,大大節約索引空間,提高索引效率。

ALTER TABLE table_name ADD INDEX(column(prefix_length));

3. 索引優點

  1. 大大減少了服務器需要掃描的數量;
  2. 可以幫助服務器避免排序和臨時表;
  3. 可以將隨機I/O變成順序I/O;

4. 索引策略

避免重復索引(列建多個索引)

獨立的列

指索引列不能是表達式的一部分或是某個函數的參數。如:

SELECT * FROM user WHERE age+1=23;

前綴索引和索引選擇性

計算合適的前綴長度,並使前綴的選擇性接近於完整列的選擇性。對於BLOB、TEXT、VARCHAR這些類型的列,必須使用前綴索引,mysql不允許索引這些列的完整長度。

選擇合適的索引列順序

對於聯合索引,如何決定索引字段的順序:

  • 選擇性和基數的經驗法則:將選擇性最高的列放到索引最前列(業務上常用的,如:某個網站根據不同性別,展示不同內容的 sex肯定要作為組合索引前列);
  • WHERE子句中的排序、分組、范圍條件等因素。(明顯縮短查找范圍)

選擇性

索引的選擇性(Selectivity),是指不重復的索引值(也叫基數,Cardinality)與表記錄數(#T)的比值:

Index Selectivity = Cardinality / #T

顯然選擇性的取值范圍為(0,1],選擇性越高的索引價值越大,這是由B+Tree的性質決定的。
如圖:

image

覆蓋索引

索引查找數據高效的方式,MySQL也可以根據索引直接獲取到數據,這樣就不比再去讀取數據行。如果一個索引包含了所需要查詢字段的值,我們就叫做覆蓋索引。(減少了二次查詢)

舉例:學生成績表(StudentScore)包含字段:id、username、score、subject;索引 clustered index(id) index(username)

#非聚集索引節點直接獲取列數據
SELECT id,username FROM StudentScore WHERE username='小米'
SELECT username FROM StudentScore WHERE username='小米'

#二次查詢
SELECT id,username,score FROM StudentScore WHERE username='小米'

冗余和重復索引

MySQL允許在同一列建多個索引。
重復索引是在相同的列上按照相同的順序創建相同類型的索引。

冗余索引 如創建index(A,B) ,又創建index(A)。大多數情況不需要冗余索引,應盡量擴展已有索引,而不是創建新索引。

優化

  • 索引查詢字段類型一致;
  • 最左原則;
  • 最左前綴;
  • 避免多個范圍條件;
  • 優化排序;
  • 盡量使用覆蓋索引;

4. 索引和鎖

索引可以讓查詢鎖定更少的行。

InnoDB在訪問行的時候才會對其加鎖,索引減少InnoDB訪問的行數,從而減少鎖的數量,減少鎖爭用。

索引操作

添加索引:alter table table_name add index index_name(fileds(length);

查看索引:show index from table_name;

刪除索引:drop index index_name ON table_name;

模擬:

create table test(
a varchar(32) default '',
b varchar(32) default '',
c int(11) default 0,
d char(64) default ''
)

index union_index(a,b,c) 聯合索引實際生成a、ab、abc索引,遵循最左原則。

a,ab,ba(查詢優化器會交換),abc,acb(cb交換) 可以命中索引;
b,c,bc,cb 無法命中索引。

此時對a加索引index a(a)

explain select * from test where a='1' ;
+----+-------------+-------+------------+------+-----------------+---------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys   | key           | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+-----------------+---------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | test  | NULL       | ref  | union_index,a   | union_index   | 99      | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+-----------------+---------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

可以看到索引會命中到 聯合索引union_index上。

此時刪除union_index(a,b,c),重新添加索引union_index(a,b,c)

mysql> alter table test add index union_index(a,b,c);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from test where a='1' ;
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | test  | NULL       | ref  | a,union_index | a    | 99      | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

對abc查詢

mysql> explain select * from test where a='1' and b='2' and c=1 ;
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | test  | NULL       | ref  | a,union_index | a    | 99      | const |    1 |    33.33 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

可以看到索引會命中到 索引a上。故此推斷出,索引的添加順序影響到索引的命中順序,也就是說會優先從索引a上查找,如果顛倒union_index和a的添加順序,會命中union_index。

這個理解可能是片面的,因為數據量不大,如果數據千萬級別,查詢優化器會選擇效率最高的查詢方案。

關於or查詢的真相是:
所謂的索引失效指的是:假如or連接的倆個查詢條件字段中有一個沒有索引的話,引擎會放棄索引而產生全表掃描。我們從or的基本含義出發應該能理解並認可這種說法,沒啥問題。

null值問題
Mysql難以優化引用可空列查詢,它會使索引、索引統計和值更加復雜。可空列需要更多的存儲空間,還需要mysql內部進行特殊處理。可空列被索引后,每條記錄都需要一個額外的字節,還能導致MYisam 中固定大小的索引變成可變大小的索引。

  • NOT IN、!= 等負向條件查詢在有 NULL 值的情況下返回永遠為空結果,查詢容易出錯
  • NULL值到非NULL的更新無法做到原地更新,更容易發生索引分裂,從而影響性能。
  • 單列索引不存null值,復合索引不存全為null的值,如果列允許為null,可能會得到“不符合預期”的結果集
  • 如果有 Null column 存在的情況下,count(Null column)需要格外注意,null 值不會參與統計。
  • 注意 Null 字段的判斷方式, = null 將會得到錯誤的結果,用 is null

注意:索引字段類型查詢值不一致,無法命中索引;
explain通過key_len、ref屬性,可以推測下聯合索引命中的字段。

explain列解釋:

select_type 查詢中每個select子句的類型
  (1) SIMPLE(簡單SELECT,不使用UNION或子查詢等)
  
  (2) PRIMARY(子查詢中最外層查詢,查詢中若包含任何復雜的子部分,最外層的select被標記為PRIMARY)
  
  (3) UNION(UNION中的第二個或后面的SELECT語句)
  
  (4) DEPENDENT UNION(UNION中的第二個或后面的SELECT語句,取決於外面的查詢)
  
  (5) UNION RESULT(UNION的結果,union語句中第二個select開始后面所有select)
  
  (6) SUBQUERY(子查詢中的第一個SELECT,結果不依賴於外部查詢)
  
  (7) DEPENDENT SUBQUERY(子查詢中的第一個SELECT,依賴於外部查詢)
  
  (8) DERIVED(派生表的SELECT, FROM子句的子查詢)
  
  (9) UNCACHEABLE SUBQUERY(一個子查詢的結果不能被緩存,必須重新評估外鏈接的第一行)
  
type :這是重要的列,顯示連接使用了何種類型。從最好到最差的連接類型為
- const(針對主鍵或唯一索引的等值查詢掃描, 最多只返回一行數據.)、
- eq_ref 此類型通常出現在多表的 join 查詢, 表示對於前表的每一個結果, 都只能匹配到后表的一行結果. 並且查詢的比較操作通常是 =, 查詢效率較高;
- ref 此類型通常出現在多表的 join 查詢, 針對於非唯一或非主鍵索引, 或者是使用了 最左前綴 規則索引的查詢;
- range 表示使用索引范圍查詢, 通過索引字段范圍獲取表中部分數據記錄. 這個類型通常出現在 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操作中;
- index 表示全索引掃描(full index scan), 和 ALL 類型類似, 只不過 ALL 類型是全表掃描, 而 index 類型則僅僅掃描所有的索引, 而不掃描數據;
- ALL 全表掃描,性能最差

  結果值從好到壞依次是:system(表中只有一條數據) > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

possible_keys:顯示可能應用在這張表中的索引。如果為空,沒有可能的索引。可以為相關的域從WHERE語句中選擇一個合適的語句

key: 實際使用的索引。如果為NULL,則沒有使用索引。很少的情況下,MYSQL會選擇優化不足的索引

key_len:使用的索引的長度。在不損失精確性的情況下,長度越短越好

ref:顯示索引的哪一列被使用了,如果可能的話,是一個常數

rows:MYSQL認為必須檢查的用來返回請求數據的行數

filtered:按表條件過濾的行百分比

Extra:關於MYSQL如何解析查詢的額外信息

  Using where:不用讀取表中所有信息,僅通過索引就可以獲取所需數據,這發生在對表的全部的請求列都是同一個索引的部分的時候,表示mysql服務器將在存儲引擎檢索行后再進行過濾

  Using temporary:表示MySQL需要使用臨時表來存儲結果集,常見於排序和分組查詢,常見 group by ; order by

  Using filesort:當Query中包含 order by 操作,而且無法利用索引完成的排序操作稱為“文件排序”
 
  Using join buffer:改值強調了在獲取連接條件時沒有使用索引,並且需要連接緩沖區來存儲中間結果。如果出現了這個值,那應該注意,根據查詢的具體情況可能需要添加索引來改進能。
  
  Impossible where:這個值強調了where語句會導致沒有符合條件的行(通過收集統計信息不可能存在結果)。
  
  Select tables optimized away:這個值意味着僅通過使用索引,優化器可能僅從聚合函數結果中返回一行
  
  No tables used:Query語句中使用from dual 或不含任何from子句
  

參考:

《高性能MySQL》書籍

聚集索引與非聚集索引的總結

理解InnoDB的聚集索引

MySQL索引背后的數據結構及算法原理

MYSQL-索引

MYSQL只會用到一個索引

MySQL 性能優化神器 Explain 使用分析


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM