[TOC]java
數據模型定義對照:mysql
MySQL | MongoDB |
---|---|
行 | 文檔 |
表 | 集合 |
庫 | 庫 |
組複製 | 複製集 |
專有名詞翻譯約定:ios
中文 | 原文 |
---|---|
讀一致性 | readConcern |
寫一致性 | writeConcern |
4.0版本中加入sql
New in version 4.0mongodb
在MongoDB中,針對單個文檔的操做是原子性的。因爲MongoDB容許在單個文檔中嵌入用以表示相互之間關係的子文檔和數組來替代跨文檔和集合的鏈接操做,這種折中的方式在不少場景下間接實現了多文檔事務的特性。shell
In MongoDB, an operation on a single document is atomic. Because you can use embedded documents and arrays to capture relationships between data in a single document structure instead of normalizing across multiple documents and collections, this single-document atomicity obviates the need for multi-document transactions for many practical use cases.數據庫
但這種辦法在面對多文檔同時更新或者多文檔一致性讀的時候就顯得捉襟見肘,MongoDB新版本提供了面向複製集的多文檔事務特性。其能知足在多個操做,文檔,集合,數據庫之間的事務性,事務的特性:一個事務中的若干個操做要麼所有完成,要麼所有回滾,操做的原子性A,數據更新的一致性C。事務提交時,全部數據更改都會被永久保存D。事務提交前其數據更改不會被外部獲取到I。express
However, for situations that require atomicity for updates to multiple documents or consistency between reads to multiple documents, MongoDB provides the ability to perform multi-document transactions against replica sets. Multi-document transactions can be used across multiple operations, collections, databases, and documents. Multi-document transactions provide an 「all-or-nothing」 proposition. When a transaction commits, all data changes made in the transaction are saved. If any operation in the transaction fails, the transaction aborts and all data changes made in the transaction are discarded without ever becoming visible. Until a transaction commits, no write operations in the transaction are visible outside the transaction.數組
注意:promise
在多數場景下,多文檔事務相對於單文檔數據變動性能損耗會更嚴重。且不要由於支持多文檔事務就妄圖放鬆對數據結構設計的要求。不管哪一種數據庫,巧妙的表設計老是會比頭鐵強行使用沒必要要的低級數據關聯來的高效。
通常狀況下,因爲減小了對多文檔事務的需求,非範式化(即內嵌子文檔和數組)的數據模型仍是會發揮比較大的做用
In most cases, multi-document transaction incurs a greater performance cost over single document writes, and the availability of multi-document transaction should not be a replacement for effective schema design. For many scenarios, the denormalized data model (embedded documents and arrays) will continue to be optimal for your data and use cases. That is, for many scenarios, modeling your data appropriately will minimize the need for multi-document transactions.
多文檔事務在4.0版本僅支持複製集,對分片集羣的事務性支持計劃在4.2版本中實現。
Multi-document transactions are available for replica sets only. Transactions for sharded clusters are scheduled for MongoDB 4.2 [1].
免責聲明,不翻譯了 | The development, release, and timing of any features or functionality described for our products remains at our sole discretion. This information is merely intended to outline our general product direction and it should not be relied on in making a purchasing decision nor is this a commitment, promise or legal obligation to deliver any material, code, or functionality. |
---|---|
想使用多文檔事務的特性的話, featureCompatibilityVersion
值必須設爲4.0
以上。(開啓後,既存的數據不支持用4.0如下版本的mongod啓動)
The featureCompatibilityVersion
(fCV) of all members of the replica set must be 4.0
or greater. To check the fCV for a member, connect to the member and run the following command:
db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
For more information on fCV, see setFeatureCompatibilityVersion
.
多文檔事務特性僅支持WiredTiger存儲引擎
Multi-document transactions are only available for deployments that use WiredTiger storage engine.
Multi-document transactions are not available for deployments that use in-memory storage engine or the deprecated MMAPv1 storage engine.
注意事項:
For transactions:
能夠在任何跨庫的表上進行CURD操做
You can specify read/write (CRUD) operations on existing collections. The collections can be in different databases.
config
,admin
,local
庫中的表不支持多文檔事務
You cannot read/write to collections in the config
, admin
, or local
databases.
各庫中的system.*
表不支持多文檔事務
You cannot write to system.*
collections.
在當前會話的事務中沒法進行返回當前操做的查詢計劃
You cannot return the supported operation’s query plan (i.e. explain
).
在事務外建立的遊標沒法在事務中進行getMore
操做
For cursors created outside of transactions, you cannot call getMore
inside a transaction.
在事務中建立的遊標沒法在事務外進行getMore
操做
getMore
outside the transaction.在多文檔事務中沒法進行諸如建立或者刪除集合,添加索引等更新數據庫元數據的操做。進一步的說,在多文檔事務中那些可能會隱式建立集合的操做也是被禁止的。
Operations that affect the database catalog, such as creating or dropping a collection or an index, are not allowed in multi-document transactions. For example, a multi-document transaction cannot include an insert operation that would result in the creation of a new collection. See Restricted Operations.
提示:
若是在建立或者刪除集合後會緊接着開啓一個會對該集合進行操做的事務,那麼建立或者刪除集合的操做須要追加寫一致性(writeConcern)擴散到多數節點的參數以確保事務能夠成功獲取該集合相關的鎖。
(注,這段話可能較爲拗口。實際上是從集羣的一致性來考慮,和事務的讀一致性readConcern相關)
When creating or dropping a collection immediately before starting a transaction, if the collection is accessed within the transaction, issue the create or drop operation with write concern "majority"
to ensure that the transaction can acquire the required locks.
目前支持多文檔事務的命令和方法
For multi-document transactions:
Method | Command | Note |
---|---|---|
db.collection.aggregate() |
aggregate |
Excluding the following stages:$collStats $currentOp $indexStats $listLocalSessions $listSessions $out |
db.collection.distinct() |
distinct |
|
db.collection.find() |
find |
|
geoSearch |
||
db.collection.deleteMany() db.collection.deleteOne() db.collection.remove() |
delete |
|
db.collection.findOneAndDelete() db.collection.findOneAndReplace() db.collection.findOneAndUpdate() |
findAndModify |
For upsert , only when run against an existing collection. |
db.collection.insertMany() db.collection.insertOne() db.collection.insert() |
insert |
Only when run against an existing collection. |
db.collection.save() |
If an insert, only when run against an existing collection. | |
db.collection.updateOne() db.collection.updateMany() db.collection.replaceOne() db.collection.update() |
update |
For upsert , only when run against an existing collection. |
db.collection.bulkWrite() Various Bulk Operation Methods |
For insert operations, only when run against an existing collection. |
若是須要在事務中進行計數操做,須要使用 $count
操做符或者把 $group
和$sum
結合起來
To perform a count operation within a transaction, use the $count
aggregation stage or the $group
(with a$sum
expression) aggregation stage.
兼容4.0版本的MongoDB驅動內含一個集合層級由 $group
和$sum
包裝出來的 countDocuments(filter,options)
計數接口
MongoDB drivers compatible with the 4.0 features provide a collection-level API countDocuments(filter,options)
as a helper method that uses the $group
with a $sum
expression to perform a count.
諸如 isMaster
, buildInfo
, connectionStatus
(及其相關的幫助命令)這些獲取參數或者信息的命令能夠在事務中使用,但不能做爲事務中起手的第一個操做。
Informational commands, such as isMaster
, buildInfo
, connectionStatus
(and their helper methods) are allowed in transactions; however, they cannot be the first operation in the transaction.
下列操做不容許在多文檔事務中使用:
The following operations are not allowed in multi-document transactions:
可能會影響到數據庫元信息的操做,諸如建立或者刪除集合,增長索引。(注:其實就是DDL操做吧)。
Operations that affect the database catalog, such as creating or dropping a collection or an index. For example, a multi-document transaction cannot include an insert operation that would result in the creation of a new collection.
listCollections
和 listIndexes
及其相關的幫助命令都在上述禁止之列。(注:這個有點意外)
The listCollections
and listIndexes
commands and their helper methods are also excluded.
非增刪改查和非查詢參數與信息的操做,諸如 createUser
, getParameter
, count
及其幫助命令等等。
createUser
, getParameter
, count
, etc. and their helpers. 若是啓用了鑑權,欲使用多文檔事務,必須擁有操做事務的權限。
If running with access control, you must have privileges for the operations in the transaction. [2]
若是開啓了審計,回滾的事務仍然會被記錄。
若使用了附加驗證方式,用戶名大小不能超過10K | If using $external authentication users (i.e. Kerberos, LDAP, x.509 users), the usernames cannot be greater than 10k bytes. |
---|---|
事務是和會話緊密相關的,一個會話在其生命週期內最多隻能併發一個處於激活狀態的事務。
Transations are associated with a session. That is, you start a transaction for a session. At any given time, you can have at most one open transaction for a session.
重點 IMPORTANT
當使用驅動進行鏈接時(非mongo shell鏈接),必須將會話與每一個事務對應起來。(注:多事務就要開啓多個鏈接會話類)。若在事務正在進行時會話退出,則事務會進行回滾
When using the drivers, you must pass the session to each operation in the transaction.
If a session ends and it has an open transaction, the transaction aborts.
如下是支持多文檔事務的各語言最低驅動版本號:
Clients require MongoDB drivers updated for MongoDB 4.0.
Java | Python | C# | Node | Ruby | Perl | PHPC | Scala |
---|---|---|---|---|---|---|---|
3.8.0 | 3.7.0C 1.11.0 | 2.7 | 3.1.0 | 2.6.0 | 2.0.0 | 1.5.0 | 2.4.0 |
重點:IMPORTANT
若是想在事務中進行讀寫結合的操做,一樣必須把每一個操做和會話對應起來
To associate read and write operations with a transaction, you must pass the session to each operation in the transaction. For examples, see Transactions and Retryable Writes.
mongo
Shell下面這些mongo shell方法能夠用於操做事務:
The following mongo
shell methods are available for transactions:
面向高可用應用 HIGHLY AVAILABLE APPLICATIONS
不管是MongoDB仍是關係型數據庫,與其鏈接的應用都應該設法處理事務提交過程的錯誤,設計事務的從新提作邏輯。
Regardless of the database system, whether MongoDB or relational databases, applications should take measures to handle errors during transaction commits and incorporate retry logic for transactions.
不管 retryWrites
有沒有被設置爲true
,事務中獨立的寫操做老是是沒法被重作的
The individual write operations inside the transaction are not retryable, regardless of whether retryWrites
is set to true
.
若是操做執行時發生了錯誤,會返回一個包含名爲 errorLabels
的數組字段。若是該錯誤是暫時性的, errorLabels
會包含一個 "TransientTransactionError"
元素,標誌着整個事務能夠被重作。
If an operation encounters an error, the returned error may have an errorLabels
array field. If the error is a transient error, the errorLabels
array field contains "TransientTransactionError"
as an element and the transaction as a whole can be retried.
例如,下面的下面的JAVA函數就實現了執行事務並重作那些包含"TransientTransactionError"
的錯誤。
(注:官方文檔有其餘幾種語言的例程,這裏選擇使用人數最多的JAVA)
For example, the following helper runs a function and retries the function if a "TransientTransactionError"
is encountered:
void runTransactionWithRetry(Runnable transactional) { while (true) { try { transactional.run(); break; } catch (MongoException e) { System.out.println("Transaction aborted. Caught exception during transaction."); if (e.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)) { System.out.println("TransientTransactionError, aborting transaction and retrying ..."); continue; } else { throw e; } } } }
提交操做自己是能夠被重作的。若是事務在提交過程當中遇到錯誤,驅動會自動忽略 retryWrites
的設置進行一次重試。
The commit operations are retryable write operations. If the commit operation operation encounters an error, MongoDB drivers retry the operation a single time regardless of whether retryWrites
is set to true
.
若是事務提交的過程當中發生錯誤,MongoDB會返回一個包含 errorLabels
的數組字段。若是錯誤是暫時性的, errorLabels
字段會包含一個"UnknownTransactionCommitResult"
元素,標識這個事務能夠被從新提交
If the commit operation encounters an error, MongoDB returns an error with an errorLabels
array field. If the error is a transient commit error, the errorLabels
array field contains"UnknownTransactionCommitResult"
as an element and the commit operation can be retried.
儘管MongoDB驅動提供了一次事務從新提交機制,應用方面仍應該設計一個方法去處理事務提交過程當中爆出的錯誤。
In addition to the single retry behavior provided by the MongoDB drivers, applications should take measures to handle "UnknownTransactionCommitResult"
errors during transaction commits.
仍以JAVA爲例:
void commitWithRetry(ClientSession clientSession) { while (true) { try { clientSession.commitTransaction(); System.out.println("Transaction committed"); break; } catch (MongoException e) { // can retry commit if (e.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)) { System.out.println("UnknownTransactionCommitResult, retrying commit operation ..."); continue; } else { System.out.println("Exception during commit ..."); throw e; } } } }
將上文兩個邏輯結合起來,看下面的例程:(JAVA)
void runTransactionWithRetry(Runnable transactional) { while (true) { try { transactional.run(); break; } catch (MongoException e) { System.out.println("Transaction aborted. Caught exception during transaction."); if (e.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)) { System.out.println("TransientTransactionError, aborting transaction and retrying ..."); continue; } else { throw e; } } } } void commitWithRetry(ClientSession clientSession) { while (true) { try { clientSession.commitTransaction(); System.out.println("Transaction committed"); break; } catch (MongoException e) { // can retry commit if (e.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)) { System.out.println("UnknownTransactionCommitResult, retrying commit operation ..."); continue; } else { System.out.println("Exception during commit ..."); throw e; } } } } void updateEmployeeInfo() { MongoCollection<Document> employeesCollection = client.getDatabase("hr").getCollection("employees"); MongoCollection<Document> eventsCollection = client.getDatabase("hr").getCollection("events"); try (ClientSession clientSession = client.startSession()) { clientSession.startTransaction(); employeesCollection.updateOne(clientSession, Filters.eq("employee", 3), Updates.set("status", "Inactive")); eventsCollection.insertOne(clientSession, new Document("employee", 3).append("status", new Document("new", "Inactive").append("old", "Active"))); commitWithRetry(clientSession); } } void updateEmployeeInfoWithRetry() { runTransactionWithRetry(this::updateEmployeeInfo); }
多文檔事務知足ACID的原子性
Multi-document transactions are atomic:
當一個事務被提交時,該事務內部中全部的變動都會保存而且能夠被其餘會話的事務讀到。
When a transaction commits, all data changes made in the transaction are saved and visible outside the transaction. Until a transaction commits, the data changes made in the transaction are not visible outside the transaction.
當事務回滾時,事務內部全部的變動都會被幹淨的丟棄。好比,事務中的任一操做失敗後,事務回滾,當前事務中全部的變動都會被丟棄,且不爲其餘會話所知。
多文檔事務中的讀操做必須使用primary
傾向性,即從複製集的主實例
Multi-document transactions that contain read operations must use read preference primary
.
All operations in a given transaction must route to the same member.
session.startTransaction( { readConcern: { level: <level> }, writeConcern: { w: <value>, j: <boolean>, wtimeout: <number> } } );
多文檔事務支持三種讀一致性等級: "snapshot"
, "local"
, and "majority"
,即快照,本機,多數派
Multi-document transactions support read concern "snapshot"
, "local"
, and "majority"
:
針對本機和多數派的讀一致性等級,MongoDB在有些時候回使用更強的等級進行替換。(注:爲啥?)
For "local"
and "majority"
read concern, MongoDB may sometimes substitute a stronger read concern.
針對多數派讀一致性等級,若是事務使用此級別進行提交,事務操做都會讀到副本集中多數派已經提交的數據。
For "majority"
read concern, if the transaction commits with write concern 「majority」, transaction operations are guaranteed to have read majority-committed data. Otherwise, the "majority"
read concern provides no guarantees that read operations read majority-committed data.
針對快照級別的讀一致性,若是。。。。。【官方文檔這兒是否是寫錯了】,若是事務使用快照級別的讀一致性進行提交,那麼能夠保證事務中的操做讀到的都是事務開始時的多數派事務快照。
"snapshot"
read concern, if the transaction commits with write concern 「majority」, the transaction operations are guaranteed to have read from a snapshot of majority committed data. Otherwise, the "snapshot"
read concern provides no guarantee that read operations used a snapshot of majority-committed data.若是針對當前事務設置讀一致性,而不是對事務中的獨立操做設置一致性,那該一致性會覆蓋事務中全部的事務。並且在事務中,事務粒度的一致性會覆蓋集合級別,數據庫級別,客戶端級別的讀一致性。
You set the read concern at the transaction level, not at the individual operation level. The operations in the transaction will use the transaction-level read concern. Any read concern set at the collection and database level is ignored inside the transaction. If the transaction-level read concern is explicitly specified, the client level read concern is also ignored inside the transaction.
能夠在事務開始時對讀一致性進行設置。
You can set the transaction read concern at the transaction start.
若是缺省了事務級別的讀一致性設置,事務會繼承會話級別的讀一致性,若是會話級別也沒有設置讀一致性,那麼會繼續向上繼承客y戶端層級設置的讀一致性。
If unspecified at the transaction start, transactions use the session-level read concern or, if that is unset, the client-level read concern.
若是針對當前事務設置寫一致性,而不是對事務中的獨立操做設置一致性。那麼在提交時,事務會使用自身的一致性去提交寫操做。事務中各個獨立操做忽略寫一致性,請不要徒勞設置。(注:這段原文語義模糊,這裏認爲本段原文與上段原文寫法不一樣,是爲了和上段中的狀況進行區分)
You set the write concern at the transaction level, not at the individual operation level. At the time of the commit, transactions use the transaction level write concern to commit the write operations. Individual operations inside the transaction ignore write concerns. Do not explicitly set the write concern for the individual write operations inside transactions.
能夠在事務開始時對讀一致性進行設置。
You can set the write concern for the transaction commit at the transaction start.
若是缺省了事務級別的寫一致性設置,那麼事務在提交時會繼承會話級別的寫一致性,會話級別也缺省了設置的話,會繼續向上繼承客戶端級別的寫一致性。
If unspecified at the transaction start, transactions use the session-level write concern for the commit or, if that is unset, the client-level write concern.
不支持寫一致性設置爲w: 0
Write concern w: 0
is not supported for transactions.
若是使用 w: 1
進行提交,那麼該事務提交時只會確認本機oK,也就是PrimaryOK。若是主節點掛掉,且該事務沒有被傳輸到當時的次級節點。那麼,恭喜你,該事務大機率會被回滾掉。會形成數據庫端和應用端數據的不一致和數據缺失。
If you commit using w: 1
write concern, your transaction can be rolled back if there is a failover.
若是事務使用多數派寫一致性進行提交,且指定了快照級別的讀一致性。那麼事務操做會確保從事務開始時多數派已經提交的快照數據中進行讀取。(注:這段語義不明,官方文檔到底在表達什麼,是否是寫錯了)
If the transaction commits with write concern 「majority」 and has specified read concern "snapshot"
read concern, transaction operations are guaranteed to have read from a snapshot of majority-committed data. Otherwise, the "snapshot"
read concern provides no guarantees that read operations used a snapshot of majority-committed data.
若是事務使用多數派寫一致性進行提交,且指定了多數派級別的讀一致性,那麼事務中的操做會被確保從多數派已提交的數據中進行讀取。(注:這個是否是官方文檔編輯時複製粘貼錯了,把上面讀一致性的內容放過來了)
"majority"
read concern, transaction operations are guaranteed to have read majority-committed data. Otherwise, the"majority"
read concern provides no guarantees that read operations read majority-committed data.事務中的操做在獲取其所須要數據上的鎖時,默認等待超時時間爲5毫秒,超時後,當前事務會被回滾。(注:這個過短了吧,幸虧能夠手動設置)
By default, transactions waits 5
milliseconds to acquire locks required by the operations in the transaction. If the transaction cannot acquire its required locks within the 5
milliseconds, the transaction aborts.
提示:
若是在建立或者刪除集合後會緊接着開啓一個會對該集合進行操做的事務,那麼建立或者刪除集合的操做須要追加寫一致性(writeConcern)擴散到多數節點的參數以確保事務能夠成功獲取該集合相關的鎖。
(注,這段話可能較爲拗口。實際上是從集羣的一致性來考慮,和事務的讀一致性readConcern相關)
When creating or dropping a collection immediately before starting a transaction, if the collection is accessed within the transaction, issue the create or drop operation with write concern "majority"
to ensure that the transaction can acquire the required locks.
可使用 maxTransactionLockRequestTimeoutMillis
參數對鎖等待超時長度進行設置。
You can use the maxTransactionLockRequestTimeoutMillis
parameter to adjust how long transactions wait to acquire locks.
也能夠將 maxTransactionLockRequestTimeoutMillis
設爲-1
,以禁用鎖等待超時回滾,該事務會一直等待本身所須要的鎖被其餘事務或者會話釋放。
You can also use operation-specific timeout by setting maxTransactionLockRequestTimeoutMillis
to -1
.
Transactions release all locks upon abort or commit.
原文連接:MongoDB document:Transactions
翻譯:張銳志