MongoDB基礎入門到深入(七)建模、調優

文章目錄

  • 系列文章索引
  • 十一、MongoDB開發規范
  • 十二、MongoDB調優
    • 1、三大導致MongoDB性能不佳的原因
    • 2、影響MongoDB性能的因素
    • 3、MongoDB性能監控工具
      • (1)mongostat
      • (2)mongotop
      • (3)Profiler模塊
      • (4)查看操作日志
      • (5)db.currentOp()
      • (6)性能問題排查案例
  • 十三、建模案例分析
    • 1、建模案例分析1
      • (1)需求
      • (2)建模 - 不好的設計
      • (3)建模 - 推薦的設計
    • 2、建模案例分析2:多列數據結構
      • (1)需求
      • (2)建模 - 不好的模型設計
      • (3)建模 - 一般的設計
      • (4)建模 - 合理的設計
    • 3、建模案例分析3 - 物聯網時序數據庫建模
      • (1)需求
      • (2)建模
      • (3)總結
  • 十四、MongoDB高級集群架構
    • 1、兩地三中心集群架構
    • 2、全球多寫集群架構

系列文章索引

MongoDB基礎入門到深入(一)安裝、文檔操作
MongoDB基礎入門到深入(二)聚合高級操作
MongoDB基礎入門到深入(三)索引高級操作
MongoDB基礎入門到深入(四)復制(副本)集
MongoDB基礎入門到深入(五)分片集群
MongoDB基礎入門到深入(六)多文檔事務
MongoDB基礎入門到深入(七)建模、調優
MongoDB基礎入門到深入(八)MongoDB整合SpringBoot、Chang Streams

十一、MongoDB開發規范

(1)命名原則。數據庫、集合命名需要簡單易懂,數據庫名使用小寫字符,集合名稱使用統一命名風格,可以統一大小寫或使用駝峰式命名。數據庫名和集合名稱均不能超過64個字符。
(2)集合設計。對少量數據的包含關系,使用嵌套模式有利于讀性能和保證原子性的寫入。對于復雜的關聯關系,以及后期可能發生演進變化的情況,建議使用引用模式。
(3)文檔設計。避免使用大文檔,MongoDB的文檔最大不能超過16MB。如果使用了內嵌的數組對象或子文檔,應該保證內嵌數據不會無限制地增長。在文檔結構上,盡可能減少字段名的長度,MongoDB會保存文檔中的字段名,因此字段名稱會影響整個集合的大小以及內存的需求。一般建議將字段名稱控制在32個字符以內。
(4)索引設計。在必要時使用索引加速查詢。避免建立過多的索引,單個集合建議不超過10個索引。MongoDB對集合的寫入操作很可能也會觸發索引的寫入,從而觸發更多的I/O操作。無效的索引會導致內存空間的浪費,因此有必要對索引進行審視,及時清理不使用或不合理的索引。遵循索引優化原則,如覆蓋索引、優先前綴匹配等,使用explain命令分析索引性能。
(5)分片設計。對可能出現快速增長或讀寫壓力較大的業務表考慮分片。分片鍵的設計滿足均衡分布的目標,業務上盡量避免廣播查詢。應盡早確定分片策略,最好在集合達到256GB之前就進行分片。如果集合中存在唯一性索引,則應該確保該索引覆蓋分片鍵,避免沖突。為了降低風險,單個分片的數據集合大小建議不超過2TB
(6)升級設計。應用上需支持對舊版本數據的兼容性,在添加唯一性約束索引之前,對數據表進行檢查并及時清理冗余的數據。新增、修改數據庫對象等操作需要經過評審,并保持對數據字典進行更新。
(7)考慮數據老化問題,要及時清理無效、過期的數據,優先考慮為系統日志、歷史數據表添加合理的老化策略。
(8)數據一致性方面,非關鍵業務使用默認的WriteConcern:1(更高性能寫入);對于關鍵業務類,使用WriteConcern:majority保證一致性(性能下降)。如果業務上嚴格不允許臟讀,則使用ReadConcern:majority選項。
(9)使用update、findAndModify對數據進行修改時,如果設置了upsert:true,則必須使用唯一性索引避免產生重復數據。
(10)業務上盡量避免短連接,使用官方最新驅動的連接池實現,控制客戶端連接池的大小,最大值建議不超過200。
(11)對大量數據寫入使用Bulk Write批量化API,建議使用無序批次更新。
(12)優先使用單文檔事務保證原子性,如果需要使用多文檔事務,則必須保證事務盡可能小,一個事務的執行時間最長不能超過60s。
(13)在條件允許的情況下,利用讀寫分離降低主節點壓力。對于一些統計分析類的查詢操作,可優先從節點上執行。
(14)考慮業務數據的隔離,例如將配置數據、歷史數據存放到不同的數據庫中。微服務之間使用單獨的數據庫,盡量避免跨庫訪問。
(15)維護數據字典文檔并保持更新,提前按不同的業務進行數據容量的規劃。

