MongoDB 4.0支持事務了,還有多少人想用MySQL呢?
大家好,我是哪吒,最近項目在使用MongoDB作為圖片和文檔的存儲數據庫,為啥不直接存MySQL裏,還要搭個MongoDB集羣,麻不麻煩?
讓我們一起,一探究竟,繼續學習MongoDB的事務、連接池以及聚合框架,實現快速入門,豐富個人簡歷,提高面試level,給自己增加一點談資,秒變面試小達人,BAT不是夢。
一、MongoDB 不支持事務?
一些第三方文章將 MongoDB 描述成 BASE 數據庫。BASE 是指“基本可用、軟狀態、最終一致”。
但這不是真的,從來都不是!MongoDB 從來都不是“最終一致”的。對主文檔的讀寫是強一致性的,對單個文檔的更新始終是原子的。軟狀態是指需要持續不斷的更新數據,否則數據就會過期,但 MongoDB 並非如此。
最後,如果太多的節點不可用,無法達成仲裁,MongoDB 將進入只讀狀態(降低可用性)。這是有意這麼設計的,因為這樣可以確保在出現問題時保持一致性。
MongoDB 是一個 ACID 數據庫。它支持原子性、一致性、隔離性和持久性。
對單個文檔的更新始終是原子的,從 4.0 版本開始,MongoDB 也支持跨多個文檔和集合的事務。從 4.2 開始,甚至支持分片集羣的跨分片事務。
雖然 MongoDB 支持事務,但在使用它時仍然要謹慎。事務是以性能為代價的,而且由於 MongoDB 支持豐富的分層文檔,如果你的模式設計正確,就沒有必要經常跨多個文檔更新數據。
二、什麼是事務?
事務是數據庫中處理的邏輯單元,包括一個或多個數據庫操作,既可以是讀操作,也可以是寫操作,MongoDB支持跨個多操作、集合、數據庫、文檔和分片的ACID事務。
事務的關鍵:它要麼都成功,要麼都失敗。
三、ACID的定義
ACID是一個事務所需要具備的一組屬性集合。
ACID是原子性atomicity、一致性consistency、隔離性isolation、持久性durability的縮寫。
ACID事務可以確保數據和數據庫狀態的有效性,即使在出現斷電或其它錯誤的情況下也是如此。
原子性確保了事務中的所有操作要麼都被執行、要麼都不被執行。
一致性確保可如果事務成功,那麼數據庫將從一個一致性狀態轉移到下一個一致性狀態。
隔離性是允許多個事務同時在數據庫中運行的屬性。它保證了一個事務不會查看到任何其它事務的部分結果,這意味着多個事務並行運行於依次運行每個事務所獲得的結果都相同。
持久性確保了在提交事務時,即使系統發生故障,所有數據也都會保持持久化。
當數據庫滿足所有這些屬性並且只有成功的事務才會被處理時,它就被稱為符合ACID的數據庫。如果在事務完成之前發生故障,ACID確保不會更改任何數據。
MongoDB是一個分佈式數據庫,它支持跨副本集和跨分片的ACID事務。網絡層增加了額外的複雜性。
四、如何使用事務
MongoDB提供了兩種API來使用事務。
- 第一種與關係型數據庫類似(如start_transaction和commit_transaction),稱為核心API;
- 第二種稱為回調API,一般推薦使用這種;
核心API不會為大多數錯誤提供重試邏輯,它要求開發人員為操作、事務提交函數以及所需的任何重試和錯誤邏輯手動編寫代碼。
與核心API不同,回調API提供了一個簡單的函數,該函數封裝了大量的功能,包括啟動與指定邏輯會話關聯的事務、執行作為回調函數提供的函數以及提交事務。回調API還提供了處理提交錯誤的重試邏輯。在MongoDB4.2中添加回調API是為了簡化使用事務的應用程序開發,也便於添加處理事務錯誤的應用程序重試邏輯。
核心API和回調API的比較
| 核心API| 回調API | |--|--| | 需要顯示調用才能啟動和提交事務 | 啟動事務、執行指定操作,然後提交(可在發生錯誤前終止) | | 不包含TransientTransactionError和UnknowTransactionCommitResult的錯誤處理邏輯,而是提供了為這些錯誤進行自定義處理的靈活性 | 自動為TransientTransactionError和UnknowTransactionCommitResult提供錯誤處理邏輯 | | 要求為特定事務將顯式的邏輯會話傳遞給API | 要求為特定事務將顯式的邏輯會話傳遞給API |
五、重要參數簡介
在MongoDB事務中有兩種限制,第一種是時間,控制事務的運行時間、事務等待獲取鎖的時間以及所有事務運行的最長時間;第二種是MongoDB的oplog條目和單個條目大大小限制;
1、時間限制
事務的默認最大運行時間是1分鐘。可以通過修改transactionLifetimeLimitSeconds的限制來增加。對於分片集羣,必須在所有分片副本集成員上設置該參數。超過此時間後,事務將被視為已過期,並由定期運行的清理進程終止。清理進程每60秒或transactionLifetimeLimitSeconds/2運行一次,以較小的值為準。
要顯式設置事務的時間限制,建議在提交事務時指定maxTimeMS參數。如果maxTimeMS沒有設置,那麼將使用transactionLifetimeLimitSeconds;如果設置了maxTimeMS,但這個值超過了transactionLifetimeLimitSeconds,那麼還是會使用transactionLifetimeLimitSeconds。
事務等待獲取其操作所需鎖的默認最大時間是 5 毫秒。可以通過修改maxTransactionLockRequestTimeoutMillis參數來控制。如果事務在此期間無法獲取鎖,則該事務會被終止。
maxTransactionLockRequestTimeoutMillis可以被設置為0、-1或大於0的數字。
- 設置為0,表示如果事務無法立即獲得所需的所有鎖,則該事務會被終止;
- 設置為-1,將使用由maxTimeMS參數所指定的超時時間;
- 設置為大於0的其它數字,將等待時間配置為該時間,嘗試獲取鎖的等待時間也是該時間,單位秒;
2、oplog大小限制
MongoDB會創建出與事務中寫操作數量相同的oplog數目。但是,每個oplog條目必須在16MB的BSON文檔大小限制之內。
六、連接池 = 數據庫連接的緩存
在最開始接觸MongoDB的時候,是通過 MongoDatabase database = new MongoClient("localhost", 27017).getDatabase("test");
的方式連接MongoDB。
它會為每個請求創建一個新的連接,然後銷燬,一般數據庫的連接都是TCP連接,TCP是長連接,如果不斷開,就會一直連着。
眾所周知,新建一個數據庫連接的代價是很大的,複用現有連接才是首選,連接池就是幹這個的。
因此當需要新的連接時,就可以複用連接池中緩存的連接了。如果使用得當,連接池可以最大程度的降低數據庫的新連接數量、創建頻率。
可以通過Mongo.get方法獲得DB對象,表示MongoDB數據庫的一個連接。默認情況下,當執行完數據庫的查詢操作後,連接將自動回到連接池中,通過api中的finally方法,將連接歸還給連接池,不需要手動調用。
1、MongoDB查詢數據五步走
- MongoDB Client需要找到可用的MongoDB;
- Server MongoDB Client需要和 MongoDB Server建立 Connection;
- 應用程序處理線程從 Connection Pool中獲取 Connection;
- 數據傳輸(獲取連接後,進行 Socket 通信,獲取數據);
- 斷開 Collection;
2、MongoDB連接池的參數配置
```xml
線程池允許的最大連接數
connectionsPerHost: 40
線程池中連接的最大空閒時間
threadsAllowedToBlockForConnectionMultiplier: 20
1、MongoDB Client需要找到可用的MongoDB Server所需要的等待時間
serverSelectionTimeout: 40000
2、MongoDB Client需要和MongoDB Server建立(new)Connection
connectTimeout: 60000
3、應用程序處理線程從Connection Pool中獲取Connection
maxWaitTime: 120000
自動重連
autoConnectRetry: true
socket是否保活
socketKeepAlive: true
4、數據傳輸(獲取連接後,進行Socket通信,獲取數據)
socketTimeout: 30000 slaveOk: true dbName: ngo
是否進行權限驗證
auth: false
用户名
username: ngo
密碼
password: 12345678 ```
七、聚合框架
聚合框架是MongoDB中的一組分析工具,可以對一個或多個集合中的文檔進行分析。
聚合框架基於管道的概念,使用聚合管道可以從MongoDB集合獲取輸入,並將該集合中的文檔傳遞到一個或多個階段,每個階段對輸入執行不同的操作。每個階段都將之前階段輸出的內容作為輸入。所有階段的輸入和輸出都是文檔,可以稱為文檔流。
每個階段都會提供一組按鈕或可調參數,可以通過控制它們來設置該階段的參數,以執行各種任務。
這些可調參數通常採用運算符的形式,可以使用這些運算符來修改字段、執行算術運算、調整文檔形狀、執行各種累加任務或其它各種操作。
常見的聚合管道包括匹配match、投射project、排序sort、跳過skip、限制limit。
八、MongoDB文檔格式設計
文檔中表示數據的方式,在進行文檔格式設計時,首先需要了解查詢和數據訪問的方式。
1、限制條件
比如最大文檔大小為16MB。
2、查詢和寫入的訪問模式
通過了解查詢的運行時間和頻率,識別出最常見的查詢,一旦確定了這些查詢,就應該儘量減少查詢的數量,並在文檔設計中確保一起查詢的數據存儲在同一個文檔中。
這些查詢中未使用的數據應該存放在不同的集合中。需要考慮是否可以將動態數據(讀/寫)和靜態數據(讀)分離開。在進行文檔格式設計時,提高最常見查詢的優先級會獲得最佳的性能。
3、關係類型
根據業務邏輯、文檔之間的關係來考慮哪些數據是相關的,確定使用嵌入還是引用。需要弄清楚如何在不執行其它查詢的情況下引用文檔,以及當關系發生變化時需要更新幾個文檔。還要考慮數據結構是否易於查詢。
4、範式化與反範式化
- 範式化是指數據分散在多個集合中,在集合之間進行數據的引用;
- 反範式化會將所有數據嵌入單個文檔中;
如何選擇範式化與反範式化,範式化的寫入速度更快,而反範式化的讀取速度更快,因此需要根據應用程序的實際需求進行權衡。
5、內嵌數據和引用數據對比
| 更適合內嵌數據 | 更適合引用數據 | |--|--| | 較小子文檔 | 較大子文檔 | | 數據不經常變更 | 數據經常變更 | | 數據最終一致即可 | 必須保證強一致性 | | 數據通常需要二次查詢才能獲得 | 數據通常不包含在結果中 | | 快速讀取 | 快速寫入 |
6、優化數據操作
優化讀操作通常包括正確的索引和單個文檔中返回儘可能多的數據; 優化寫操作通常包括減少索引數量、儘可能的提高更新效率;
通過刪除舊數據進行優化:
第一種方式是通過固定集合實現;
第二種是使用TTL集合。TTL集合可以更精確的控制刪除文檔的時間,但在寫入量過大的集合中操作速度不夠快,通過遍歷TTL索引來刪除文檔。
第三種方式是分庫分表。每個月的文檔單獨使用一個集合。這種方式實現起來更加複雜,因為需要使用動態集合或數據庫名稱,可能需要查詢多個數據庫。
九、小結
- MongoDB從4.0開始支持事務了,MongoDB 是一個 ACID 數據庫。它支持原子性、一致性、隔離性和持久性。雖然 MongoDB 支持事務,但在使用它時仍然要謹慎。事務是以性能為代價的;
- 瞭解MongoDB瞭如何使用事務以及它的參數配置方案,達到即插即用的效果;
- 對MongoDB查詢數據的過程,有了更深層次的理解;
- 領悟了MongoDB連接池的意義;
- 深刻理解了MongoDB文檔格式設計思想;
- 總結了MongoDB讀寫的優化方式;