支撐百億級訪問的MongoDB線上實踐指南
文檔
文檔中的key禁止使用任何_以外的特殊字元
盡量將同樣類型的文檔存放在一個集合中,將不同類型的文檔分散在不同的集合中
相同類型的文檔能夠大幅度提高索引利用率,如果文檔混雜存放則可能會出現查詢經常需要全表掃描的情況
禁止使用_id,如:向_id中寫入自訂內容
【案例3】某業務的MongoDB在放量後出現嚴重的寫入效能問題,大致為:寫入達到300/s的時候IO跑滿,排查中發現,該業務在設計的時候為了方便, 而將_id中寫入了無序的類似md5的資料。MongoDB的表與InnoDB相似,都是索引組織表,資料內容跟在主鍵後,而_id是MongoDB中的預設主鍵,一旦_id的值為非自增,當資料量達到一定程度之後,每一次寫入都可能導致主鍵的二叉樹大幅度調整,這將是一個代價極大的寫入, 所以寫入就會隨著資料量的增大而下降,所以一定不要在_id中寫入自訂的內容。
盡量不要讓數組欄位成為查詢條件
【案例4】某業務在一個表的數組欄位上建立了一個索引,建立完畢之後發現表體積增大了很多很多,排查發現是由於索引體積的大幅度增大導致在MongoDB中,如果為一個數組欄位添加索引,那麼MongoDB會主動為這個數組中的所有元素依次添加獨立索引,例如: 為數組欄位{a:[x,y,z]}添加索引{a:1},實際上添加的索引為:
{a:[x:1]}
{a:[y:1]}
{a:[z:1]}
該業務的數組欄位中有11個元素,那麼等於一次建立了11條索引,這是索引體積大幅度增大的根本原因。 另外,如果複合式索引中存在數組欄位,那麼MongoDB會為每一個元素與其它欄位的組合建立一個獨立的索引,例如: 為數組欄位{a:[x,y,z]}和{b:qqq}添加索引{a:1,b:1},實際上添加的索引為:
{a:[x:1],b:1}
{a:[y:1],b:1}
{a:[z:1],b:1}
如果一個複合式索引中存在兩個數組欄位,那麼索引的數量將是兩個數組欄位中元素的笛卡兒積,所以MongoDB不允許索引中存在一個以上的數組欄位。
如果欄位較大,應盡量壓縮存放
【案例5】某業務上線後一直很正常,但在放量3倍之後發現MongoDB伺服器的網卡流量警示,IO壓力警示,排查中發現,該業務講一個超長的文字欄位存 放在MongoDB中,而這個欄位的平均體積達到了7K。在並發為2000QPS的情境下,每次取出1~20條資料,導致這個MongoDB每秒鐘要發送將 近100MB的資料,而對於資料庫而言,讀寫均為隨機IO,所以在如此大的資料吞吐情境中,IO達到了警示閾值。
由於文本是一個容易壓縮的樣本, 所以我們對該欄位進行了壓縮存放,使其平均體積降低到了2K,而解壓在業務端進行處理,最終將吞吐降低到了20MB/S左右。
如果欄位較大且會成為查詢條件,例如一長串的url,盡量轉成md5後存放
【案例6】某業務上線前進行壓力測試,測試中發現某個情境下的查詢效能不夠理想,排查中發現該情境的查詢條件類似:{url:xxxx},而url欄位中的值大部分都很長很長,該欄位的平均體積達到了0.5K,在這種情況下索引的體積會變得很大從而導致雖然請求雖然能夠走索引但效率並不夠理想,於是dba配合業務開發一起對該情境進行最佳化:
1.將該欄位的存放的內容由真實的url改為url內容md5後的值,欄位體積得到了大幅度縮小,固定在了32位
2.查詢時,使用者請求通過url查詢,而此時程式會將該url進行md5,然後用得到的值進行查詢,由於所以體積大幅度縮小,所以查詢速度有了極大的提高,最佳化完畢後再次進行壓力測試,效能達標,為之前的6倍。
由於MongoDB是大小寫敏感的,如果欄位無需大小寫敏感,為了提高查詢效率應盡量存放統一了大小寫後的資料,如:全部小寫或為該欄位增加一個統一了大小寫輔助欄位
【案例7】 某業務需要根據欄位{a:XxX}來進行查詢,在MongoDB中a的值是大小寫敏感的,並且無法配置為忽略大小寫,但該業務情境為了滿足查詢需求而需要忽略大小寫,這個大小寫敏感與否的矛盾導致業務需要使用正則來進行匹配:{a:/xxx/i},i參數在正則中表示忽略大小寫,上線後發現, 查詢效能非常低下,在一個擁有200萬文檔的集合中一次查詢需要消耗2.8~7秒,並發達到50QPS的時候MongoDB執行個體所在伺服器的CPU就跑到了973%。
MongoDB在查詢條件中使用正則的時候,能夠像普通精確匹配一樣使用索引達到高效率的查詢,但一旦使用了參數i來忽略大小寫查詢最佳化工具就需要對每一個資料的大小寫進行調整然後再進行匹配,此時這個請求就變成了全表掃描,這就是效率低下的根本原因。
對於這種情境可以採用建立一個統一了大小的欄位,例如全部小寫:假設原欄位為:{a:aAbB},那麼為其添加一個全部為小寫對應欄位:{a_low:aabb}然後通過欄位a_low進行查詢就能達到精確匹配,按照該方案改進後,該情境的查詢耗時降低到了2毫秒 雖然新增欄位導致執行個體會變大一些,但對於換來效能的大幅度提升還是非常值得的。
不要存放太長的字串,如果這個欄位為查詢條件,那麼確保該欄位的值不超過1KB
MongoDB的索引僅支援1K以內的欄位,如果你存入的資料長度超過1K,那麼它將無法被索引