十二、MongoDB調優

1、三大導致MongoDB性能不佳的原因

1. 慢查詢
2. 阻塞等待
3. 硬件資源不足
1,2通常是因為模型/索引設計不佳導致的
排查思路:按1-2-3依次排查

2、影響MongoDB性能的因素

https://www.mongodb.com/docs/v4.4/tutorial/model-embedded-one-to-one-relationships-between-documents/
https://www.mongodb.com/docs/v4.4/reference/connection-string/
https://www.mongodb.com/docs/manual/administration/production-notes/#hardware-considerations
在這里插入圖片描述

3、MongoDB性能監控工具

(1)mongostat

mongostat是MongoDB自帶的監控工具,其可以提供數據庫節點或者整個集群當前的狀態視圖。該功能的設計非常類似于Linux系統中的vmstat命令,可以呈現出實時的狀態變化。不同的是,mongostat所監視的對象是數據庫進程。mongostat常用于查看當前的QPS/內存使用/連接數,以及多個分片的壓力分布。mongostat采用Go語言實現,其內部使用了db.serverStatus()命令,要求執行用戶需具備clusterMonitor角色權限。

mongostat -h 192.168.56.10 --port 28017 -uroot -proot --authenticationDatabase=admin --discover -n 300 2

參數說明:
-h:指定監聽的主機,分片集群模式下指定到一個mongos實例,也可以指定單個mongod,或者復制集的多個節點。
–port:接入的端口,如果不提供則默認為27017。
-u:接入用戶名,等同于-user。
-p:接入密碼,等同于-password。
–authenticationDatabase:鑒權數據庫。
–discover:啟用自動發現,可展示集群中所有分片節點的狀態。
-n 300 2:表示輸出300次,每次間隔2s。也可以不指定“-n 300”,此時會一直保持輸出。
在這里插入圖片描述
指標說明
在這里插入圖片描述
mongostat需要關注的指標主要有如下幾個:
插入、刪除、修改、查詢的速率是否產生較大波動,是否超出預期。
qrw、arw:隊列是否較高,若長時間大于0則說明此時讀寫速度較慢。
conn:連接數是否太多。
dirty:百分比是否較高,若持續高于10%則說明磁盤I/O存在瓶頸。
netIn、netOut:是否超過網絡帶寬閾值。
repl:狀態是否異常,如PRI、SEC、RTR為正常,若出現REC等異常值則需要修復。

使用交互模式
mongostat一般采用滾動式輸出,即每一個間隔后的狀態數據會被追加到控制臺中。從MongoDB 3.4開始增加了--interactive選項,用來實現非滾動式的監視,非常方便。

mongostat -h 192.168.56.10 --port 28017 -uroot -proot --authenticationDatabase=admin --discover --interactive -n 2

在這里插入圖片描述

(2)mongotop

mongotop命令可用于查看數據庫的熱點表,通過觀察mongotop的輸出,可以判定是哪些集合占用了大部分讀寫時間。mongotop與mongostat的實現原理類似,同樣需要clusterMonitor角色權限。

mongotop -h 192.168.56.10 --port=28017 -uroot -proot --authenticationDatabase=admin

默認情況下,mongotop會持續地每秒輸出當前的熱點表
在這里插入圖片描述
指標說明

在這里插入圖片描述
mongotop通常需要關注的因素主要包括:
熱點表操作耗費時長是否過高。這里的時長是在一定的時間間隔內的統計值,它代表某個集合讀寫操作所耗費的時間總量。在業務高峰期時,核心表的讀寫操作一般比平時高一些,通過mongotop的輸出可以對業務尖峰做出一些判斷。
是否存在非預期的熱點表。一些慢操作導致的性能問題可以從mongotop的結果中體現出來

#mongotop的統計周期、輸出總量都是可以設定的
#最多輸出100次,每次間隔時間為2s
mongotop -h 192.168.56.10 --port=28017 -uroot -proot --authenticationDatabase=admin -n 100 2

在這里插入圖片描述

(3)Profiler模塊

