來源:https://segmentfault.com/a/1190000000473085 假設一高頻查詢如下SELECT * FROM user WHERE area='amoy' AND sex=0 ORDER BY last_login DESC limit 30;如何建立索引?描述考慮的過 ...
來源:https://segmentfault.com/a/1190000000473085
假設一高頻查詢如下
SELECT * FROM user WHERE area='amoy' AND sex=0 ORDER BY last_login DESC limit 30;
如何建立索引?描述考慮的過程
user表如下:
初始化100W條數據,其中,area要通過IP查詢生成,sex為 0,1 隨機
CREATE TABLE user
(id
int(10) NOT NULL AUTO_INCREMENT COMMENT '自增編號',username
varchar(30) NOT NULL DEFAULT '0' COMMENT '用戶名',password
varchar(30) NOT NULL DEFAULT '0' COMMENT '密碼',area
varchar(30) NOT NULL COMMENT '地址',sex
int(10) NOT NULL COMMENT '性別1,男;2,女。',last_login
int(10) NOT NULL COMMENT '最近一次登錄時間戳',
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=892013 DEFAULT CHARSET=latin1
最終我的索引
(last_login,area)
索引原則:
1.where和order by等的欄位建立索引
2.使用唯一索引:對於last_login,area等欄位重覆的次數比較少,可以使用索引;而sex無非就兩個值:性別1,男;2,不值得索引
3.多列索引:不要為每一個列單獨建立索引,這樣並不能將mysql索引的效率最大化。使用“索引合併策略”
4.選擇合理的索引列順序:索引列的順序意味著索引首先按照最左列進行排序,然後是第二列,以此類推。如(last_login,area)會先按照 last_login 進行排序,然後才是area。
5.將選擇性最高的索引放到前面,也就是會所按照這個條件搜索到的數據最少,選擇性就越高,比如選擇性:last_login> area> sex。
6.索引不是越多越好,適合的索引可以提高查詢效率,但是會降低寫入效率,根據項目保持兩者的平衡性最好了。
總結上面,首先sex不適合建立索引,有沒有索引對於效率的提升意義不大,其次索引會按照最左列進行排序,因此將last_login放到最前面。
測試過程:
user表
沒有任何索引的查詢相關日誌:
SELECT * FROM user WHERE area='美國ATT用戶' AND sex=0 ORDER BY last_login DESC limit 30; 0.57s
SELECT * FROM user WHERE area='泰國' AND sex=0 ORDER BY last_login DESC limit 30; 0.56s
SELECT * FROM user WHERE area='臺灣省臺灣大寬頻' AND sex=0 ORDER BY last_login DESC limit 30; 0.55s
SELECT * FROM user WHERE area='美國弗吉尼亞州' AND sex=0 ORDER BY last_login DESC limit 30; 0.59s
SELECT * FROM user WHERE area='德國賓士汽車' AND sex=0 ORDER BY last_login DESC limit 30; 0.55s
SELECT * FROM user WHERE area='臺灣省中華電信' AND sex=0 ORDER BY last_login DESC limit 30; 0.55s
SELECT * FROM user WHERE area='南韓' AND sex=0 ORDER BY last_login DESC limit 30; 0.57s
SELECT * FROM user WHERE area='拉美地區' AND sex=0 ORDER BY last_login DESC limit 30; 0.58s
SELECT * FROM user WHERE area='美國紐約(Prudential)' AND sex=0 ORDER BY last_login DESC limit 30; 0.57s
SELECT * FROM user WHERE area='印度尼西亞' AND sex=0 ORDER BY last_login DESC limit 30; 0.57s
共花費時間:5.66s
建立索引area:
ALTER TABLE user
ADD INDEX index_area
(area
) ;
SELECT * FROM user WHERE area='美國ATT用戶' AND sex=0 ORDER BY last_login DESC limit 30; 0.06s
SELECT * FROM user WHERE area='泰國' AND sex=0 ORDER BY last_login DESC limit 30; 0.02s
SELECT * FROM user WHERE area='臺灣省臺灣大寬頻' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='美國弗吉尼亞州' AND sex=0 ORDER BY last_login DESC limit 30; 0.10s
SELECT * FROM user WHERE area='德國賓士汽車' AND sex=0 ORDER BY last_login DESC limit 30; 0.04s
SELECT * FROM user WHERE area='臺灣省中華電信' AND sex=0 ORDER BY last_login DESC limit 30; 0.02s
SELECT * FROM user WHERE area='南韓' AND sex=0 ORDER BY last_login DESC limit 30; 0.11s
SELECT * FROM user WHERE area='拉美地區' AND sex=0 ORDER BY last_login DESC limit 30; 0.20s
SELECT * FROM user WHERE area='美國紐約(Prudential)' AND sex=0 ORDER BY last_login DESC limit 30; 0.07s
SELECT * FROM user WHERE area='印度尼西亞' AND sex=0 ORDER BY last_login DESC limit 30; 0.04s
共花費時間:0.66s
可見,建立area以後對性能的影響是巨大的(5.66/0.66 約為8.5758倍)
刪除索引:ALTER TABLE user
DROP INDEX index_area
;
刪除area索引發現時間又變成了0.57s
建立last_login索引:
SELECT * FROM user WHERE area='美國ATT用戶' AND sex=0 ORDER BY last_login DESC limit 30; 0.03s
SELECT * FROM user WHERE area='泰國' AND sex=0 ORDER BY last_login DESC limit 30; 0.09s
SELECT * FROM user WHERE area='臺灣省臺灣大寬頻' AND sex=0 ORDER BY last_login DESC limit 30; 0.51s
SELECT * FROM user WHERE area='美國弗吉尼亞州' AND sex=0 ORDER BY last_login DESC limit 30; 0.01s
SELECT * FROM user WHERE area='德國賓士汽車' AND sex=0 ORDER BY last_login DESC limit 30; 0.04s
SELECT * FROM user WHERE area='臺灣省中華電信' AND sex=0 ORDER BY last_login DESC limit 30; 0.07s
SELECT * FROM user WHERE area='南韓' AND sex=0 ORDER BY last_login DESC limit 30; 0.01s
SELECT * FROM user WHERE area='拉美地區' AND sex=0 ORDER BY last_login DESC limit 30; 0.01s
SELECT * FROM user WHERE area='美國紐約(Prudential)' AND sex=0 ORDER BY last_login DESC limit 30; 0.04s
SELECT * FROM user WHERE area='印度尼西亞' AND sex=0 ORDER BY last_login DESC limit 30; 0.06s
共花費時間:0.87s
同樣能夠提升性能(5.66/0.87 約為6.5057倍)
建立sex索引:
ALTER TABLE user
ADD INDEX index_sex
(sex
) ;
SELECT * FROM user WHERE area='美國ATT用戶' AND sex=0 ORDER BY last_login DESC limit 30; 0.87s
SELECT * FROM user WHERE area='泰國' AND sex=0 ORDER BY last_login DESC limit 30; 0.87s
SELECT * FROM user WHERE area='臺灣省臺灣大寬頻' AND sex=0 ORDER BY last_login DESC limit 30; 0.87s
SELECT * FROM user WHERE area='美國弗吉尼亞州' AND sex=0 ORDER BY last_login DESC limit 30; 0.89s
SELECT * FROM user WHERE area='德國賓士汽車' AND sex=0 ORDER BY last_login DESC limit 30; 0.88s
SELECT * FROM user WHERE area='臺灣省中華電信' AND sex=0 ORDER BY last_login DESC limit 30; 0.87s
SELECT * FROM user WHERE area='南韓' AND sex=0 ORDER BY last_login DESC limit 30; 0.86s
SELECT * FROM user WHERE area='拉美地區' AND sex=0 ORDER BY last_login DESC limit 30; 0.88s
SELECT * FROM user WHERE area='美國紐約(Prudential)' AND sex=0 ORDER BY last_login DESC limit 30; 0.87s
SELECT * FROM user WHERE area='印度尼西亞' AND sex=0 ORDER BY last_login DESC limit 30; 0.87s
共花費時間:8.73s
同樣能夠提升性能(5.66s/8.73 約為0.6483倍)效率反而降低了??求解?
建立這個sex索引還不如不建。
刪除索引:
ALTER TABLE user
DROP INDEX index_sex
;
發現時間又變成了0.57s左右,
建立兩個單獨的索引:
ALTER TABLE user
ADD INDEX index_area
(area
) ,
ADD INDEX index_last_login
(last_login
) ;
SELECT * FROM user WHERE area='美國ATT用戶' AND sex=0 ORDER BY last_login DESC limit 30; 0.09s
SELECT * FROM user WHERE area='泰國' AND sex=0 ORDER BY last_login DESC limit 30; 0.33s
SELECT * FROM user WHERE area='臺灣省臺灣大寬頻' AND sex=0 ORDER BY last_login DESC limit 30; 0.21s
SELECT * FROM user WHERE area='美國弗吉尼亞州' AND sex=0 ORDER BY last_login DESC limit 30; 0.01s
SELECT * FROM user WHERE area='德國賓士汽車' AND sex=0 ORDER BY last_login DESC limit 30; 0.28s
SELECT * FROM user WHERE area='臺灣省中華電信' AND sex=0 ORDER BY last_login DESC limit 30; 0.02s
SELECT * FROM user WHERE area='南韓' AND sex=0 ORDER BY last_login DESC limit 30; 0.02s
SELECT * FROM user WHERE area='拉美地區' AND sex=0 ORDER BY last_login DESC limit 30; 0.02s
SELECT * FROM user WHERE area='美國紐約(Prudential)' AND sex=0 ORDER BY last_login DESC limit 30; 0.03s
SELECT * FROM user WHERE area='印度尼西亞' AND sex=0 ORDER BY last_login DESC limit 30; 0.67s
發現建立兩個單獨的索引還不如只建立一個索引
刪除索引:
發現時間又變成了0.57s左右,
建立一個的聯合索引:
ALTER TABLE user
ADD INDEX index_last_login_area
(last_login
,area
) ,
SELECT * FROM user WHERE area='美國ATT用戶' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='泰國' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='臺灣省臺灣大寬頻' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='美國弗吉尼亞州' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='德國賓士汽車' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='臺灣省中華電信' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='南韓' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='拉美地區' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='美國紐約(Prudential)' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
SELECT * FROM user WHERE area='印度尼西亞' AND sex=0 ORDER BY last_login DESC limit 30; 0.00s
額,第二條數據這是怎麼了,我測試了5次都在這附近晃悠哈!
這尼瑪,找對索引啦!就該這麼建立,查詢不出來需要的時間啦!估計就是我們需要的索引啦!!!!
刪除索引:
發現時間又變成了0.57s左右,
建立一個的聯合索引:
ALTER TABLE user
ADD INDEX index_sex_last_login_area
(sex
,last_login
,area
)
SELECT * FROM user WHERE area='美國ATT用戶' AND sex=0 ORDER BY last_login DESC limit 30; 0.18s
SELECT * FROM user WHERE area='泰國' AND sex=0 ORDER BY last_login DESC limit 30; 0.17s
SELECT * FROM user WHERE area='臺灣省臺灣大寬頻' AND sex=0 ORDER BY last_login DESC limit 30; 0.81s
SELECT * FROM user WHERE area='美國弗吉尼亞州' AND sex=0 ORDER BY last_login DESC limit 30; 0.01s
SELECT * FROM user WHERE area='德國賓士汽車' AND sex=0 ORDER BY last_login DESC limit 30; 0.02s
SELECT * FROM user WHERE area='臺灣省中華電信' AND sex=0 ORDER BY last_login DESC limit 30; 0.04s
SELECT * FROM user WHERE area='南韓' AND sex=0 ORDER BY last_login DESC limit 30; 0.01s
SELECT * FROM user WHERE area='拉美地區' AND sex=0 ORDER BY last_login DESC limit 30; 0.01s
SELECT * FROM user WHERE area='美國紐約(Prudential)' AND sex=0 ORDER BY last_login DESC limit 30; 0.03s
SELECT * FROM user WHERE area='印度尼西亞' AND sex=0 ORDER BY last_login DESC limit 30; 0.04s
sex怎麼總是你在拖後腿啊!把你調整到索引的最後一個吧!
刪除索引:
發現時間又變成了0.57s左右,
建立一個的聯合索引:
ALTER TABLE user
ADD INDEX index_last_login_area_sex
(area
,last_login
,sex
)
SELECT * FROM user WHERE area='美國ATT用戶' AND sex=0 ORDER BY last_login DESC limit 30; 0.03s
SELECT * FROM user WHERE area='泰國' AND sex=0 ORDER BY last_login DESC limit 30; 0.07s
SELECT * FROM user WHERE area='臺灣省臺灣大寬頻' AND sex=0 ORDER BY last_login DESC limit 30; 0.50s
SELECT * FROM user WHERE area='美國弗吉尼亞州' AND sex=0 ORDER BY last_login DESC limit 30; 0.02s
SELECT * FROM user WHERE area='德國賓士汽車' AND sex=0 ORDER BY last_login DESC limit 30; 0.05s
SELECT * FROM user WHERE area='臺灣省中華電信' AND sex=0 ORDER BY last_login DESC limit 30; 0.06s
SELECT * FROM user WHERE area='南韓' AND sex=0 ORDER BY last_login DESC limit 30; 0.02s
SELECT * FROM user WHERE area='拉美地區' AND sex=0 ORDER BY last_login DESC limit 30; 0.02s
SELECT * FROM user WHERE area='美國紐約(Prudential)' AND sex=0 ORDER BY last_login DESC limit 30; 0.04s
SELECT * FROM user WHERE area='印度尼西亞' AND sex=0 ORDER BY last_login DESC limit 30; 0.06s
綜上所述:1.建立索引不一定能夠加快查詢效率如sex這種給重覆次數特別多的列增加索引如sex這種會降低查詢效率,具體的原因有待查找
2.給重覆次數比較少的列增加u謳吟還是能夠大幅度提高效率
3.給where和orderby之後的欄位添加索引才會加快查詢效率
4.為每一個列單獨建立索引,不能將索引的效率最大化,應該使用索引合併策略,即根據查詢條件,建立聯合索引
5.聯合索引的順序問題:將選擇性高的索引放到前面
6.根據資料建立索引意味著索引按照最左列進行排序,然後事第二列,以此類推。如(last_login ,area)就會按照last_login進行排序,然後才是area
7.根據這次的這個查詢條件來說最好的索引是:ALTER TABLE user
ADD INDEX index_last_login_area
(last_login
,area
)。