索引提供了快速的查詢方案,同時增長了刪除、修改的開銷;續上一篇博客《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經過主鍵彙集數據,也就是對主鍵列聚簇索引。聚簇索引的優點以下:
聚簇索引的缺陷以下:
簡單說,聚簇索引只能索引一列,一般是主鍵;全部數據是按照聚簇索引列的順序連續排列的。
如下表爲例,對比一下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表的主鍵能夠定義爲自增列。一方面保證了順序寫入,另外一方面在主鍵關聯時性能也更好。同時,還能節約索引空間。
其餘幾類索引策略將在下一篇博客中介紹。