Profiler模塊可以用來記錄、分析MongoDB的詳細操作日志。默認情況下該功能是關閉的,對某個業務庫開啟Profiler模塊之后,符合條件的慢操作日志會被寫入該庫的system.profile集合中。Profiler的設計很像代碼的日志功能,其提供了幾種調試級別:
在這里插入圖片描述
對當前的數據庫開啟Profiler模塊:

# 將level設置為2,此時所有的操作會被記錄下來。
db.setProfilingLevel(2)
#檢查是否生效
db.getProfilingStatus()

在這里插入圖片描述
slowms是慢操作的閾值,單位是毫秒;
sampleRate表示日志隨機采樣的比例,1.0則表示滿足條件的全部輸出。

# 如果希望只記錄時長超過500ms的操作,則可以將level設置為1
db.setProfilingLevel(1,500)
# 還可以進一步設置隨機采樣的比例
db.setProfilingLevel(1,{slowms:500,sampleRate:0.5})

(4)查看操作日志

# 開啟Profiler模塊之后,可以通過system.profile集合查看最近發生的操作日志
db.system.profile.find().limit(5).sort({ts:-1}).pretty()

在這里插入圖片描述

這里需要關注的一些字段主要如下所示:
op:操作類型,描述增加、刪除、修改、查詢。
ns:名稱空間,格式為{db}.{collection}。
Command:原始的命令文檔。
Cursorid:游標ID。
numYield:操作數,大于0表示等待鎖或者是磁盤I/O操作。
nreturned:返回條目數。
keysExamined:掃描索引條目數,如果比nreturned大出很多,則說明查詢效率不高。docsExamined:掃描文檔條目數,如果比nreturned大出很多,則說明查詢效率不高。
locks:鎖占用的情況。
storage:存儲引擎層的執行信息。
responseLength:響應數據大小(字節數),一次性查詢太多的數據會影響性能,可以使用limit、batchSize進行一些限制。
millis:命令執行的時長,單位是毫秒。
planSummary:查詢計劃的概要,如IXSCAN表示使用了索引掃描。
execStats:執行過程統計信息。
ts:命令執行的時間點。

根據這些字段,可以執行一些不同維度的查詢。比如查看執行時長最大的10條操作記錄

#查看某個集合中的update操作日志
db.system.profile.find().limit(10).sort({millis:-1}).pretty()
#查看某個集合中的update操作日志
db.system.profile.find({op:"update",ns:"shop.user"})

注意事項
system.profile是一個1MB的固定大小的集合,隨著記錄日志的增多,一些舊的記錄會被滾動刪除。
在線上開啟Profiler模塊需要非常謹慎,這是因為其對MongoDB的性能影響比較大。建議按需部分開啟,同時slowms的值不要設置太低
sampleRate的默認值是1.0,該字段可以控制記錄日志的命令數比例,但只有在MongoDB 4.0版本之后才支持。
Profiler模塊的設置是內存級的,重啟服務器后會自動恢復默認狀態

(5)db.currentOp()

Profiler模塊所記錄的日志都是已經發生的事情,db.currentOp()命令則與此相反,它可以用來查看數據庫當前正在執行的一些操作。想象一下,當數據庫系統的CPU發生驟增時,我們最想做的無非是快速找到問題的根源,這時db.currentOp就派上用場了。
db.currentOp()讀取的是當前數據庫的命令快照,該命令可以返回許多有用的信息,比如:
操作的運行時長,快速發現耗時漫長的低效掃描操作。
執行計劃信息,用于判斷是否命中了索引,或者存在鎖沖突的情況。
操作ID、時間、客戶端等信息,方便定位出產生慢操作的源頭。

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
對示例操作的解讀如下:
(1)從ns、op字段獲知,當前進行的操作正在對test.items集合執行update命令。
(2)command字段顯示了其原始信息。其中,command.q和command.u分別展示了update的查詢條件和更新操作。
(3)“planSummary”:“COLLSCAN” 說明情況并不樂觀,update沒有利用索引而是正在全表掃描。
(4)microsecs_running:NumberLong(186070)表示操作運行了186ms,注意這里的單位是微秒。

優化方向:
value字段加上索引
如果更新的數據集非常大,要避免大范圍update操作,切分成小批量的操作

#opid表示當前操作在數據庫進程中的唯一編號。如果已經發現該操作正在導致數據庫系統響應緩慢,則可以考慮將其“殺”死
db.killOp(4001)

db.currentOp默認輸出當前系統中全部活躍的操作,由于返回的結果較多,我們可以指定一些過濾條件:

# 查看等待鎖的增加、刪除、修改、查詢操作
db.currentOp({waitingForLock:true,$or:[{op:{$in:["insert","update","remove"]}},{"query.findandmodify":{$exists:true}}]
})#查看執行時間超過1s的操作
db.currentOp({secs_running:{$gt:1}
})
#查看test數據庫中的操作
db.currentOp({ns:/test/
})

