WithTransaction MongoDB

Các thành viên

id SessionId

Id máy chủ được liên kết với phiên này

phương pháp

abortTransaction ( gọi lại ){Promise}

node_modules/mongodb-core/lib/sessions. js, dòng 229

Hủy bỏ giao dịch hiện đang hoạt động trong phiên này

NameTypeDescription_______8 chức năngkhông bắt buộc

gọi lại tùy chọn để hoàn thành hoạt động này

trả lại
lời hứa được trả lại nếu không có cuộc gọi lại nào được cung cấp

advanceOperationTime (thời gian hoạt động)

node_modules/mongodb-core/lib/sessions. js, dòng 142

Nâng cao thời gian hoạt động cho ClientSession

NameTypeDescription_______9 Dấu thời gian

mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
0 của loại hoạt động mà nó muốn chuyển sang

commitTransaction ( gọi lại ){Promise}

node_modules/mongodb-core/lib/sessions. js, dòng 208

Cam kết giao dịch hiện đang hoạt động trong phiên này

NameTypeDescription_______8 chức năngtùy chọn

gọi lại tùy chọn để hoàn thành hoạt động này

trả lại
lời hứa được trả lại nếu không cung cấp lệnh gọi lại

endSession ( tùy chọn, callback)

node_modules/mongodb-core/lib/sessions. js, dòng 112

Kết thúc phiên này trên máy chủ

NameTypeDescription_______12 Đối tượngtùy chọn

cài đặt tùy chọn. Hiện đang được bảo lưu để sử dụng trong tương lai

callback chức năngtùy chọn

Gọi lại tùy chọn để hoàn thành thao tác này

bằng (phiên){boolean}

node_modules/mongodb-core/lib/sessions. js, dòng 158

Được sử dụng để xác định xem phiên này có bằng phiên khác không

NameTypeDescription_______14 ClientSession
trả lại
nếu các phiên bằng nhau

số lượng giao dịch tăng ()

node_modules/mongodb-core/lib/sessions. js, dòng 169

Tăng số giao dịch trên ServerSession nội bộ

inTransaction (){boolean}

node_modules/mongodb-core/lib/sessions. js, dòng 176

trả lại
phiên này hiện có trong giao dịch hay không

bắt đầu Giao dịch (tùy chọn)

node_modules/mongodb-core/lib/sessions. js, dòng 185

Bắt đầu một giao dịch mới với các tùy chọn đã cho

NameTypeDescription_______12 Tùy chọn giao dịch

Tùy chọn giao dịch

withTransaction (fn, tùy chọn )

node_modules/mongodb-core/lib/sessions. js, dòng 271

Chạy lambda được cung cấp trong một giao dịch, thử lại thao tác cam kết
hoặc toàn bộ giao dịch nếu cần (và khi lỗi cho phép) để đảm bảo tốt hơn rằng
the transaction can complete successfully.

QUAN TRỌNG. Phương pháp này yêu cầu người dùng trả lại một Lời hứa, tất cả các lambda không
trả lại một Lời hứa sẽ dẫn đến hành vi không xác định.

NameTypeDescription_______16 WithTransactionCallback
mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
2 Tùy chọn giao dịchtùy chọn

Cài đặt tùy chọn cho giao dịch

Các giao dịch là mới trong MongoDB nhưng đã tồn tại trong cơ sở dữ liệu SQL hơn 30 năm. Các giao dịch được sử dụng để duy trì tính nhất quán và chính xác trong các hệ thống cơ sở dữ liệu chịu sự thay đổi đồng thời do nhiều người dùng đưa ra

Các giao dịch thường dẫn đến tính nhất quán được cải thiện với chi phí giảm đồng thời. Do đó, các giao dịch có ảnh hưởng lớn đến hiệu suất cơ sở dữ liệu

Bài đăng này không nhằm mục đích hướng dẫn về giao dịch. Để tìm hiểu cách lập trình giao dịch, hãy xem phần thủ công MongoDB về giao dịch[1]. Trong bài đăng này, chúng tôi sẽ tập trung vào việc tối đa hóa thông lượng giao dịch và giảm thiểu thời gian chờ giao dịch

Giao dịch thoáng quaLỗi

Không giống như nhiều cơ sở dữ liệu giao dịch khác như MySQL hay Oracle, MongoDB không sử dụng khóa chặn để ngăn xung đột giữa các giao dịch. Thay vào đó, nó phát hành

mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
8 để hủy bỏ các giao dịch có thể gây xung đột

Hình bên dưới minh họa mô hình MongoDB. Khi một phiên cập nhật một tài liệu, nó sẽ không khóa nó. Tuy nhiên, nếu phiên thứ hai cố gắng sửa đổi tài liệu trong một giao dịch, một

mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
9 sẽ được cấp

Đây là một số mã minh họa mô hình TransientTransactionError. Đoạn mã tạo hai phiên, mỗi phiên nằm trong giao dịch riêng của nó. Sau đó, chúng tôi cố gắng cập nhật cùng một tài liệu trong mỗi giao dịch

