這是前幾天同事做的技術(shù)分析块仆,講的挺不錯脐彩,現(xiàn)在整理一下
本文中數(shù)據(jù)庫操作與數(shù)據(jù)庫工作原理描述徙歼,都是經(jīng)過簡化的犁河,實際應(yīng)用中鳖枕,數(shù)據(jù)庫本身也會提供各種優(yōu)化措施。
數(shù)據(jù)庫線性搜索
假設(shè)現(xiàn)在又一個幾百萬條數(shù)據(jù)的購物車銷售記錄表桨螺,表結(jié)構(gòu)類似下表
id | userid | productid | dealdate | promotions | dealstate |
---|---|---|---|---|---|
1 | 10876 | 100003 | 14567776667 | -9.5 | 成交 |
2 | 93772 | 188293 | 14566636777 | -8 | 關(guān)閉 |
Q1: 查詢 userid = 100003 的用戶宾符,所有購買記錄。
A1: 在沒有做任何優(yōu)化的情況下灭翔,數(shù)據(jù)庫需要從 id = 1 的記錄開始向下掃描魏烫,直到將整個?表掃描完畢,才可以檢索出所有?符合要求的記錄缠局。?可想而知则奥,當(dāng)表中記錄非常多的時候,需要?非常多的磁盤 I/O 才能完成此次查詢狭园。在實際應(yīng)用中读处,例如 x 東用戶查詢自己的購買記錄這種需求,對服務(wù)器來說就是災(zāi)難啊唱矛。
使用索引提高查詢效率
使用?單個字段建立索引
回到 Q1 這個問題罚舱,如果我們在 userid 這個字段上建立索引,那么會產(chǎn)生一個 userid 到?記錄位置的映射绎谦。?這也是一個?表管闷,在這個表中?查找某個 userid,就能夠找到所有符合該 userid ?的記錄在表中的位置窃肠,然后直接訪問這些位置讀取記錄就可以了包个。
使用多個字段建立索引
?Q2: 如果遇到一個購物達(dá)人,購物記錄有上萬條冤留,通過 userid 這個索引查詢該用戶的所有搜索記錄碧囊,就?會產(chǎn)生上萬次磁盤 I/O,如果是機(jī)械硬盤纤怒,單單是尋道時間就?把這次查詢卡爆了糯而,即使換 SSD,能夠服務(wù)的用戶?也是很有限的泊窘。
A2: ?遇到這種情況熄驼,我們可以將該用戶的購物記錄分頁,每次返回十幾條展示出來就可以了烘豹。向后翻頁的時候瓜贾,可以從 offset 開始,再檢索出十幾條返回到前端展示携悯。特別是移動端與桌面端?可能會跳到第10頁這種需求不同祭芦,向來都是上拉加載更多,是線性加載的蚌卤,?對數(shù)據(jù)庫壓力就更小了实束。(PS. 跳轉(zhuǎn)到第幾頁這種需求奥秆,需要 count 數(shù)據(jù)的總數(shù),通常情況下咸灿,這個操作也需要將所有記錄讀取一遍构订,效率很低。)
這種需求下避矢,一般會將購物記錄按照時間順序返回給用戶悼瘾,我們只需要用 userid 和 dealdate 這兩個字段建立一個復(fù)合索引{userid,dealdate}就可以了审胸。(建立索引時亥宿,指定 dealdate 按照降序排列)
檢索時,查找“近一月購物記錄”砂沛,只要按照時間檢索一些數(shù)據(jù)出來就可以了烫扼。
不應(yīng)該建立?什么樣的索引
如果?我們需要查詢成交狀態(tài),是不是可以?建立一個{dealstate}這樣的索引呢碍庵?
答案是映企,沒什么用。
如果檢索 dealstate = 成交 這樣的記錄静浴,如果掃描整個表堰氓,需要檢索的記錄是 500?w 條。建立索引后苹享,檢索的記錄也就減到 100w-200w 條双絮。
這種優(yōu)化,并沒有數(shù)量級上的優(yōu)化效果得问,還需要占用大量空間囤攀,這種“優(yōu)化”一般認(rèn)為是沒有意義的。
對于值為 bool 型或 enum? 型的字段椭赋,通常不建立索引抚岗。
建立索引的兩種原理
索引到數(shù)據(jù)映射一般有兩種實現(xiàn)方式或杠,一種是 hash?哪怔,這個沒有排序的能力,另一種是? B 樹或 B+ 樹向抢,這個有排序的能力认境。
如何快速從索引中檢索相應(yīng)的值
通常情況下,會選擇 B 樹或者 B+ 樹實現(xiàn)索引挟鸠,以獲得排序的能力叉信。
從 B+ 樹的數(shù)據(jù)結(jié)構(gòu)來看,?解決 ?Q2? 這個問題艘希,例如要檢索上個月購物記錄硼身,只要 logN 的時間就能查詢到相應(yīng)的記錄硅急。
?并且從上圖可以看到,B+ 樹的各個節(jié)點之間佳遂,都通過指針進(jìn)行連接营袜,從當(dāng)前指向的數(shù)據(jù)出發(fā),向后查找數(shù)據(jù)是非常方便和快速的丑罪。能完美的實現(xiàn)上拉加載更多這種需求荚板。
應(yīng)該如何建立復(fù)合索引
如果對本文開頭的表,建立一個復(fù)合索引吩屹,同時查詢購買時間跪另、購物折扣等,可以?這樣建立復(fù)合索引煤搜。
{ productid(升序)免绿,promotions(升序), dealdate(降序)}
這里擦盾,雖然對 productid 進(jìn)行排序沒有現(xiàn)實意義针姿,但是在計算機(jī)看來,依然是可以排序的厌衙。
在建立? B+ 樹時距淫,先比較 productid,再比較 promotions婶希,最后比較? dealdate榕暇,并且按照比較結(jié)果?生成一顆 B+ 樹。
那么查找的時候喻杈,如果檢索的內(nèi)容是:{productid}彤枢,索引是有效的。
如果?檢索的內(nèi)容是:{ productid(升序)筒饰、dealdate(降序)} 那么索引也是有效的缴啡,比較的時候,promotions 不比較(永遠(yuǎn)==)就可以了瓷们。
如果檢索的內(nèi)容是:{ productid(升序)业栅、 dealdate(降序)、promotions(升序)} 這時候谬晕,索引就無效了碘裕。因為此時的比較條件,與生存索引時的比較條件不一致(生成索引時攒钳,productid 相同的情況下帮孔,比較 promotions 的大小,此時 productid 相同的情況下不撑,比較 dealdate 的大小文兢。)
如果檢索的內(nèi)容是:{ productid(升序)晤斩、dealdate(升序)} 索引也是無效的。此時比較的條件姆坚,也與索引生成時不一致尸昧。
從上文就可以看出,建立數(shù)據(jù)庫索引時旷偿,就要考慮檢索條件才行