currentOp命令輸出說明
currentOp.type:操作類型,可以是op、idleSession、idleCursor的一種,一般的操作信息以op表示。其為MongoDB 4.2版本新增功能。
currentOp.host:主機的名稱。currentOp.desc:連接描述,包含connectionId。currentOp.connectionId:客戶端連接的標識符。currentOp.client:客戶端主機和端口。currentOp.appName:應用名稱,一般是描述客戶端類型。
currentOp.clientMetadata:關于客戶端的附加信息,可以包含驅動的版本。currentOp.currentOpTime:操作的開始時間。MongoDB 3.6版本新增功能。
currentOp.lsid:會話標識符。MongoDB 3.6版本新增功能。
currentOp.opid:操作的標志編號。
currentOp.active:操作是否活躍。如果是空閑狀態則為false。
currentOp.secs_running:操作持續時間(以秒為單位)。
currentOp.microsecs_running:操作持續時間(以微秒為單位)。
currentOp.op:標識操作類型的字符串。可能的值是:“none” “update” “insert”“query”“command” “getmore” “remove” “killcursors”。其中,command操作包括大多數命令,如createIndexes和findAndModify。
currentOp.ns:操作目標的集合命名空間。
currentOp.command:操作的完整命令對象的文檔。如果文檔大小超過1KB,則會使用一種$truncate形式表示。
currentOp.planSummary:查詢計劃的概要信息。
currentOp.locks:當前操作持有鎖的類型和模式。
currentOp.waitingForLock:是否正在等待鎖。
currentOp.numYields:當前操作執行yield(讓步)的次數。一些鎖互斥或者磁盤I/O讀取都會導致該值大于0。
currentOp.lockStats:當前操作持有鎖的統計。
currentOp.lockStats.acquireCount:操作以指定模式獲取鎖的次數。
currentOp.lockStats.acquireWaitCount:操作獲取鎖等待的次數,等待是因為鎖處于沖突模式。acquireWaitCount小于或等于acquireCount。
currentOp.lockStats.timeAcquiringMicros:操作為了獲取鎖所花費的累積時間(以微秒為單位)。timeAcquiringMicros除以acquireWaitCount可估算出平均鎖等待時間。
currentOp.lockStats.deadlockCount:在等待鎖獲取時,操作遇到死鎖的次數。

注意事項
db.currentOp返回的是數據庫命令的瞬時狀態,因此,如果數據庫壓力不大,則通常只會返回極少的結果。
如果啟用了復制集,那么currentOp還會返回一些復制的內部操作(針對local.oplog.rs),需要做一些篩選。
db.currentOp的結果是一個BSON文檔,如果大小超過16MB,則會被壓縮。可以使用聚合操作$currentOp獲得完整的結果。

(6)性能問題排查案例

記一次 MongoDB 占用 CPU 過高問題的排查
MongoDB線上案例:一個參數提升16倍寫入速度

十三、建模案例分析

1、建模案例分析1

(1)需求

朋友圈評論內容管理

社交類的APP需求,一般都會引入“朋友圈”功能,這個產品特性有一個非常重要的功能就是評論體系
先整理下需求:
這個APP希望點贊和評論信息都要包含頭像信息:
點贊列表,點贊用戶的昵稱,頭像;
評論列表,評論用戶的昵稱,頭像;

數據查詢則相對簡單:
根據分享ID,批量的查詢出10條分享里的所有評論內容;

(2)建模 - 不好的設計

跟據上面的內容,先來一個非常非常"樸素"的設計:

{"_id": 41,"username": "小白","uid": "100000","headurl": "http://xxx.yyy.cnd.com/123456ABCDE","praise_list": ["100010","100011","100012"],"praise_ref_obj": {"100010": {"username": "小一","headurl": "http://xxx.yyy.cnd.com/8087041AAA","uid": "100010"},"100011": {"username": "mayun","headurl": "http://xxx.yyy.cnd.com/8087041AAB","uid": "100011"},"100012": {"username": "penglei","headurl": "http://xxx.yyy.cnd.com/809999041AAA","uid": "100012"}},"comment_list": ["100013","100014"],"comment_ref_obj": {"100013": {"username": "小二","headurl": "http://xxx.yyy.cnd.com/80232041AAA","uid": "100013","msg": "good"},"100014": {"username": "小三","headurl": "http://xxx.yyy.cnd.com/11117041AAB","uid": "100014","msg": "bad"}}
}

