MongoDB 4.0支援事務了,還有多少人想用MySQL呢?

語言: CN / TW / HK

大家好,我是哪吒,最近專案在使用MongoDB作為圖片和文件的儲存資料庫,為啥不直接存MySQL裡,還要搭個MongoDB叢集,麻不麻煩?

讓我們一起,一探究竟,繼續學習MongoDB的事務、連線池以及聚合框架,實現快速入門,豐富個人簡歷,提高面試level,給自己增加一點談資,秒變面試小達人,BAT不是夢。

一、MongoDB 不支援事務?

一些第三方文章將 MongoDB 描述成 BASE 資料庫。BASE 是指“基本可用、軟狀態、最終一致”。

但這不是真的,從來都不是!MongoDB 從來都不是“最終一致”的。對主文件的讀寫是強一致性的,對單個文件的更新始終是原子的。軟狀態是指需要持續不斷的更新資料,否則資料就會過期,但 MongoDB 並非如此。

最後,如果太多的節點不可用,無法達成仲裁,MongoDB 將進入只讀狀態(降低可用性)。這是有意這麼設計的,因為這樣可以確保在出現問題時保持一致性。

MongoDB 是一個 ACID 資料庫。它支援原子性、一致性、隔離性和永續性。

對單個文件的更新始終是原子的,從 4.0 版本開始,MongoDB 也支援跨多個文件和集合的事務。從 4.2 開始,甚至支援分片叢集的跨分片事務。

雖然 MongoDB 支援事務,但在使用它時仍然要謹慎。事務是以效能為代價的,而且由於 MongoDB 支援豐富的分層文件,如果你的模式設計正確,就沒有必要經常跨多個文件更新資料。

4.0時代.jpg

二、什麼是事務?

事務是資料庫中處理的邏輯單元,包括一個或多個數據庫操作,既可以是讀操作,也可以是寫操作,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查詢資料五步走

  1. MongoDB Client需要找到可用的MongoDB;
  2. Server MongoDB Client需要和 MongoDB Server建立 Connection;
  3. 應用程式處理執行緒從 Connection Pool中獲取 Connection;
  4. 資料傳輸(獲取連線後,進行 Socket 通訊,獲取資料);
  5. 斷開 Collection;

五步走.png

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集合獲取輸入,並將該集合中的文件傳遞到一個或多個階段,每個階段對輸入執行不同的操作。每個階段都將之前階段輸出的內容作為輸入。所有階段的輸入和輸出都是文件,可以稱為文件流。

聚合管道.png

每個階段都會提供一組按鈕或可調引數,可以通過控制它們來設定該階段的引數,以執行各種任務。

這些可調引數通常採用運算子的形式,可以使用這些運算子來修改欄位、執行算術運算、調整文件形狀、執行各種累加任務或其它各種操作。

常見的聚合管道包括匹配match、投射project、排序sort、跳過skip、限制limit。

八、MongoDB文件格式設計

文件中表示資料的方式,在進行文件格式設計時,首先需要了解查詢和資料訪問的方式。

1、限制條件

比如最大文件大小為16MB。

2、查詢和寫入的訪問模式

通過了解查詢的執行時間和頻率,識別出最常見的查詢,一旦確定了這些查詢,就應該儘量減少查詢的數量,並在文件設計中確保一起查詢的資料儲存在同一個文件中。

這些查詢中未使用的資料應該存放在不同的集合中。需要考慮是否可以將動態資料(讀/寫)和靜態資料(讀)分離開。在進行文件格式設計時,提高最常見查詢的優先順序會獲得最佳的效能。

3、關係型別

根據業務邏輯、文件之間的關係來考慮哪些資料是相關的,確定使用嵌入還是引用。需要弄清楚如何在不執行其它查詢的情況下引用文件,以及當關系發生變化時需要更新幾個文件。還要考慮資料結構是否易於查詢。

4、正規化化與反正規化化

  • 正規化化是指資料分散在多個集合中,在集合之間進行資料的引用;
  • 反正規化化會將所有資料嵌入單個文件中;

如何選擇正規化化與反正規化化,正規化化的寫入速度更快,而反正規化化的讀取速度更快,因此需要根據應用程式的實際需求進行權衡。

5、內嵌資料和引用資料對比

| 更適合內嵌資料 | 更適合引用資料 | |--|--| | 較小子文件 | 較大子文件 | | 資料不經常變更 | 資料經常變更 | | 資料最終一致即可 | 必須保證強一致性 | | 資料通常需要二次查詢才能獲得 | 資料通常不包含在結果中 | | 快速讀取 | 快速寫入 |

6、優化資料操作

優化讀操作通常包括正確的索引和單個文件中返回儘可能多的資料; 優化寫操作通常包括減少索引數量、儘可能的提高更新效率;

通過刪除舊資料進行優化:

第一種方式是通過固定集合實現;

第二種是使用TTL集合。TTL集合可以更精確的控制刪除文件的時間,但在寫入量過大的集合中操作速度不夠快,通過遍歷TTL索引來刪除文件。

第三種方式是分庫分表。每個月的文件單獨使用一個集合。這種方式實現起來更加複雜,因為需要使用動態集合或資料庫名稱,可能需要查詢多個數據庫。

九、小結

  1. MongoDB從4.0開始支援事務了,MongoDB 是一個 ACID 資料庫。它支援原子性、一致性、隔離性和永續性。雖然 MongoDB 支援事務,但在使用它時仍然要謹慎。事務是以效能為代價的;
  2. 瞭解MongoDB瞭如何使用事務以及它的引數配置方案,達到即插即用的效果;
  3. 對MongoDB查詢資料的過程,有了更深層次的理解;
  4. 領悟了MongoDB連線池的意義;
  5. 深刻理解了MongoDB文件格式設計思想;
  6. 總結了MongoDB讀寫的優化方式;