理解索引:索引優化

最近有個需求,要修改現有存儲結構,涉及查詢條件和查詢效率的考量,看了幾篇索引和HBase相關的文章,回憶了相關知識,結合項目需求,說說本身的理解和總結。sql

錯過的朋友能夠先回顧下前3篇文章:數據庫

  1. 索引結構和數據定位過程
  2. 查詢過程和高級查詢
  3. 執行計劃詳細介紹

上一篇詳細介紹了explain命令,經過該命令,能夠定位出在哪一步出現了性能問題,下一步就是經過優化索引來解決它。緩存

部份內容摘錄了幾個博友的文章,最後會給出文章連接,感謝他們的精彩分析。微信

常見優化方法

聯合索引最左前綴原則

複合索引遵照「最左前綴」原則,查詢條件中,使用了複合索引前面的字段,索引纔會被使用,若是不是按照索引的最左列開始查找,則沒法使用索引。函數

好比在(a,b,c)三個字段上創建聯合索引,那麼它可以加快a|(a,b)|(a,b,c)三組查詢的速度,而不能加快b|(b,a)這種查詢順序。post

另外,建聯合索引的時候,區分度最高的字段在最左邊。性能

不要在列上使用函數和進行運算

不要在列上使用函數,這將致使索引失效而進行全表掃描。優化

例以下面的 SQL 語句:ui

select * from artile where YEAR(create_time) <= '2018'; 
複製代碼

即便 date 上創建了索引,也會全表掃描,能夠把計算放到業務層,這樣作不只能夠節省數據庫的 CPU,還能夠起到查詢緩存優化效果。spa

負向條件查詢不能使用索引

負向條件有:!=、<>、not in、not exists、not like 等。

select * from artile where status != 1 and status != 2;
複製代碼

可使用in進行優化:

select * from artile where status in (0,3)
複製代碼
使用覆蓋索引

所謂覆蓋索引,是指被查詢的列,數據能從索引中取得,而不用經過行定位符再到數據表上獲取,可以極大的提升性能。

能夠定義一個讓索引包含的額外的列,即便這個列對於索引而言是無用的。

避免強制類型轉換

當查詢條件左右兩側類型不匹配的時候會發生強制轉換,強制轉換可能致使索引失效而進行全表掃描。

若是phone字段是varchar類型,則下面的SQL不能命中索引:

select * from user where phone=12345678901;
複製代碼

能夠優化爲:

select * from user where phone='12345678901';
複製代碼
範圍列能夠用到索引

範圍條件有:<、<=、>、>=、between等。

範圍列能夠用到索引,可是範圍列後面的列沒法用到索引,索引最多用於一個範圍列,若是查詢條件中有兩個範圍列則沒法全用到索引。

更新頻繁、數據區分度不高的字段上不宜創建索引

更新會變動B+樹,更新頻繁的字段創建索引會大大下降數據庫性能。

「性別」這種區分度不大的屬性,創建索引沒有意義,不能有效過濾數據,性能與全表掃描相似。

區分度可使用 count(distinct(列名))/count(*) 來計算,在80%以上的時候就能夠創建索引。

索引列不容許爲null

單列索引不存null值,複合索引不存全爲null的值,若是列容許爲 null,可能會獲得不符合預期的結果集。

避免使用or來鏈接條件

應該儘可能避免在 where 子句中使用 or 來鏈接條件,由於這會致使索引失效而進行全表掃描,雖然新版的MySQL可以命中索引,但查詢優化耗費的 CPU比in多。

模糊查詢

前導模糊查詢不能使用索引,非前導查詢能夠。

優化案例

利用延遲關聯或者子查詢優化超多分頁場景

MySQL 並非跳過 offset 行,而是取 offset+N 行,而後返回放棄前 offset 行,返回 N 行。

當 offset 特別大的時候,效率很是低下,要麼控制返回的總頁數,要麼對超過特定閾值的頁數進行SQL改寫。

能夠先快速定位須要獲取的id段,而後再關聯:

selecta.* from 表1 a,(select id from 表1 where 條件 limit 1000000 ,10 ) b where a.id=b.id  
複製代碼
若是明確知道只有一條結果返回,limit 1 可以提升效率

雖然本身知道只有一條結果,但數據庫並不知道,明確告訴它,讓它主動中止遊標移動。

如何創建索引
where a=1 and b=1  
where b=1  
where b=1 order by time desc
複製代碼

建議創建兩個索引,即 idx_ab(a,b) 和 idx_b_time(b,time)

MySQL 的查詢優化器會自動調整where子句的條件順序以使用適合的索引,對於上面的第一條 SQL,若是創建索引爲idx_ba(b,a) 也是能夠用到索引的。

多值匹配和範圍匹配

假若有聯合索引(empno、title、fromdate),下面的 SQL 是否能夠用到索引,若是能夠的話,會使用幾個?

select * from employee.titles  
where emp_no between '10001' and'10010'  
and title='軟件工程師'   
and from_date between '2008-01-01'and '2018-01-01'  
複製代碼

可使用索引,能夠用到索引所有三個列,這個 SQL看起來是用了兩個範圍查詢,但做用於empno上的between實際上至關於in,也就是說empno 實際是多值精確匹配。

在 MySQL 中要謹慎地區分多值匹配和範圍匹配,不然會對 MySQL 的行爲產生困惑。

聯合索引的最左匹配原則

假如創建聯合索引(a,b,c),下列語句是否可使用索引,若是能夠,使用了那幾列?

where a= 3  // 是,使用了a列
where a= 3 and b = 5  // 是,使用了ab列  
where a = 3 and c = 4 and b = 5  // 是,使用了 a,b,c 列  
where b= 3 // 否
where a= 3 and c = 4  // 是,使用了a列  
where a = 3 and b > 10 and c = 7  // 是,使用了 a,b 列  
where a = 3 and b like 'xx%' andc = 7  // 是,使用了 a,b 列
複製代碼
根據區分度建立索引

有以下查詢語句,查找指定產品已審覈(status=1)的評論:

SELECT user_id,title,content FROM `comment`
WHERE status=1 AND product_id=1
LIMIT 0,5 ;
複製代碼

能夠創建聯合索引,status和product_id,可是哪一個放左邊就要計算區分度:

SELECT COUNT(DISTINCT status)/COUNT(*) AS audit_rate,
COUNT(DISTINCT product_id)/COUNT(*) AS product_rate
FROM comment;
複製代碼

通常product的區分度會高點,能夠建立以下索引:

CREATE INDEX idx_productID_Status ON comment(product_id,status)
複製代碼
排序字段索引

查看某個用戶最近20條登陸記錄,按時間排序:

select *  from login_history where uid = $uid order by create_time desc limit 20;
複製代碼

創建uid+timeline複合索引,將排序引入到索引結構中,數據庫負載驟降。

參考文章:

  1. MySQL 索引及優化實戰
  2. 索引使用的注意事項

歡迎掃描下方二維碼,關注個人我的微信公衆號,查看更多文章 ~

情情說
相關文章
相關標籤/搜索