可以看到,comment_ref_obj與praise_ref_obj兩個字段,有非常重的關系型數據庫痕跡,猜測,這個系統之前應該是放在了普通的關系型數據庫上,或者設計者被關系型數據庫的影響較深。而在MongoDB這種文檔型數據庫里,實際上是沒有必要這樣去設計,這種建模只造成了多于的數據冗余

另外一個問題是,url占用了非常多的信息空間,這點在壓測的時候會有體現,帶寬會過早的成為瓶頸。同樣username信息也是如此,此類信息相對來說是全局穩定的,基本不會做變化。并且這類信息跟隨評論一起在整個APP中流轉,也無法處理”用戶名修改“的需求。

(3)建模 - 推薦的設計

{"_id": 41,"uid": "100000","praise_uid_list": ["100010","100011","100012"],"comment_msg_list": [{"100013": "good"},{"100014": "bad"}]
}

對比可以看到,整個結構要小了幾個數量級,并且可以發現url,usrname信息都全部不見了。那這樣的需求應該如何去實現呢?

從業務抽象上來說,url,username這類信息實際上是非常穩定的,不會發生特別大的頻繁變化。并且這兩類信息實際上都應該是跟uid綁定的,每個uid含有指定的url,username,是最簡單的key,value模型。所以,這類信息非常適合做一層緩存加速讀取查詢。

進一步的,每個人的好友基本上是有限的,頭像,用戶名等信息,甚至可以在APP層面進行緩存,也不會消耗移動端過多容量。但是反過來看,每次都到后段去讀取,不但浪費了移動端的流量帶寬,也加劇了電量消耗。

總結
MongoDB的文檔模型固然強大,但絕對不是等同于關系型數據庫的粗暴聚合,而是要考慮需求和業務,合理的設計。有些人在設計時,也會被文檔模型誤導,三七二十一一股腦的把信息塞到一個文檔中,反而最后會帶來各種使用問題。

2、建模案例分析2:多列數據結構

(1)需求

基于電影票售賣的不同渠道價格存儲。某一個場次的電影,不同的銷售渠道對應不同的價格。整理需求為:

數據字段:
場次信息;
播放影片信息;
渠道信息,與其對應的價格;
渠道數量最多幾十個;

業務查詢有兩種:
根據電影場次,查詢某一個渠道的價格;
根據渠道信息,查詢對應的所有場次信息;

(2)建模 - 不好的模型設計

我們先來看其中一種典型的不好建模設計:

{"scheduleId": "0001","movie": "你的名字","price": {"gewala": 30,"maoyan": 50,"taopiao": 20}
}

數據表達上基本沒有字段冗余,非常緊湊。再來看業務查詢能力:

1、根據電影場次,查詢某一個渠道的價格;
建立createIndex({scheduleId:1, movie:1})索引,雖然對price來說沒有創建索引優化,但通過前面兩個維度,已經可以定位到唯一的文檔,查詢效率上來說尚可;

2、根據渠道信息,查詢對應的所有場次信息;
為了優化這種查詢,需要對每個渠道分別建立索引,例如:
createIndex({“price.gewala”:1})
createIndex({“price.maoyan”:1})
createIndex({“price.taopiao”:1})
但渠道會經常變化,并且為了支持此類查詢,肯能需要創建幾十個索引,對維護來說簡直就是噩夢;
此設計行不通,否決。

(3)建模 - 一般的設計

{"scheduleId": "0001","movie": "你的名字","channel": "gewala","price": 30
}{"scheduleId": "0001","movie": "你的名字","channel": "maoyan","price": 50
}{"scheduleId": "0001","movie": "你的名字","channel": "taopiao","price": 20
}

與上面的方案相比,把整個存儲對象結構進行了平鋪展開,變成了一種表結構,傳統的關系數據庫多數采用這種類型的方案。信息表達上,把一個對象按照渠道維度拆成多個,其他的字段進行了冗余存儲。如果業務需求再復雜點,造成的信息冗余膨脹非常巨大。膨脹后帶來的副作用會有磁盤空間占用上升,內存命中率降低等缺點。對查詢的處理呢:
1、根據電影場次,查詢某一個渠道的價格;
建立createIndex({scheduleId:1, movie:1, channel:1})索引;
2、根據渠道信息,查詢對應的所有場次信息;
建立createIndex({channel:1})索引;

更進一步的優化呢?

(4)建模 - 合理的設計

{"scheduleId": "0001","movie": "你的名字","provider": [{"channel": "gewala","price": 30},{"channel": "maoyan","price": 50},{"channel": "taopiao","price": 20}]
}

