高性能索引優(yōu)化策略(三):索引列的次序該如何排列更合適?

在眾多困擾索引使用的原因中,其中最常見的一個(gè)是索引中列的次序秤朗。正確的次序依賴于使用索引的查詢,因此需要考慮怎樣選擇索引次序以便數(shù)據(jù)行的排序火分組能夠從中受益(這個(gè)僅在二叉樹索引有用笔喉,哈希索引和其他類型的索引并沒有像二叉樹索引那樣對(duì)數(shù)據(jù)進(jìn)行排序)取视。

在二叉樹索引中多列的順序意味著會(huì)首先對(duì)最左列進(jìn)行排序,然后才是其他列常挚。因此作谭,為滿足ORDER BY,GROUP BY和DISTINCT的條件的查詢奄毡,索引可能會(huì)按正向或逆向掃描進(jìn)行排序折欠。

結(jié)果就是,索引列的次序在多列索引中極其重要吼过。這個(gè)次序有可能強(qiáng)化或弱化性能锐秦。接下來會(huì)通過很多例子說明這種情況。有一個(gè)古老的值得推薦的原則:將最具篩選性的列放在索引的第一位盗忱。這個(gè)建議多有用酱床?在某些例子中是有用的,但是與避免隨機(jī)I/O和排序相比趟佃,就沒有那么重要了(有很多特殊的例子斤葱,因此沒有一個(gè)普適性的原則。這里只是告訴你這個(gè)原則未必有你想的那么重要)揖闸。

在沒有排序和分組的時(shí)候揍堕,將最具篩選性的列放在第一位會(huì)是一個(gè)好主意,因?yàn)檫@時(shí)候索引僅僅是優(yōu)化WHERE條件的查詢汤纸。在這類場(chǎng)景下衩茸,這樣的索引確實(shí)能夠足夠快地篩選出想要的數(shù)據(jù)。然而贮泞,這不僅僅依賴于列的篩選性楞慈,還同樣依賴于查找數(shù)據(jù)行的值——值的離散性幔烛。這和我們選擇一個(gè)好的前綴索引長度是類似的。你可能會(huì)需要選擇一個(gè)合適的索引列次序去盡可能地滿足最頻繁查詢的篩選性囊蓝。

以下面的查詢?yōu)槔?/p>

SELECT * FROM payment WHERE staff_id = 2 AND customer_id = 584;

你應(yīng)該在(staff_id, customer_id)創(chuàng)建一個(gè)索引或者是以相反的次序創(chuàng)建索引嗎饿悬?我們可以運(yùn)行一些查詢?nèi)z查數(shù)據(jù)表數(shù)據(jù)的離散性來決定哪個(gè)次序更具備篩選性。讓我們將查詢轉(zhuǎn)換一下聚霜,去統(tǒng)計(jì)候選項(xiàng)的數(shù)量:

SELECT SUM(staff_id = 2), SUM(customer_id = 584) FROM payment;
--------------------------------------------------------------
SUM(staff_id = 2):7992
SUM(customer_id = 584): 30

根據(jù)首要原則來看狡恬,我們應(yīng)該將customer_id放在第一位,因?yàn)檫@個(gè)條件匹配的數(shù)據(jù)行更少蝎宇。然后我們?cè)賮砜纯粗付薱ustomer_id后的staff_id的篩選性怎么樣:

SELECT SUM(staff_id = 2) FROM payment WHERE customer_id=584;
--------------------------------------------------------------
SUM(staff_id = 2):17

請(qǐng)慎用這項(xiàng)技巧弟劲,因?yàn)檫@個(gè)結(jié)果是依賴于特定的常量的。如果你對(duì)這個(gè)查詢這樣優(yōu)化你的索引可能其他的查詢并不會(huì)表現(xiàn)得很好姥芥。服務(wù)器的性能可能全部受影響或者部分查詢并不像我們預(yù)期那樣運(yùn)行兔乞。

如果你使用像pt-query-digest的工具來分析最壞的情況,這個(gè)可能是一個(gè)有效的途徑去看看什么索引是最適合你的查詢和數(shù)據(jù)的凉唐。但是庸追,如果你沒有特殊的樣例去運(yùn)行,也許使用那個(gè)古老的首要原則會(huì)更好 —— 這是根據(jù)整體情況來選擇的台囱,而不是單個(gè)查詢锚国。

SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity, 
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
COUNT(*)
FROM payment;
--------------------------------------------------------------
staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
COUNT(*): 16049

customer_id具有更高的篩選性,因此答案是將這列放在第一位:

ALTER TABLE payment ADD KEY(customer_id, staff_id);