var session1=db.getMongo().startSession();
var session2=db.getMongo().startSession();
var session1Collection=session1.getDatabase(db.getName()).transTest;
var session2Collection=session2.getDatabase(db.getName()).transTest;
session1.startTransaction();
session2.startTransaction();
session1Collection.update({_id:1},{$set:{value:1}});
session2Collection.update({_id:1},{$set:{value:2}});
session1.commitTransaction();
session2.commitTransaction();

Khi gặp câu lệnh cập nhật thứ hai, MongoDB sẽ báo lỗi

mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
Giao dịch trong trình điều khiển MongoDB

Từ MongoDB 4. 2 trở đi, trình điều khiển MongoDB ẩn

async function myTransaction(session, db, 
fromAcc, toAcc, dollars) {
try {
await session.withTransaction(async () => {
await db.collection('accounts').
updateOne({ _id: fromAcc },
{ $inc: { balance: -1*dollars } },
{ session });
await db.collection('accounts').
updateOne({ _id: toAcc },
{ $inc: { balance: dollars } },
{ session });
}, transactionOptions);
} catch (error) {
console.log(error.message);
}
}
0 khỏi bạn, bằng cách tự động thử lại giao dịch. Chẳng hạn, bạn có thể chạy đồng thời nhiều bản sao của mã NodeJS này mà không gặp phải bất kỳ
mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
8 nào

async function myTransaction(session, db, 
fromAcc, toAcc, dollars) {
try {
await session.withTransaction(async () => {
await db.collection('accounts').
updateOne({ _id: fromAcc },
{ $inc: { balance: -1*dollars } },
{ session });
await db.collection('accounts').
updateOne({ _id: toAcc },
{ $inc: { balance: dollars } },
{ session });
}, transactionOptions);
} catch (error) {
console.log(error.message);
}
}

Trình điều khiển NodeJS — và trình điều khiển cho các ngôn ngữ khác như Java, Python, Go, v.v. , — tự động xử lý mọi

mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
8 và gửi lại mọi giao dịch bị hủy bỏ. Tuy nhiên, các lỗi vẫn do máy chủ MongoDB gây ra và bạn có thể thấy chúng được ghi lại trong nhật ký MongoDB

~$ grep -i 'assertion.*writeconflict' \
/usr/local/var/log/mongodb/mongo.log \
|tail -1|jq

{
"t": {
"$date": "2020-08-08T14:04:47.643+10:00"
},

"msg": "Assertion while executing command",
"attr": {
"command": "update",
"db": "MongoDBTuningBook",
"commandArgs": {
"update": "transTest",
"updates": [
{
"q": {
"_id": 1
},
"u": {
"$inc": {
"value": 2
}
},
"upsert": false,
"multi": false
}
],
/* Other transaction information */
},
"error": "WriteConflict: WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction."
}
}

Ở cấp độ toàn cầu, các lần thử lại có thể nhìn thấy trong bộ đếm

mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
1
mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
2

function txnCounts() {
var ssTxns = db.serverStatus().transactions;
print(ssTxns.totalStarted + 0, 'transactions started');
print(ssTxns.totalAborted + 0, 'transactions aborted');
print(ssTxns.totalCommitted + 0, 'transactions commited');
print(Math.round(ssTxns.totalAborted * 100 /
ssTxns.totalStarted) + '% txns aborted');
}
mongo> txnCounts();
203628 transactions started
167989 transactions aborted
35639 transactions commited
82% txns aborted
Ý nghĩa về hiệu suất của TransientTransactionErrors

Các lần thử lại từ

mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
8 rất tốn kém — chúng không chỉ liên quan đến việc loại bỏ bất kỳ công việc nào đã thực hiện trong giao dịch cho đến nay mà còn hoàn nguyên trạng thái cơ sở dữ liệu trở lại lúc bắt đầu giao dịch. Tác động của việc thử lại giao dịch nhiều hơn bất kỳ thứ gì khác khiến giao dịch MongoDB trở nên đắt đỏ. Biểu đồ bên dưới cho thấy khi tỷ lệ hủy bỏ giao dịch tăng lên, thời gian trôi qua cho các giao dịch giảm xuống nhanh chóng

Giảm tác động của lỗi giao dịch nhất thời

Cho rằng các lần thử lại của

mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
9 có ảnh hưởng nghiêm trọng đến hiệu suất giao dịch, nên chúng ta cần phải làm bất cứ điều gì có thể để giảm thiểu các lần thử lại này. Có một vài chiến lược mà chúng ta có thể sử dụng

  • Tránh một giao dịch hoàn toàn
  • Phân vùng các tài liệu “nóng” có mức độ xung đột ghi cao
  • Thứ tự các hoạt động để giảm thiểu số lượng các hoạt động xung đột

Bạn có thể tránh các giao dịch theo một số cách, có thể bằng cách đặt các mục liên quan vào một tài liệu duy nhất để bạn có thể cập nhật chúng một cách nguyên tử. Bạn có thể giảm tranh chấp tài liệu bằng cách phân vùng dữ liệu trên nhiều tài liệu. Trong cuốn sách của chúng tôi, chúng tôi đưa ra một vài ví dụ về những kỹ thuật này