這里使用了在MongoDB建模中非常容易忽略的結構——”數組“。查詢方面的處理,是可以建立Multikey Index索引
1、根據電影場次,查詢某一個渠道的價格;
建立createIndex({scheduleId:1, movie:1, "provider.channel":1})索引;
2、根據渠道信息,查詢對應的所有場次信息;
建立createIndex({"provider.channel":1})索引;

總結
這個案例并不復雜,需求也很清晰,但確實非常典型的MongoDB建模設計,開發人員在進行建模設計時經常也會受傳統數據庫的思路影響,沿用之前的思維慣性,而忽略了“文檔”的價值。

3、建模案例分析3 - 物聯網時序數據庫建模

(1)需求

本案例非常適合與IoT場景的數據采集,結合MongoDB的Sharding能力,文檔數據結構等優點,可以非常好的解決物聯網使用場景。

需求:
案例背景是來自真實的業務,美國州際公路的流量統計。數據庫需要提供的能力:
存儲事件數據
提供分析查詢能力

理想的平衡點:
內存使用
寫入性能
讀取分析性能

可以部署在常見的硬件平臺上

(2)建模

每個事件用一個獨立的文檔存儲

{segId: "I80_mile23",speed: 63,ts: ISODate("2013-10-16T22:07:38.000-0500")
}

非常“傳統”的設計思路,每個事件都會寫入一條同樣的信息。多少的信息,就有多少條數據,數據量增長非常快。
數據采集操作全部是Insert語句;

每分鐘的信息用一個獨立的文檔存儲(存儲平均值)

{segId: "I80_mile23",speed_num: 18,speed_sum: 1134,ts: ISODate("2013-10-16T22:07:00.000-0500")
}

對每分鐘的平均速度計算非常友好(speed_sum/speed_num);
數據采集操作基本是Update語句;
數據精度降為一分鐘;

每分鐘的信息用一個獨立的文檔存儲(秒級記錄)

{segId: "I80_mile23",speed: {0:63, 1:58, ... , 58:66, 59:64},ts: ISODate("2013-10-16T22:07:00.000-0500")
}

每秒的數據都存儲在一個文檔中;
數據采集操作基本是Update語句;

每小時的信息用一個獨立的文檔存儲(秒級記錄)

{segId: "I80_mile23",speed: {0:63, 1:58, ... , 3598:54, 3599:55},ts: ISODate("2013-10-16T22:00:00.000-0500")
}

相比上面的方案更進一步,從分鐘到小時:
每小時的數據都存儲在一個文檔中;
數據采集操作基本是Update語句;
更新最后一個時間點(第3599秒),需要3599次迭代(雖然是在同一個文檔中)

進一步優化下:

{segId: "I80_mile23",speed: {0:  {0:47, ..., 59:45},...,59: {0:65, ... , 59:56}}ts: ISODate("2013-10-16T22:00:00.000-0500")
}

用了嵌套的手法把秒級別的數據存儲在小時數據里;
數據采集操作基本是Update語句;
更新最后一個時間點(第3599秒),需要59+59次迭代;

嵌套結構正是MongoDB的魅力所在,稍動腦筋把一維拆成二維,大幅度減少了迭代次數;

每個事件用一個獨立的文檔存儲VS每分鐘的信息用一個獨立的文檔存儲
從寫入上看:后者每次修改的數據量要小很多,并且在WiredTiger引擎下,同一個文檔的修改一定時間窗口下是可以在內存中合并的;
從讀取上看:查詢一個小時的數據,前者需要返回3600個文檔,而后者只需要返回60個文檔,效率上的差異顯而易見;
從索引上看:同樣,因為穩定數量的大幅度減少,索引尺寸也是同比例降低的,并且segId,ts這樣的冗余數據也會減少冗余。容量的降低意味著內存命中率的上升,也就是性能的提高;

每小時的信息用一個獨立的文檔存儲VS每分鐘的信息用一個獨立的文檔存儲
從寫入上看:因為WiredTiger是每分鐘進行一次刷盤,所以每小時一個文檔的方案,在這一個小時內要被反復的load到PageCache中,再刷盤;所以,綜合來看后者相對更合理;
從讀取上看:前者的數據信息量較大,正常的業務請求未必需要這么多的數據,有很大一部分是浪費的;
從索引上看:前者的索引更小,內存利用率更高;

(3)總結

