MySQL的索引策略(1)

索引提供了快速的查詢方案,同時增長了刪除、修改的開銷;續上一篇博客《MySQL索引基礎》,根據索引的優缺點,本文列舉一些高效的MySQL策略。mysql

  • 不參與計算

select id from table where id+1<10sql

select * from table where to_days(current_data)-to_days(date) <=10數據庫

相似上述查詢,將索引列寫進數學表達式,或者做爲函數的一部分,都會引發索引失效。緩存

  • 前綴索引

​​​​​​​上篇博客提到了模擬哈希索引在長值字段的應用。相似狀況,還可使用前綴索引,即只對字段開始的前n個字符創建索引。前綴索引是犧牲索引選擇性,換取創建索引效率的方案。索引選擇性是衡量索引對字段區分能力的指標,即 count(distinct(column))/count(*);選擇性越大,對數據的區分度越好,則查詢效率越高。例如,對美國城市名作前綴索引,取三位,則San字段會有不少重複名(舊金山、聖塔芭芭拉),總體選擇性會降低。除了索引的選擇性,還要考慮前綴的分佈狀況,儘可能均勻,或者在高頻查詢的數據行有良好的區分性。併發

MySQL沒法基於前綴索引作ORDER BY和GROUP BY操做。逆序字符串的前綴索引就變成了後綴索引,在電子郵箱查詢等特定場景有不錯的效果。函數

  • 多列索引

​​​​​​​多條件查詢很常見,此時就須要創建多列索引。尤爲是OR操做,耗費大量資源用於緩存、排序、合併,這些都不會被優化器計算進查詢成本,影響併發性。能夠用 EXPLAIN ${SQL}查看,若是有索引合併,也說明這種場景須要多列索引。性能

多列索引須要合理的順序,以知足:1. 儘量快地查訊 2.知足ORDER BY和GROUP BY操做  3.儘可能知足覆蓋索引。優化

簡單地,能夠講選擇性最高的列放在最左。spa

mysql> SELECT SUM(staff_id = 2), SUM(customer_id = 584) FROM payment\G
*************************** 1. row ***************************
SUM(staff_id = 2): 7992
SUM(customer_id = 584): 30

mysql> SELECT SUM(staff_id = 2) FROM payment WHERE customer_id = 584\G
*************************** 1. row ***************************
SUM(staff_id = 2): 17

mysql> SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
> COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
> COUNT(*)
> FROM payment\G
*************************** 1. row ***************************
staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
COUNT(*): 16049

第一個查詢代表,customer_id爲584對應的行更少。第二個查詢說明在customer_id=584時,staff_id具備良好的選擇性。第三個查詢顯示,總體上,customer_id具備更好的選擇性。因此這個例子符合選擇性最高的列放在最左的原則。須要注意,查詢1和2的結果,依賴於選定的具體值,可能會對其餘值產生偏見指針

mysql> EXPLAIN SELECT COUNT(DISTINCT threadId) AS COUNT_VALUE
-> FROM Message
-> WHERE (groupId = 10137) AND (userId = 1288826) AND (anonymous = 0)
-> ORDER BY priority DESC, modifiedDate DESC

id: 1
select_type: SIMPLE
table: Message
type: ref
key: ix_groupId_userId
key_len: 18
ref: const,const
rows: 1251162
Extra: Using where

mysql> SELECT COUNT(*), SUM(groupId = 10137),
-> SUM(userId = 1288826), SUM(anonymous = 0)
-> FROM Message\G
*************************** 1. row ***************************
count(*): 4142217
sum(groupId = 10137): 4092654
sum(userId = 1288826): 1288496
sum(anonymous = 0): 4141934

第一個查詢顯示,該表有一個(groupId, userId)的聯合索引。但第二個查詢代表,這兩列上的索引選擇性都很低,基本失效。這類問題不能在數據庫層面解決,須要在業務上特殊處理這些用戶和組,好比禁止相關id查詢這條SQL。這類問題比較廣泛,好比對全部訪客固定一個ID,或者擁有大量好友、評論等的用戶。

還有一個重要的原則,考察該表上執行各種條件查詢的頻率,其相關字段是創建聯合索引的重要依據。

  • 聚簇索引

​​​​​​​聚簇索引是一種存儲方式,InnoDB中,在同一個結構中保存了索引和數據行。即非葉節點保存索引,葉子節點保存數據,與B+樹的定義一致。InnoDB經過主鍵彙集數據,也就是對主鍵列聚簇索引。聚簇索引的優點以下:

  1. 相關數據連續存儲(減小磁盤IO)。例如,郵箱數據,以用戶id爲主鍵,能夠用較少的IO取出與某用戶全部往來郵件。
  2. 訪問速度更快。數據與索引在同一個樹中。
  3. 能夠直接使用葉子頁中的主鍵值。

​​​​​​​聚簇索引的缺陷以下:

  1. 若是全部數據都放進內存,那麼訪問順序的影響就會變小。
  2. 插入順序影響插入速度;以主鍵順序的插入的速度最快。
  3. 更新代價很高。會把對應行移動到新位置,其餘行的位置跟着改變。
  4. 插入或者更新,某列插入已滿的頁,則須要分裂頁,佔用更大的空間。
  5. 致使全表掃描變慢。
  6. 二級索引(即非聚簇索引)更大,由於在葉子節點包含了引用行的主鍵列。因此二級索引的工做順序是:在二級索引中查找主鍵->在聚簇索引中查找行,因此作了兩次B+樹搜索(顧名思義?)。

簡單說,聚簇索引只能索引一列,一般是主鍵;全部數據是按照聚簇索引列的順序連續排列的。

​​​​​​​如下表爲例,對比一下InnoDB聚簇索引和MyISAM中數據存儲的方式。

CREATE TABLE layout_test (
col1 int NOT NULL,
col2 int NOT NULL,
PRIMARY KEY(col1),
KEY(col2)
);

假設col1的值在1-10000,在磁盤上行的順序隨機;col2取值在1-100,有不少重複值。

MyISAM按照插入順序存儲。

這種方式能夠直接根據行號查找到數據,索引中只需保存行號。主鍵索引和col2索引以下圖:

MyISAM的主鍵索引與其餘列的索引並沒有差別;不過是知足了惟1、爲空的索引。

InnoDB支持聚簇索引,其索引和數據分佈狀況以下圖。

一個顯著的不一樣是 ,聚簇索引存儲了整張表;即便主鍵是列前綴也如此。

回滾指針用於事務和MVCC。

InnoDB的二級索引與主鍵索引不一樣。

用主鍵值代替「行指針」的優點是,非主鍵列的插入、更新不會引發大量移動、也分裂;缺陷是佔用了更大的存儲空間。這二者在數據存儲時的對比,可抽象爲下圖。

基於插入性能的考慮,InnoDB表的主鍵能夠定義爲自增列。一方面保證了順序寫入,另外一方面在主鍵關聯時性能也更好。同時,還能節約索引空間。

其餘幾類索引策略將在下一篇博客中介紹。

相關文章
相關標籤/搜索