Tùy chọn thứ ba là thay đổi thứ tự các hành động trong giao dịch. Cách tiếp cận này có thể mang lại hiệu quả bất ngờ

Ví dụ: xem xét giao dịch sau

await session.withTransaction(async () => {
await db.collection('txnTotals').
updateOne({ _id: 1 },
{ $inc: { counter: 1 } },
{ session });
await db.collection('accounts').
updateOne({ _id: fromAcc },
{ $inc: { balance: -1*dollars } },
{ session });
await db.collection('accounts').
updateOne({ _id: toAcc },
{ $inc: { balance: dollars } },
{ session });
}, transactionOptions);;

Giao dịch này chuyển tiền giữa hai tài khoản, nhưng trước tiên, nó cập nhật một “bộ đếm giao dịch” toàn cầu. Mọi giao dịch cố gắng phát hành giao dịch này sẽ cố gắng cập nhật bộ đếm này và kết quả là nhiều giao dịch sẽ gặp phải lần thử lại

mongo>session1Collection.update({_id:1},{$set:{value:1}});
WriteCommandError({
"errorLabels" : [
"TransientTransactionError"
],
"operationTime" : Timestamp(1596785629, 1),
"ok" : 0,
"errmsg" : "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.",
"code" : 112,
"codeName" : "WriteConflict",
9

Nếu chúng ta di chuyển tuyên bố gây tranh cãi đến cuối giao dịch, thì khả năng xảy ra lỗi TransientTransactionError sẽ giảm đi, vì khoảng thời gian xung đột sẽ giảm xuống một vài thời điểm cuối cùng trong quá trình thực hiện giao dịch. Dưới đây chúng ta thấy hiệu quả của việc tối ưu hóa này

Cân nhắc việc đặt các hoạt động “nóng” — những hoạt động thường gặp phải TransientTransactionErrors — cuối cùng trong các giao dịch của bạn để giảm khoảng thời gian xung đột

Phần kết luận

Giao dịch là một bổ sung thực sự hữu ích cho MongoDB, nhưng chúng có tác động đến hiệu suất. Cụ thể, việc thử lại giao dịch có thể có tác động tiêu cực đáng kể đến thông lượng. Trong bài đăng này, chúng tôi đã thảo luận về một số tùy chọn để giảm tác động của giao dịch

Để biết thêm thông tin, hãy thử cuốn sách của chúng tôi Điều chỉnh hiệu suất MongoDB — Chương 9 dành riêng cho việc sử dụng và điều chỉnh các giao dịch

Guy Harrison và Michael Harrison là tác giả của MongoDB Performance Tuning (Apress, 2021). Cả hai đều làm việc tại ProvenDB, trong số những thứ khác, mang lại sự bất biến và tin cậy của các Chuỗi khối công cộng cho sức mạnh và năng suất của MongoDB

MongoDB có thể được sử dụng cho các ứng dụng giao dịch không?

MongoDB luôn cung cấp đảm bảo giao dịch cho các hoạt động trên một tài liệu . nguyên tử. Các hoạt động tài liệu đơn lẻ luôn là nguyên tử trong MongoDB. Các thao tác này có thể ghi vào một hoặc nhiều trường, bao gồm các tài liệu con, các phần tử trong một mảng và thậm chí cả các mảng lồng nhau.

Làm cách nào để sử dụng các giao dịch trong MongoDB?

Các giao dịch trong MongoDB tuân thủ các nguyên tắc ACID này và có thể được sử dụng một cách đáng tin cậy trong các trường hợp cần thay đổi nhiều tài liệu trong một lần. .
Bước 1 — Chuyển đổi phiên bản MongoDB độc lập của bạn thành một bộ bản sao. .
Bước 2 — Chuẩn bị dữ liệu mẫu. .
Bước 3 — Tạo giao dịch hoàn chỉnh đầu tiên của bạn

Tôi có nên sử dụng giao dịch trong MongoDB không?

Đối với các tình huống yêu cầu tính nguyên tử của việc đọc và ghi vào nhiều tài liệu (trong một hoặc nhiều bộ sưu tập), MongoDB hỗ trợ giao dịch nhiều tài liệu . Với các giao dịch phân tán, các giao dịch có thể được sử dụng trên nhiều hoạt động, bộ sưu tập, cơ sở dữ liệu, tài liệu và phân đoạn.

Tại sao MongoDB không phải là giao dịch?

Không có giao dịch là một sự đánh đổi để cho phép MongoDB có thể mở rộng . Mục đích của giao dịch là đảm bảo rằng toàn bộ cơ sở dữ liệu luôn nhất quán trong khi nhiều hoạt động diễn ra. Nhưng trái ngược với hầu hết các cơ sở dữ liệu quan hệ, MongoDB không được thiết kế để chạy trên một máy chủ duy nhất.