您好,登錄后才能下訂單哦!
MongoDB設(shè)計命名規(guī)范有哪些,針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
1. 庫
1. 庫名全部小寫,禁止使用任何`_`以外的特殊字符,禁止使用數(shù)字打頭的庫名,如:`123_abc`;
2. 庫以文件夾的形式存在,使用特殊字符或其它不規(guī)范的命名方式會導(dǎo)致命名混亂;
3. 數(shù)據(jù)庫名最多為64字符;
4. 在創(chuàng)建新的庫前應(yīng)盡量評估該庫的體積、QPS等,提前與DBA討論是應(yīng)該新建一個庫還是專門為該庫創(chuàng)建一個新的集群;
某開發(fā)在拿到DBA提供的MongoDB后由于MongoDB的權(quán)限控制比較寬松,導(dǎo)致該業(yè)務(wù)的開發(fā)在創(chuàng)建集合的時候懶得與DBA討論,而是隨意的將所有集合都創(chuàng)建在一個庫中,最初并沒有什么問題,因為業(yè)務(wù)的請求量并不大。半年后,該業(yè)務(wù)增長到了一個比較大的量級,而此時開發(fā)人員上線了一個新的項目,該項目的寫入量很大,大部分都為批量更新,由于所有集合都存放在一個庫中,這個新項目的批量更新帶來了頻繁的鎖、I/O平均等。最后開發(fā)配合DBA一起將該庫拆散到了多個新的庫中,將一庫N集合轉(zhuǎn)換為單庫單集合,性能問題迎刃而解。
2. 集合
1. 集合名全部小寫,禁止使用任何`_`以外的特殊字符,禁止使用數(shù)字打頭的集合名,如:`123_abc`,禁止system打頭; system是系統(tǒng)集合前綴;
2. 集合名稱最多為64字符;
3. 一個庫中寫入較大的集合會影響其它集合的讀寫性能,如果業(yè)務(wù)比較繁華的集合在一個DB中,建議最多80個集合,同時也要考慮磁盤I/O的性能;
4. 如果評估單集合數(shù)據(jù)量較大,可以將一個大表拆分為多個小表,然后將每一個小表存放在獨立的庫中或者sharding分表;
5. MongoDB的集合擁有“自動清理過期數(shù)據(jù)”的功能,只需在該集合中文檔的時間字段增加一個TTL索引即可實現(xiàn)該功能,但需要注意的是該字段的類型則必須是mongoDate(),一定要結(jié)合實際業(yè)務(wù)設(shè)計是否需要;
6. 設(shè)計輪詢集合---集合是否設(shè)計為Capped限制集,一定要結(jié)合實際業(yè)務(wù)設(shè)計是否需要;
7. 創(chuàng)建集合規(guī)則
不同的業(yè)務(wù)場景是可以配置進行不同的配置;
a. 如果是讀多寫少的表在創(chuàng)建時我們可以盡量將 page size 設(shè)置的比較小,比如 16KB,如果表數(shù)據(jù)量不太大(
"internal_page_max=16KB,leaf_page_max=16KB,leaf_value_max=8KB,os_cache_max=1GB"
b. 如果這個讀多寫少的表數(shù)據(jù)量比較大,可以為其設(shè)置一個壓縮算法,例如:
"block_compressor=zlib,internal_page_max=16KB,leaf_page_max=16KB,leaf_value_max=8KB"
c. 注意:該zlib壓縮算法不要使用,對cpu消耗特別大,如果使用snapp消耗20% cpu,而且使用zlib能消耗90%cpu,甚至100%
d. 如果是寫多讀少的表,可以將 leaf_page_max 設(shè)置到 1MB,并開啟壓縮算法,也可以為其制定操作系統(tǒng)層面 page cache 大小的os_cache_max 值,讓它不會占用太多的 page cache 內(nèi)存,防止影響讀操作;
e. 案例
db.createCollection(
"logs",
{ storageEngine: { wiredTiger: {configString: "internal_page_max=16KB,leaf_page_max=16KB,leaf_value_max=8KB,os_cache_max=1GB" } } }
)
f. 說明
讀多寫少的表
internal_page_max=16KB 默認為4KB
leaf_page_max=16KB 默認為32KB
leaf_value_max=8KB 默認為64MB
os_cache_max=1GB 默認為0
讀多寫少的表 而且數(shù)據(jù)量比較大
block_compressor=zlib 默認為snappy
internal_page_max=16KB 默認為4KB
leaf_page_max=16KB 默認為32KB
leaf_value_max=8KB 默認為64MB
3. 文檔
1. 文檔中的key禁止使用任何`_`以外的特殊字符;
2. 盡量將同樣類型的文檔存放在一個集合中,將不同類型的文檔分散在不同的集合中;相同類型的文檔能夠大幅度提高索引利用率,如果文檔混雜存放則可能會出現(xiàn)查詢經(jīng)常需要全表掃描的情況;
3. 禁止使用_id,如:向_id中寫入自定義內(nèi)容;
某業(yè)務(wù)的MongoDB在放量后出現(xiàn)嚴重的寫入性能問題,大致為:寫入達到300/s的時候IO跑滿,排查中發(fā)現(xiàn),該業(yè)務(wù)在設(shè)計的時候為了方便, 而將_id中寫入了無序的類似md5的數(shù)據(jù)。MongoDB的表與InnoDB相似,都是索引組織表,數(shù)據(jù)內(nèi)容跟在主鍵后,而_id是MongoDB中的默認主鍵,一旦_id的值為非自增,當(dāng)數(shù)據(jù)量達到一定程度之后,每一次寫入都可能導(dǎo)致主鍵的二叉樹大幅度調(diào)整,這將是一個代價極大的寫入, 所以寫入就會隨著數(shù)據(jù)量的增大而下降,所以一定不要在_id中寫入自定義的內(nèi)容。
4. 盡量不要讓數(shù)組字段成為查詢條件
某業(yè)務(wù)在一個表的數(shù)組字段上創(chuàng)建了一個索引,創(chuàng)建完畢之后發(fā)現(xiàn)表體積增大了很多很多,排查發(fā)現(xiàn)是由于索引體積的大幅度增大導(dǎo)致在MongoDB中,如果為一個數(shù)組字段添加索引,那么MongoDB會主動為這個數(shù)組中的所有元素依次添加獨立索引,例如: 為數(shù)組字段{a:[x,y,z]}添加索引{a:1},實際上添加的索引為:
{a:[x:1]}
{a:[y:1]}
{a:[z:1]}
該業(yè)務(wù)的數(shù)組字段中有11個元素,那么等于一次創(chuàng)建了11條索引,這是索引體積大幅度增大的根本原因。另外,如果組合索引中存在數(shù)組字段,那么MongoDB會為每一個元素與其它字段的組合創(chuàng)建一個獨立的索引,例如: 為數(shù)組字段{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}
如果一個組合索引中存在兩個數(shù)組字段,那么索引的數(shù)量將是兩個數(shù)組字段中元素的笛卡兒積,所以MongoDB不允許索引中存在一個以上的數(shù)組字段。
5. 如果字段較大,應(yīng)盡量壓縮存放
某業(yè)務(wù)上線后一直很正常,但在放量3倍之后發(fā)現(xiàn)MongoDB服務(wù)器的網(wǎng)卡流量報警,IO壓力報警,排查中發(fā)現(xiàn),該業(yè)務(wù)講一個超長的文本字段存放在MongoDB中,而這個字段的平均體積達到了7K。在并發(fā)為2000QPS的場景下,每次取出1~20條數(shù)據(jù),導(dǎo)致這個MongoDB每秒鐘要發(fā)送將近100MB的數(shù)據(jù),而對于數(shù)據(jù)庫而言,讀寫均為隨機IO,所以在如此大的數(shù)據(jù)吞吐場景中,IO達到了報警閾值。
由于文本是一個容易壓縮的樣本, 所以我們對該字段進行了壓縮存放,使其平均體積降低到了2K,而解壓在業(yè)務(wù)端進行處理,最終將吞吐降低到了20MB/S左右。
如果字段較大且會成為查詢條件,例如一長串的url,盡量轉(zhuǎn)成md5后存放
某業(yè)務(wù)上線前進行壓力測試,測試中發(fā)現(xiàn)某個場景下的查詢性能不夠理想,排查中發(fā)現(xiàn)該場景的查詢條件類似:{url:xxxx},而url字段中的值大部分都很長很長,該字段的平均體積達到了0.5K,在這種情況下索引的體積會變得很大從而導(dǎo)致雖然請求雖然能夠走索引但效率并不夠理想,于是dba配合業(yè)務(wù)開發(fā)一起對該場景進行優(yōu)化:
1.將該字段的存放的內(nèi)容由真實的url改為url內(nèi)容md5后的值,字段體積得到了大幅度縮小,固定在了32位
2.查詢時,用戶請求通過url查詢,而此時程序會將該url進行md5,然后用得到的值進行查詢,由于所以體積大幅度縮小,所以查詢速度有了極大的提高,優(yōu)化完畢后再次進行壓力測試,性能達標,為之前的6倍。
6. 由于MongoDB是大小寫敏感的,如果字段無需大小寫敏感,為了提高查詢效率應(yīng)盡量存放統(tǒng)一了大小寫后的數(shù)據(jù),如:全部小寫或為該字段增加一個統(tǒng)一了大小寫的輔助字段;
某業(yè)務(wù)需要根據(jù)字段{a:XxX}來進行查詢,在MongoDB中a的值是大小寫敏感的,并且無法配置為忽略大小寫,但該業(yè)務(wù)場景為了滿足查詢需求而需要忽略大小寫,這個大小寫敏感與否的矛盾導(dǎo)致業(yè)務(wù)需要使用正則來進行匹配:{a:/xxx/i},i參數(shù)在正則中表示忽略大小寫,上線后發(fā)現(xiàn), 查詢性能非常低下,在一個擁有200萬文檔的集合中一次查詢需要消耗2.8~7秒,并發(fā)達到50QPS的時候MongoDB實例所在服務(wù)器的CPU就跑到了973%。
MongoDB在查詢條件中使用正則的時候,能夠像普通精確匹配一樣使用索引達到高效率的查詢,但一旦使用了參數(shù)i來忽略大小寫查詢優(yōu)化器就需要對每一個數(shù)據(jù)的大小寫進行調(diào)整然后再進行匹配,此時這個請求就變成了全表掃描,這就是效率低下的根本原因。
對于這種場景可以采用新建一個統(tǒng)一了大小的字段,例如全部小寫:假設(shè)原字段為:{a:aAbB},那么為其添加一個全部為小寫的對應(yīng)字段:{a_low:aabb}然后通過字段a_low進行查詢就能達到精確匹配,按照該方案改進后,該場景的查詢耗時降低到了2毫秒雖然新增字段導(dǎo)致實例會變大一些,但對于換來性能的大幅度提升還是非常值得的。
7. 不要存放太長的字符串,如果這個字段為查詢條件,那么確保該字段的值不超過1KB;
8. MongoDB的索引僅支持1K以內(nèi)的字段,如果你存入的數(shù)據(jù)長度超過1K,那么它將無法被索引;
4. 索引
1. MongoDB 的組合索引使用策略與 MySQL 一致,遵循“最左原則”;
2. 索引名稱長度不要超過128字符;
3. 應(yīng)盡量綜合評估查詢場景,通過評估盡可能的將單列索引并入組合索引以降低所以數(shù)量,結(jié)合1,2點;
MongoDB的組合索引規(guī)則和MySQL一樣,都遵循最左原理,假設(shè)一個組合索引為:{a:1,b:1,c:1},那么以下條件的查詢是可以被用到的:
{a:1}
{a:1,b:2}
{a:1,b:2,c:3}
{}
以下條件的查詢是不能用到索引的:
{b:1}
{b:1:c:2}
{c:2}
另外在設(shè)計索引的時候可以通過該原理減少索引的數(shù)目,如果需要通過{a:xxx}或{a:xxx,b:xxx}來進行查詢,那么創(chuàng)建索引:
{a:1,b:1}
即可同時滿足這兩個查詢場景而無需再單獨創(chuàng)建{a:1}索引。
4. 在創(chuàng)建組合索引的時候,應(yīng)評估索引中包含的字段,盡量將數(shù)據(jù)基數(shù)大(唯一值多的數(shù)據(jù))的字段放在組合索引的前面;
某業(yè)務(wù)某場景下的查詢十分緩慢,大概需要1.7秒左右,需要進行調(diào)優(yōu),該場景的查詢和對應(yīng)索引如下:
查詢:{name:baidu,status:0}
索引:{status:1,name:1}
乍一看沒什么問題,因為查詢和索引十分匹配,但對該集合分析后發(fā)現(xiàn)該集合一共有150萬文檔,而status=0的有1499930由于這基本上占了99%的文檔數(shù)目(數(shù)據(jù)基數(shù)很小),所以導(dǎo)致雖然使用了索引,但依然需要從149萬行數(shù)據(jù)中找到name=baidu的數(shù)據(jù)但name字段則有大量不同的數(shù)據(jù)(數(shù)據(jù)基數(shù)很大),所以如果將該組合索引調(diào)整為name在前,該查詢即可先通過name字段抽出較少的數(shù)據(jù),再通過status進行過濾,就快了:
{name:1.status:1}調(diào)整后查詢耗時降低到3~5毫秒。
5. 在數(shù)據(jù)量較大的時候,MongoDB 索引的創(chuàng)建是一個緩慢的過程,所以應(yīng)當(dāng)在上前線或數(shù)據(jù)量變得很大前盡量評估,按需創(chuàng)建會用到的索引;
6. MongoDB 支持 TTL 索引,該索引能夠按你的需要自動刪除XXX秒之前的數(shù)據(jù)并會盡量選擇在業(yè)務(wù)低峰期執(zhí)行刪除操作;看業(yè)務(wù)是否需要這一類型索引;
7. 如果你存放的數(shù)據(jù)是地理位置信息,比如:經(jīng)緯度數(shù)據(jù)。那么可以在該字段上添加 MongoDB 支持的地理索引:2d 及2dsphere,但他們是不同的,混用會導(dǎo)致結(jié)果不準確;
2d:只能用于點對點索引,適用于平面地圖和時間連續(xù)的數(shù)據(jù),比如非常適用于游戲地圖【2dsphere:允許指定點、線和多邊形。適用于地球表面類型的地圖(球體) 】如果在球體表面創(chuàng)建2d索引,則會導(dǎo)致極點附近出現(xiàn)大量扭曲變形,最終導(dǎo)致結(jié)果不準確;
8. MongoDB 的全文索引目前仍然處于“實驗”階段,性能并不夠理想,當(dāng)前不建議使用;
9. 從 MongoDB2.4開始,支持索引的 ICP 功能,可以通過其合理減少索引數(shù)量;
從 MongoDB2.4開始,組合索引能夠被更有效的利用,如:
索引{x:1,y:1,z:1}可以被查詢{x:1,z:1}所利用如果x字段的數(shù)據(jù)基數(shù)很大,而該條件匹配到的數(shù)據(jù)有很少,在這種情況下無需專門添加{x:1,z:1}索引,索引{x:1,y:1,z:1}即可帶來理想的性能但需要注意的是,ICP 性能并沒有原生的連續(xù)的組合索引效率好,如果發(fā)現(xiàn)效率不佳那么還是需要添加單獨的{x:1,z:1}索引;
10. 創(chuàng)建索引要在后臺創(chuàng)建,避免阻塞業(yè)務(wù)正常DML和查詢
db.works.createIndex({plan:1,trainingpoints:1,cmsOrder:1,stateValue:1},{background:true})
a. 添加唯一索引
db.bodys.createIndex({user:1,date:-1},{unique:true,background:true}) 唯一索引 3.2以下必須這樣加唯一索引;
b. 添加帶有數(shù)組好其他列的索引
db.antis.createIndex({"action.last":1,refe_type:1,_id:1},{background:true})
其中action.last是數(shù)組
c. TTL索引,字段create_date,180天后自動清理數(shù)據(jù)
db.orders.createIndex({"create_date":1},{"expireAfterSeconds":15552000})
d. 案例說明
創(chuàng)建位置和狀態(tài)索引,為了能快速處理“某地未處理訂單”查詢,這是一個多條件的查詢,所以是一個復(fù)合索引,
status字段放在前面,因為多數(shù)的查詢都會依賴狀態(tài)字段
db.order.createIndex({"status":1, "delivery.city":1,"delivery.address":1})
在這個Demo里,還有一種加快查詢速度的方法就是,創(chuàng)建一個只包含指定狀態(tài)的一個Partial Indexes索引。
比如status必須為delivering 才加入到索引中,有效控制索引的大小,加快查詢速度。
db.order.createIndex({"delivery.city":1,"delivery.address":1},{partialFilterExpression:{'status':{$eq:"delivering"}}})
e.
11. 創(chuàng)建索引建議:先做等值查詢,在做排序,在做范圍查詢。
5. 實踐操作性能
1. 索引中的-1和1是不一樣的,一個是逆序,一個是正序,應(yīng)當(dāng)根據(jù)自己的業(yè)務(wù)場景建立適合的索引排序,需要注意的是{a:1,b:-1} 和 {a:-1,b:1}是一樣的;
2. 在開發(fā)業(yè)務(wù)的時候盡量檢查自己的程序性能,可以使用explain() 函數(shù)檢查你的查詢執(zhí)行詳情,另外 hint() 函數(shù)相當(dāng)于 MySQL 中的force index();
3. 查詢中的某些 $ 操作符可能會導(dǎo)致性能低下,如 $ne,$not,$exists,$nin,$or,盡量在業(yè)務(wù)中不要使用
a. $exist:因為松散的文檔結(jié)構(gòu)導(dǎo)致查詢必須遍歷每一個文檔
b. $ne:如果當(dāng)取反的值為大多數(shù),則會掃描整個索引
c. $not:可能會導(dǎo)致查詢優(yōu)化器不知道應(yīng)當(dāng)使用哪個索引,所以會經(jīng)常退化為全表掃描
d. $nin:全表掃描
e. $or:有多少個條件就會查詢多少次,最后合并結(jié)果集,所以盡可能的使用 $in
4. 如果你結(jié)合體積大小/文檔數(shù)固定,那么建議創(chuàng)建 capped(封頂)集合,這種集合的寫入性能非常高并無需專門清理老舊數(shù)據(jù),需要注意的是 capped 表不支持r emove() 和update();
5. 在寫入數(shù)據(jù)的時候,如果你需要實現(xiàn)類似 MySQL 中INSERT INTO ON DUPLICATE KEY UPDATE 的功能,那么可以選擇 upsert() 函數(shù);
db.analytice.update(
{"url":"/blog"},
{"$inc":{"visits":1}},
true
)
第3個參數(shù)表示,這是upsert
6. 不要一次取出太多的數(shù)據(jù)進行排序,MongoDB 目前支持對32MB以內(nèi)的結(jié)果集進行排序,如果需要排序,那么請盡量限制結(jié)果集中的數(shù)據(jù)量;
7. MongoDB 的聚合框架非常好用,能夠通過簡單的語法實現(xiàn)復(fù)雜的統(tǒng)計查詢,并且性能也不錯;
8. 如果需要清理掉一個集合中的所有數(shù)據(jù),那么 remove() 的性能是非常低下的,該場景下應(yīng)當(dāng)使用 drop();
remove() 是逐行操作,所以在刪除大量數(shù)據(jù)的時候性能很差;
9. 寫入大量數(shù)據(jù)的時候可以選擇使用 batchInsert,但目前MongoDB 每一次能夠接受的最大消息長度為48MB,如果超出48MB,將會被自動拆分為多個48MB的消息;
10. 在使用數(shù)組字段做為查詢條件的時候,將于覆蓋索引無緣;
這是因為數(shù)組是保存在索引中的,即便將數(shù)組字段從需要返回的字段中剔除,這樣的索引仍然無法覆蓋查詢;
11. 在查詢中如果有范圍條件,那么盡量和定值條件放在一起進行過濾,并在創(chuàng)建索引的時候?qū)⒍ㄖ挡樵冏侄畏旁诜秶樵冏侄吻?
關(guān)于MongoDB設(shè)計命名規(guī)范有哪些問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。