那么到底選擇哪個方案更合理呢?從理論分析上可以看出,不管是小時存儲,還是分鐘存儲,都是利用了MongoDB的信息聚合的能力。
每小時的信息用一個獨立的文檔存儲:設計上較極端,優勢劣勢都很明顯;
每分鐘的信息用一個獨立的文檔存儲:設計上較平衡,不會與業務期望偏差較大;
落實到現實的業務上,哪種是最優的?最好的解決方案就是根據自己的業務情況進行性能測試,以上的分析只是“理論”基礎,給出“實踐”的方向,但千萬不可以此論斷。

十四、MongoDB高級集群架構

1、兩地三中心集群架構

雙中心雙活+異地熱備=兩地三中心
在這里插入圖片描述

2、全球多寫集群架構

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/14225.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/14225.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/14225.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

K8S認證|CKA題庫+答案| 16. 升級集群

16、升級集群 CKA v1.29.0模擬系統免費下載試用: 百度網盤:https://pan.baidu.com/s/1vVR_AK6MVK2Jrz0n0R2GoQ?pwdwbki 題目: 您必須在以下Cluster/Node上完成此考題: Cluster Ma…

CTF網絡安全大賽簡單web題目:eval

題目來源于&#xff1a;bugku 題目難度&#xff1a;簡單 一道簡單web的題目 題目源代碼&#xff1a; <?phpinclude "flag.php";$a $_REQUEST[hello];eval( "var_dump($a);");show_source(__FILE__); ?> 這個PHP腳本有幾個關鍵部分&#xff0c;但…

太陽誘電:順應時代需求的新型電容器為何能在全球得到廣泛應用(下)

隨著汽車電動化和電子控制化的進展&#xff0c;車載計算機和電氣部件也在逐漸向大功率化的方向發展。而構成這些車載設備電源電路的電子元器件也必須隨之進行技術革新。太陽誘電集團攜手全資子公司ELNA&#xff0c;開發并供應新型電容器“導電性高分子混合鋁電解電容器”&#…

【熱門話題】一文帶你讀懂公司是如何知道張三在脈脈上發了“一句話”的

按理說呢&#xff0c;A公司和脈脈屬于不同的平臺&#xff0c;而且脈脈上大家可以匿名發言&#xff0c;所以&#xff0c;即便我坐在你邊上&#xff0c;我發了一句話上去&#xff0c;你也不知道是誰發的。但通過一些技術&#xff0c;我們卻可以分析出&#xff0c;公司是如何知道張…

IOC控制反轉

IOC IOC&#xff0c;全稱為Inversion of Control(控制反轉)&#xff0c;是一種設計原則&#xff0c;它反轉了傳統編程中的控制流程。在傳統的編程模式中&#xff0c;組件之間的依賴關系是由組件自身在內部創建和維護的。而在控制反轉模式中&#xff0c;這種依賴關系由外部容器(…

SSH 免密登錄vscode 附debug 免密登錄失敗問題排查

SSH 免密登錄vscode 附debug 關鍵詞 &#xff1a;vscode ssh ssh無法免密登錄 ssh免密登錄失敗 1 sshd 的配置文件/etc/ssh/sshd_config&#xff0c; 確保公鑰登錄開啟 PubkeyAuthentication yes2 生成公鑰并上傳 ssh-keygen找到本地 .ssh/id_rsa.pub 將其中文本內容搞到…

PS —— 制作證件照

PS —— 制作證件照 裁剪工具魔棒工具油漆桶工具擴展畫布 老是看編程&#xff0c;會有些疲勞&#xff0c;這個專欄我會放一些其他的知識&#xff0c;我們今天利用PS制作證件照&#xff08;注意&#xff0c;這里一些ps的基礎操作我不會很展開的去講&#xff09;&#xff1a; 裁…

Redisson分布式Redis鎖,tryLock方法詳解

在 Java 中&#xff0c;RLock 是 Redisson 庫中提供的一個分布式鎖接口&#xff0c;用于實現基于 Redis 的分布式鎖。RLock 的 tryLock 方法用于嘗試獲取鎖&#xff0c;并在特定的時間內等待獲取鎖。 方法簽名如下&#xff1a; boolean tryLock(long waitTime, long leaseTim…

WPF關鍵組件代碼示例

通過一個綜合示例代碼&#xff0c;展示WPF的關鍵組件&#xff0c;包括XAML、控件、數據綁定、樣式和模板以及動畫。這個示例創建一個簡單的WPF應用程序&#xff0c;包含一個文本框、按鈕和列表框&#xff0c;實現數據綁定、自定義樣式和模板&#xff0c;以及按鈕點擊后的動畫效…

深入解析R語言的貝葉斯網絡模型:構建、優化與預測;INLA下的貝葉斯回歸;現代貝葉斯統計學方法;R語言混合效應(多水平/層次/嵌套)

