DB索引、索引覆蓋、索引優化
###########索引###########
@see http://mp.weixin.qq.com/s/4W4iVOZHdMglk0F_Ikao7A
聚集索引(clustered index):聚集索引決定數據在磁盤上的物理排序,一個表只能有一個聚集索引,一般用primary key來約束。
舉例:t_user場景中,uid上的索引。
非聚集索引(non-clustered index):它並不決定數據在磁盤上的物理排序,索引上只包含被建立索引的數據,以及一個行定位符row-locator,這個行定位符,可以理解為一個聚集索引物理排序的指針,通過這個指針,可以找到行數據。
舉例
select uid from t_user where age > 18 and age < 26;
age上建立的索引,就是非聚集索引。
聯合索引:多個字段上建立的索引,能夠加速復核查詢條件的檢索
舉例,登錄業務需求:
select uid, login_time from t_user where
login_name=? and passwd=?
可以建立(login_name, passwd)的聯合索引。
聯合索引能夠滿足最左側查詢需求,例如(a, b, c)三列的聯合索引,能夠加速a | (a, b) | (a, b, c) 三組查詢需求。
這也就是為何不建立(passwd, login_name)這樣聯合索引的原因,業務上幾乎沒有passwd的單條件查詢需求,而有很多login_name的單條件查詢需求。
提問:
select uid, login_time from t_user where
passwd=? and login_name=?
能否命中(login_name, passwd)這個聯合索引?
回答:可以,最左側查詢需求,並不是指SQL語句的寫法必須滿足索引的順序(這是很多朋友的誤解)
索引覆蓋:被查詢的列,數據能從索引中取得,而不用通過行定位符row-locator再到row上獲取,即“被查詢列要被所建的索引覆蓋”,這能夠加速查詢速度。
###########索引優化############
@see http://mp.weixin.qq.com/s/ZWez27EmVw_u7GzNbvXuYw
舉例,登錄業務需求:
select uid, login_time from t_user where
login_name=? and passwd=?
可以建立(login_name, passwd, login_time)的聯合索引,由於login_time已經建立在索引中了,被查詢的uid和login_time就不用去row上獲取數據了,從而加速查詢。
末了多說一句,登錄這個業務場景,login_name具備唯一性,建這個單列索引就好。
假設訂單業務表結構為:
order(oid, date, uid, status, money, time, …)
其中:
oid,訂單ID,主鍵
date,下單日期,有普通索引,管理後臺經常按照date查詢
uid,用戶ID,有普通索引,用戶查詢自己訂單
status,訂單狀態,有普通索引,管理後臺經常按照status查詢
money/time,訂單金額/時間,被查詢字段,無索引
…
假設訂單有三種狀態:0已下單,1已支付,2已完成
業務需求,查詢未完成的訂單,哪個SQL更快呢?
select * from order where status!=2
select * from order where status=0 or status=1
select * from order where status IN (0,1)
select * from order where status=0
union all
select * from order where status=1
結論:方案1最慢,方案2,3,4都能命中索引
但是...
一:union all 肯定是能夠命中索引的
select * from order where status=0
union all
select * from order where status=1
說明:
直接告訴MySQL怎麽做,MySQL耗費的CPU最少
程序員並不經常這麽寫SQL(union all)
二:簡單的in能夠命中索引
select * from order where status in (0,1)
說明:
讓MySQL思考,查詢優化耗費的cpu比union all多,但可以忽略不計
程序員最常這麽寫SQL(in),這個例子,最建議這麽寫
三:對於or,新版的MySQL能夠命中索引
select * from order where status=0 or status=1
說明:
讓MySQL思考,查詢優化耗費的cpu比in多,別把負擔交給MySQL
不建議程序員頻繁用or,不是所有的or都命中索引
對於老版本的MySQL,建議查詢分析下
四、對於!=,負向查詢肯定不能命中索引
select * from order where status!=2
說明:
全表掃描,效率最低,所有方案中最慢
禁止使用負向查詢。使用負向查詢時,一定要帶上其他可以過濾大量數據的正向查詢條件,這樣配合使用負向查詢是可以的。
舉例:
SELECT oid FROM t_order WHERE uid=123 AND status != 1;
訂單表5000w數據,但uid=123就會迅速的將數據量過濾到很少的級別(uid建立了索引),此時再接上一個負向的查詢條件就無所謂了,掃描的行數本身就會很少。
但如果要查詢所有已完成訂單之外的訂單:
SELECT oid FROM t_order WHERE status != 1;
這就掛了,立馬CPU100%,status索引會失效,負向查詢導致全表掃描。
五、其他方案
select * from order where status < 2
這個具體的例子中,確實快,但是:
這個例子只舉了3個狀態,實際業務不止這3個狀態,並且狀態的“值”正好滿足偏序關系,萬一是查其他狀態呢,SQL不宜依賴於枚舉的值,方案不通用
這個SQL可讀性差,可理解性差,可維護性差,強烈不推薦
DB索引、索引覆蓋、索引優化