MongoDB中的一些坑
MongoDB 是目前炙手可熱的 NoSQL 文件型資料庫,它提供的一些特性很棒:如自動 failover 機制,自動 sharding,無模式 schemaless,大部分情況下效能也很棒。但是作者在深入使用 MongoDB 過程中,遇到了不少問題,下面總結幾個我們遇到的坑。特別申明:我們目前用的 MongoDB 版本是 2.4.10,曾經升級到 MongoDB 2.6.0 版本,問題依然存在,又回退到 2.4.10 版本。
MongoDB 資料庫級鎖
坑爹指數:5星(最高5星)
MongoDB的鎖機制和一般關聯式資料庫如 MySQL(InnoDB), Oracle 有很大的差異,InnoDB 和 Oracle 能提供行級粒度鎖,而 MongoDB 只能提供 庫級粒度鎖,這意味著當 MongoDB 一個寫鎖處於佔用狀態時,其它的讀寫操作都得乾等。
初看起來庫級鎖在大併發環境下有嚴重的問題,但是 MongoDB 依然能夠保持大併發量和高效能,這是因為 MongoDB 的鎖粒度雖然很粗放,但是在鎖處理機制和關聯式資料庫鎖有很大差異,主要表現在:
MongoDB 沒有完整事務支援,操作原子性只到單個 document 級別,所以通常操作粒度比較小;
MongoDB 鎖實際佔用時間是記憶體資料計算和變更時間,通常很快;
MongoDB 鎖有一種臨時放棄機制,當出現需要等待慢速 IO 讀寫資料時,可以先臨時放棄,等 IO 完成之後再重新獲取鎖。
通常不出問題不等於沒有問題,如果資料操作不當,依然會導致長時間佔用寫鎖,比如下面提到的前臺建索引操作,當出現這種情況的時候,整個資料庫就處於完全阻塞狀態,無法進行任何讀寫操作,情況十分嚴重。
解決問題的方法,儘量避免長時間佔用寫鎖操作,如果有一些集合操作實在難以避免,可以考慮把這個集合放到一個單獨的 MongoDB 庫裡,因為 MongoDB 不同庫鎖是相互隔離的,分離集合可以避免某一個集合操作引發全域性阻塞問題。
建索引導致資料庫阻塞
坑爹指數:3星
上面提到了 MongoDB 庫級鎖的問題,建索引就是一個容易引起長時間寫鎖的問題,MongoDB 在前臺建索引時需要佔用一個寫鎖(而且不會臨時放棄),如果集合的資料量很大,建索引通常要花比較長時間,特別容易引起問題。
解決的方法很簡單,MongoDB 提供了兩種建索引的訪問,一種是 background 方式,不需要長時間佔用寫鎖,另一種是非 background 方式,需要長時間佔用鎖。使用 background 方式就可以解決問題。 例如,為超大表 posts 建立索引, 千萬不用使用
db.posts.ensureIndex({user_id: 1})
而應該使用
db.posts.ensureIndex({user_id: 1}, {background: 1})
不合理使用嵌入 embed document
坑爹指數:5星
embed document 是 MongoDB 相比關聯式資料庫差異明顯的一個地方,可以在某一個 document 中嵌入其它子 document,這樣可以在父子 document 保持在單一 collection 中,檢索修改比較方便。
比如薄荷的應用情景中有一個 Group document,使用者申請加入 Group 建模為 GroupRequest document,我們最初的時候使用 embed 方式把 GroupRequest 放置到 Group 中。 Ruby 程式碼如下所示(使用了 Mongoid ORM):
class Group
include Mongoid::Document
...
embeds_many :group_requests
...
end
class GroupRequest
include Mongoid::Document
...
embedded_in :group
...
end
這個使用方式讓我們掉到坑裡了,差點就爬不出來,它導致有接近兩週的時間系統問題,高峰時段常有幾分鐘的系統卡頓,最嚴重一次甚至引起 MongoDB 當機。
仔細分析後,發現某些活躍的 Group 的 group_requests 增加(當有新申請時)和更改(當透過或拒絕使用者申請時)異常頻繁,而這些操作經常長時間佔用寫鎖,導致整個資料庫阻塞。原因是當有增加 group_request 操作時,Group 預分配的空間不夠,需要重新分配空間(記憶體和硬碟都需要),耗時較長,另外 Group 上建的索引很多,移動 Group 位置導致大量索引更新操作也很耗時,綜合起來引起了長時間佔用鎖問題。
解決問題的方法,說起來也簡單,就是把 embed 關聯更改成的普通外來鍵關聯,就是類似關聯式資料庫的做法,這樣 group_request 增加或修改都只發生在 GroupRequest 上,簡單快速,避免長時間佔用寫鎖問題。當關聯物件的資料不固定或者經常發生變化時,一定要避免使用 embed 關聯,不然會死的很慘。
不合理使用 Array 欄位
坑爹指數:4星
MongoDB 的 Array 欄位是比較獨特的一個特性,它可以在單個 document 裡儲存一些簡單的一對多關係。
薄荷有一個應用情景使用遇到嚴重的效能問題,直接上程式碼如下所示:
class User
include Mongoid::Document
...
field :follower_user_ids, type: Array, default: []
...
end
User 中透過一個 Array 型別欄位 follower_user_ids 儲存使用者關注的人的 id,使用者關注的人從 10個到 3000 個不等,變化是比較頻繁的,和上面 embed 引發的問題類似,頻繁的 follower_user_ids 增加修改操作導致大量長時間資料庫寫鎖,從而引發 MongoDB 資料庫效能急劇下降。
解決問題的方法:我們把 follower_user_ids 轉移到了記憶體資料庫 redis 中,避免了頻繁更改 MongoDB 中的 User, 從而徹底解決問題。如果不使用 redis,也可以建立一個 UserFollower 集合,使用外來鍵形式關聯。
先列舉上面幾個坑吧,都是害人不淺的陷阱,使用 MongoDB 過程一定要多加註意,避免掉到坑裡。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15498/viewspace-1982637/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- AngularJS 中的一些坑AngularJS
- js中函式的一些”坑“JS函式
- 在JSON中遇到的一些坑JSON
- 表單提交中碰到的一些坑
- pyqt5使用中的一些坑QT
- 嘗試 WebGPU 過程中掉的一些坑WebGPU
- [微信小程式]開發中遇到的一些“坑”微信小程式
- SQLSERVER的一些坑。SQLServer
- mongodb 的一些日常操作MongoDB
- 記一些vue使用postcss中遇到的坑o(╯□╰)oVueCSS
- $.ajax的一些坑啊
- 關於MongoDB的一些PPTMongoDB
- Go中slice作為引數傳遞的一些“坑”Go
- golang最近遇到的一些坑Golang
- yarn install 的一些坑Yarn
- js使用defineProperty的一些坑JS
- 雙刃劍MongoDB的學習和避坑MongoDB
- 前端的一些坑,一些記錄,一些冷知識前端
- 在 ReactNative 的 App 中,整合 Bugly 你會遇到的一些坑ReactAPP
- 動態載入的一些坑
- Go語言中defer的一些坑Go
- JavaScript深拷貝的一些坑JavaScript
- 使用Golang時遇到的一些坑Golang
- 遇到 MySQL 8.0.11 的一些坑MySql
- CSS新手常遇到的一些坑CSS
- MongoDB 中的鎖分析MongoDB
- MongoDB 中的事務MongoDB
- iOS多級選擇框架封裝與專案中的一些坑iOS框架封裝
- 記錄Centos一些坑CentOS
- windows開發環境的一些坑Windows開發環境
- Flutter v1.12.13 的一些坑Flutter
- Vue中元件使用的一些小坑兒Vue元件
- Presto上使用SQL遇到的一些坑RESTSQL
- 從SqlServer轉手Oracle的一些坑SQLServerOracle
- Latex 編輯論文的一些坑
- docker釋出專案的一些坑Docker
- Mongodb安裝坑 - Service 'MongoDB Server' (MongDB) failed to start. Verify that you have...MongoDBServerAI
- MongoDB中的定時索引MongoDB索引