目錄 ①基于R語言的貝葉斯網絡模型的實踐應用 ②R語言貝葉斯方法在生態環境領域中的應用 ③基于R語言貝葉斯進階:INLA下的貝葉斯回歸、生存分析、隨機游走、廣義可加模型、極端數據的貝葉斯分析 ④基于R語言的現代貝葉斯統計學方法&#xff08;貝葉斯參數估計、貝葉斯回歸、…

react使用AntV

AntV使用&#xff08;https://antv.antgroup.com/&#xff09; import React, { useEffect } from "react"; // npm install antv/g2 import { Chart } from "antv/g2"; const Charts () > { function Ccc() { // 準備數據 const data [ { genre: …

【Linux】腳本shell script

shell是與Linux交互的基本工具 shell script是針對shell所寫的腳本&#xff0c;解釋執行&#xff0c;無需編譯 注意事項 指令的執行是從上而下、從左而右的分析與執行&#xff1b; 指令、選項與參數間的多個空白都會被忽略掉&#xff1b; 空白行也將被忽略掉&#xff0c;并且…

抽象工廠模式(AbstractFactoryPattern)

文章目錄 1.抽象工廠模式定義2.UML類圖3.抽象工廠模式具體實現工廠模式實現單一產品族抽象工廠實現多產品族產品類工廠類使用 4.抽象工廠模式優缺點 1.抽象工廠模式定義 提供一個創建一系列相關或相互依賴對象的接口&#xff0c;而無需指定它們具體的類。 工廠方法模式是單一產…

2024電工杯B題食譜評價與優化模型思路代碼論文分析

2024年電工杯數學建模競賽B題論文和代碼已完成&#xff0c;代碼為B題全部問題的代碼&#xff0c;論文包括摘要、問題重述、問題分析、模型假設、符號說明、模型的建立和求解&#xff08;問題1模型的建立和求解、問題2模型的建立和求解、問題3模型的建立和求解&#xff09;、模型…

正點原子[第二期]Linux之ARM(MX6U)裸機篇學習筆記-17講 定時器按鍵消抖

前言&#xff1a; 本文是根據嗶哩嗶哩網站上“正點原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸機篇”視頻的學習筆記&#xff0c;在這里會記錄下正點原子 I.MX6ULL 開發板的配套視頻教程所作的實驗和學習筆記內容。本文大量引用了正點原子教學視頻和鏈接中的內容。…

計算機網絡安全控制技術

1.防火墻技術 防火墻技術是近年來維護網絡安全最重要的手段&#xff0c;但是防火墻不是萬能的&#xff0c;需要配合其他安全措施來協同 2.加密技術 目前加密技術主要有兩大類&#xff1a;對稱加密和非對稱加密 3.用戶識別技術 核心是識別網絡者是否是屬于系統的合法用戶 …

【設計模式深度剖析】【1】【結構型】【代理模式】| 玩游戲打怪、升級為例加深理解

&#x1f448;?上一篇:創建型設計模式對比 | 下一篇:裝飾器模式&#x1f449;? 目 錄 代理模式定義英文原話直譯如何理解&#xff1f; 3個角色UML類圖1. 抽象主題&#xff08;Subject&#xff09;角色2. 代理類&#xff1a;代理主題&#xff08;Proxy Subject&#xff0…

UE5 OnlineSubsystem Steam創建會話失敗解決方法

連接上Steam但是創建會話失敗 解決方法 在DefaultEngine.ini中加上bInitServerOnClienttrue,這個其實在官方文檔里用注釋給出了&#xff0c;直接取消注釋就行 刪除項目目錄中的Saved、Internmediate、Binaries目錄 右鍵你的項目.uproject選擇Generate Visual Studio project f…

ASP.Net MVC在控制臺添加視圖時沒有模型類并且不能添加視圖

情況如下&#xff1a; 解決方法&#xff1a; 1.查看vs能否創建asp.net mvc項目&#xff0c;這種情況一般是更換了vs打開老項目 2.點擊跳轉至修改安裝選項界面 3.選擇安裝項即可 如果以上都有&#xff1a; 看看你的視圖文件是否存在在項目中 也不能點擊添加&#xff0c;如果…

探索數值分析的奧秘:掌握NumPy與Pandas基礎

新書上架~&#x1f447;全國包郵奧~ python實用小工具開發教程http://pythontoolsteach.com/3 歡迎關注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目錄 一、NumPy&#xff1a;數值計算的效率提升器 二、Pandas&#xff1a;數據處理與分析的利器 …