對(duì)于前綴索引玄坦,與正常的基數(shù)相比血筑,特殊值的問題會(huì)更多。例如煎楣,我們會(huì)發(fā)現(xiàn)有些應(yīng)用將未登錄的用戶當(dāng)作游客——在session表中會(huì)有一個(gè)特殊的user ID豺总,而其他地方記錄著這個(gè)用戶的活動(dòng)。包括這樣的user ID的查詢與其他查詢相比可能差別很大择懂。這是因?yàn)橥ǔ?huì)有很多未登錄的session記錄喻喳。我們會(huì)發(fā)現(xiàn)系統(tǒng)賬戶會(huì)導(dǎo)致同樣的問題。一個(gè)應(yīng)用有一個(gè)魔法的管理員賬戶困曙,這并不是真正的用戶表伦,而是整個(gè)網(wǎng)站的每一個(gè)用戶的“好友”——以便這個(gè)賬戶可以發(fā)送狀態(tài)通知和其他消息。這個(gè)用戶有龐大的好友列表慷丽,結(jié)果會(huì)導(dǎo)致網(wǎng)站的性能問題蹦哼。

這在現(xiàn)實(shí)中非常典型。任何系統(tǒng)無關(guān)的用戶要糊,即便它不是應(yīng)用管理的一個(gè)錯(cuò)誤決定纲熏,也會(huì)導(dǎo)致問題。那些真正擁有許多好友、照片局劲、狀態(tài)消息和點(diǎn)贊的用戶勺拣,可能會(huì)面臨假用戶混在一起的麻煩。

以下是我們?cè)?jīng)見到過的一個(gè)真實(shí)案例鱼填。一個(gè)供用戶交流產(chǎn)品體驗(yàn)和經(jīng)驗(yàn)的產(chǎn)品論壇药有,在一些特殊的場(chǎng)景中運(yùn)行十分緩慢。

SELECT COUNT(DISTINCT threadId) AS COUNT_VALUE
FROM Message
WHERE (groupId = 10137) AND (userId = 1288826) AND (anonyous = 0)
ORDERBY priority DESC, modifiedDate DESC;

這個(gè)查詢看似是索引沒有用好苹丸,因此客戶讓我們檢查看看能不能優(yōu)化愤惰,EXPLAIN的結(jié)果如下:

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

MySQL選擇的索引是(groupId, userId),在沒有數(shù)據(jù)列的基數(shù)信息時(shí)谈跛,這看起來是十分正確的選擇羊苟。然而塑陵,當(dāng)我們檢查有多少行數(shù)據(jù)匹配那個(gè)user ID和group Id時(shí)感憾,出現(xiàn)了不同的情況:

SELECT COUNT(*), SUM(groupId = 10137),
SUM(userId = 1288826), SUM(anonymous = 0)
FROM Message;
-----------------------------------------------------
count(*): 4142217
sum(groupId = 10137): 4092654
sum(userId = 1288826):1288496
sum(anonymous = 0): 4141934

這樣的結(jié)果顯示這個(gè)分組基本上覆蓋了整個(gè)數(shù)據(jù)表。這個(gè)用戶有130萬行相關(guān)的數(shù)據(jù)令花。這個(gè)案例里阻桅,索引根本沒法解決這樣的問題。這是因?yàn)閿?shù)據(jù)是從別的應(yīng)用遷移過來的兼都,而所有的消息都被賦予了管理員用戶嫂沉,并在在導(dǎo)入過程中分進(jìn)了一個(gè)組。這個(gè)問題的解決方案是修改應(yīng)用的代碼去識(shí)別特殊的用戶和分組扮碧,而不是對(duì)這個(gè)用戶的查詢問題趟章。

這個(gè)小故事要告訴大家的是首要原則是有用的,但是需要小心的是慎王,平均性能并不能代表特殊案例的性能蚓土,而特殊案例可能拖垮整個(gè)應(yīng)用的性能。

最后赖淤,雖然關(guān)于選擇性和基數(shù)的首要原則是十分有吸引力的蜀漆,但其他因素,例如排序咱旱,分組和范圍條件在WHERE條件中也可能會(huì)對(duì)查詢的性能影響很大确丢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吐限,隨后出現(xiàn)的幾起案子鲜侥,更是在濱河造成了極大的恐慌,老刑警劉巖诸典,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剃毒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)赘阀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門益缠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人基公,你說我怎么就攤上這事幅慌。” “怎么了轰豆?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵胰伍,是天一觀的道長。 經(jīng)常有香客問我酸休,道長骂租,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任斑司,我火速辦了婚禮渗饮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宿刮。我一直安慰自己互站,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布僵缺。 她就那樣靜靜地躺著胡桃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪磕潮。 梳的紋絲不亂的頭發(fā)上翠胰,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音自脯,去河邊找鬼之景。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冤今,可吹牛的內(nèi)容都是我干的闺兢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼戏罢,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼屋谭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起龟糕,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤桐磁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后讲岁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體我擂,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衬以,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了校摩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片看峻。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖衙吩,靈堂內(nèi)的尸體忽然破棺而出互妓,到底是詐尸還是另有隱情,我是刑警寧澤坤塞,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布冯勉,位于F島的核電站,受9級(jí)特大地震影響摹芙,放射性物質(zhì)發(fā)生泄漏灼狰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一浮禾、第九天 我趴在偏房一處隱蔽的房頂上張望交胚。 院中可真熱鬧,春花似錦伐厌、人聲如沸承绸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至轩猩,卻和暖如春卷扮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背均践。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國打工晤锹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人彤委。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓鞭铆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親焦影。 傳聞我的和親對(duì)象是個(gè)殘疾皇子车遂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容