千家信息网

【MongoDB】4.0版本事务上手测试

发表于:2025-02-23 作者:千家信息网编辑
千家信息网最后更新 2025年02月23日,事务上手测试基础:原来3版本的只能叫单文档事务,即针对行的事务。所以没必要显式提供调用,多文档事务由于有损耗,照顾到性能的需求,提供了事务开启关闭的接口。多行,多集合,多库之间读肯定会牵扯到一致性读,
千家信息网最后更新 2025年02月23日【MongoDB】4.0版本事务上手测试

事务上手测试

基础:

原来3版本的只能叫单文档事务,即针对行的事务。所以没必要显式提供调用,多文档事务由于有损耗,照顾到性能的需求,提供了事务开启关闭的接口。
多行,多集合,多库之间读肯定会牵扯到一致性读,所以多文档事务肯定是有必要的。
4.2版本可能会支持分片系统多文档事务,4.0的多文档事务最多只面向复制集

准备

集合:

use dba;[10.240.129.36:30001_primary@dba]> db.coll_2.find()[10.240.129.36:30001_primary@dba]> db.coll_1.find(){ "_id" : ObjectId("5b371e120d6160b73a9fcee6"), "test" : true }{ "_id" : ObjectId("5b371e6d0d6160b73a9fcee7"), "test" : true }

精简命令:

mysql">// 显式开启一个会话(难道通过shell链接来本身不就是会话吗?)session=db.getMongo().startSession()// 显式开启事务session.startTransaction()// 获取表对象coll_1=session.getDatabase('dba').coll_1coll_2=session.getDatabase('dba').coll_2;// 使用表对象的插入函数进行插入coll_1.insertOne({'not_test':false})coll_2.insertOne({'not_test':false})// 提交事务session.commitTransaction()// 回滚事务session.abortTransaction()// 检查事务准确性db.coll_1.find()db.coll_2.find()

超时参数怎么设置?

默认事务生命周期为60秒,超出后会被自动清理

// The minimum value for transactionLifetimeLimitSeconds is 1 second.db.adminCommand( { setParameter: 1, transactionLifetimeLimitSeconds: 30 } )// You can also set parameter transactionLifetimeLimitSeconds at startup time.// 可以考虑写在配置文件中mongod --setParameter "transactionLifetimeLimitSeconds=30"

第一次尝试

因为忘记设置后向兼容性,执行失败,在同一个shell中进行设置后,再次插入,提示事务不存在

[10.240.129.36:30001_primary@dba]> Session.startTransactions()2018-06-30T14:17:10.670+0800 E QUERY    [js] ReferenceError: Session is not defined :@(shell):1:1[10.240.129.36:30001_primary@dba]> db.getMongo()connection to 127.0.0.1:30001[10.240.129.36:30001_primary@dba]> db.getMongo().startSession()session { "id" : UUID("887f4564-4f8a-4783-886d-a55a536f41aa") }[10.240.129.36:30001_primary@dba]> session=db.getMongo().startSession()session { "id" : UUID("9b3e10b9-93cd-4010-b1a3-0e7b49796db3") }[10.240.129.36:30001_primary@dba]> session.startTransaction()[10.240.129.36:30001_primary@dba]> coll_1=session.getDatabase('dba').coll_1;dba.coll_1[10.240.129.36:30001_primary@dba]> coll_2=session.getDatabase('dba').coll_2;dba.coll_2[10.240.129.36:30001_primary@dba]> coll_1.insertOne({'not_test':false})// 后向兼容性要求为4,也就是说想开启事务必须是mongod4以上的版本2018-06-30T14:28:55.708+0800 E QUERY    [js] WriteCommandError: Transactions are only supported in featureCompatibilityVersion 4.0. See http://dochub.mongodb.org/core/4.0-feature-compatibility for more information. :WriteCommandError({    "ok" : 0,    "errmsg" : "Transactions are only supported in featureCompatibilityVersion 4.0. See http://dochub.mongodb.org/core/4.0-feature-compatibility for more information.",    "code" : 50773,    "codeName" : "Location50773"})WriteCommandError@src/mongo/shell/bulk_api.js:420:48Bulk/executeBatch@src/mongo/shell/bulk_api.js:902:1Bulk/this.execute@src/mongo/shell/bulk_api.js:1150:21DBCollection.prototype.insertOne@src/mongo/shell/crud_api.js:252:9@(shell):1:1[10.240.129.36:30001_primary@dba]> db.adminCommand({setFeatureCompatibilityVersion:'4.0'}){ "ok" : 1 }// 可以看到,报错,重新设置版本兼容性后,原先的事务出现了问题[10.240.129.36:30001_primary@dba]> coll_1.insertOne({'not_test':false})2018-06-30T14:31:16.706+0800 E QUERY    [js] WriteCommandError: Given transaction number 0 does not match any in-progress transactions. :WriteCommandError({    "errorLabels" : [        "TransientTransactionError"    ],    "ok" : 0,    "errmsg" : "Given transaction number 0 does not match any in-progress transactions.",    "code" : 251,    "codeName" : "NoSuchTransaction"})WriteCommandError@src/mongo/shell/bulk_api.js:420:48Bulk/executeBatch@src/mongo/shell/bulk_api.js:902:1Bulk/this.execute@src/mongo/shell/bulk_api.js:1150:21DBCollection.prototype.insertOne@src/mongo/shell/crud_api.js:252:9@(shell):1:1// 事务怎么也调不好了[10.240.129.36:30001_primary@dba]> session.startTransaction()2018-06-30T14:33:53.293+0800 E QUERY    [js] Error: Transaction already in progress on this session. :startTransaction@src/mongo/shell/session.js:732:1startTransaction@src/mongo/shell/session.js:925:17@(shell):1:1[10.240.129.36:30001_primary@dba]> db.getMongo().abortTransaction[10.240.129.36:30001_primary@dba]> db.getMongo().abortTransaction[10.240.129.36:30001_primary@dba]> db.getMongo().abortTransaction[10.240.129.36:30001_primary@dba]> db.getMongo().abortTransaction[10.240.129.36:30001_primary@dba]> db.getMongo().abortTransaction[10.240.129.36:30001_primary@dba]>[10.240.129.36:30001_primary@dba]> db.getMongo().abortTransaction[10.240.129.36:30001_primary@dba]> db.getMongo().abortTransaction[10.240.129.36:30001_primary@dba]> exit// 直接退出当前连接

第二次尝试:

[10.240.129.36:30001_primary@admin]> session=db.getMongo().startSession()session { "id" : UUID("b82d71b0-b698-4909-b5b8-a7845dba98b2") }[10.240.129.36:30001_primary@admin]>  session.startTransaction()[10.240.129.36:30001_primary@admin]> coll_1=session.getDatabase('dba').coll_1dba.coll_1[10.240.129.36:30001_primary@admin]> coll_2=session.getDatabase('dba').coll_2;dba.coll_2[10.240.129.36:30001_primary@admin]> coll_1.insertOne({'not_test':false}){    "acknowledged" : true,    "insertedId" : ObjectId("5b37270429c5c0c486b4b17b")}[10.240.129.36:30001_primary@admin]> coll_2.insertOne({'not_test':false})// 这里的事务回滚可能是因为超时引起的,默认事务生命周期最大值为1分钟2018-06-30T15:00:50.629+0800 E QUERY    [js] WriteCommandError: Transaction 0 has been aborted. :WriteCommandError({    "errorLabels" : [        "TransientTransactionError"    ],    "ok" : 0,    "errmsg" : "Transaction 0 has been aborted.",    "code" : 251,    "codeName" : "NoSuchTransaction"})WriteCommandError@src/mongo/shell/bulk_api.js:420:48Bulk/executeBatch@src/mongo/shell/bulk_api.js:902:1Bulk/this.execute@src/mongo/shell/bulk_api.js:1150:21DBCollection.prototype.insertOne@src/mongo/shell/crud_api.js:252:9@(shell):1:1[10.240.129.36:30001_primary@admin]> coll_2.insertOne({'not_test':false})2018-06-30T15:00:59.308+0800 E QUERY    [js] WriteCommandError: Transaction 0 has been aborted. :WriteCommandError({    "errorLabels" : [        "TransientTransactionError"    ],    "ok" : 0,    "errmsg" : "Transaction 0 has been aborted.",    "code" : 251,    "codeName" : "NoSuchTransaction"})WriteCommandError@src/mongo/shell/bulk_api.js:420:48Bulk/executeBatch@src/mongo/shell/bulk_api.js:902:1Bulk/this.execute@src/mongo/shell/bulk_api.js:1150:21DBCollection.prototype.insertOne@src/mongo/shell/crud_api.js:252:9@(shell):1:1// 事务超时回滚之后,应该还可以重开新的事务才对,这里老是报错[10.240.129.36:30001_primary@admin]> session.startTransaction()2018-06-30T15:01:04.932+0800 E QUERY    [js] Error: Transaction already in progress on this session. :startTransaction@src/mongo/shell/session.js:732:1startTransaction@src/mongo/shell/session.js:925:17@(shell):1:1// 内部已经回滚掉了,结果竟然需要我手动回滚才行,晕[10.240.129.36:30001_primary@admin]> session.abortTransaction()[10.240.129.36:30001_primary@admin]> session.startTransaction()[10.240.129.36:30001_primary@admin]> coll_1.insertOne({'not_test':false}){    "acknowledged" : true,    "insertedId" : ObjectId("5b372ac229c5c0c486b4b17e")}[10.240.129.36:30001_primary@admin]> coll_2.insertOne({'not_test':false}){    "acknowledged" : true,    "insertedId" : ObjectId("5b372ac529c5c0c486b4b17f")}// 试试能不能进行explain操作[10.240.129.36:30001_primary@admin]> coll_1.explain().find({'test_explain':1})2018-06-30T15:03:09.375+0800 E QUERY    [js] Error: explain failed: {    "ok" : 0,    "errmsg" : "Cannot run 'explain' in a multi-document transaction.",    "code" : 50767,    "codeName" : "Location50767"} :  // 果然是报错了呢,在事务中不能进行explain操作_getErrorWithCode@src/mongo/shell/utils.js:25:13throwOrReturn@src/mongo/shell/explainable.js:31:1constructor/this.finish@src/mongo/shell/explain_query.js:171:24constructor/this.shellPrint@src/mongo/shell/explain_query.js:210:26shellPrintHelper@src/mongo/shell/utils.js:594:1@(shell2):1:1// 尝试提交时报错[10.240.129.36:30001_primary@admin]> session.commitTransaction()2018-06-30T15:04:21.985+0800 E QUERY    [js] Error: command failed: {    "errorLabels" : [        "TransientTransactionError"    ],    "ok" : 0,    "errmsg" : "Transaction 1 has been aborted.",    "code" : 251,    "codeName" : "NoSuchTransaction"} :_getErrorWithCode@src/mongo/shell/utils.js:25:13doassert@src/mongo/shell/assert.js:18:14_assertCommandWorked@src/mongo/shell/assert.js:534:17assert.commandWorked@src/mongo/shell/assert.js:618:16commitTransaction@src/mongo/shell/session.js:929:17@(shell):1:1[10.240.129.36:30001_primary@dba]> db.coll_1.find(){ "_id" : ObjectId("5b371e120d6160b73a9fcee6"), "test" : true }{ "_id" : ObjectId("5b371e6d0d6160b73a9fcee7"), "test" : true }[10.240.129.36:30001_primary@dba]> db.coll_2.find()// 这边可以看到,插入语句并没有真正被执行[10.240.129.36:30001_primary@dba]> session.commitTransaction()2018-06-30T15:05:46.288+0800 E QUERY    [js] Error: command failed: {    "errorLabels" : [        "TransientTransactionError"    ],    "ok" : 0,    "errmsg" : "Transaction 1 has been aborted.",    "code" : 251,    "codeName" : "NoSuchTransaction"} :_getErrorWithCode@src/mongo/shell/utils.js:25:13doassert@src/mongo/shell/assert.js:18:14_assertCommandWorked@src/mongo/shell/assert.js:534:17assert.commandWorked@src/mongo/shell/assert.js:618:16commitTransaction@src/mongo/shell/session.js:929:17@(shell):1:1// 这儿又抽风了,内部回滚之后禁止提交操作,尝试进行回滚,结果说已经提交了,不能回滚,真是晕死[10.240.129.36:30001_primary@dba]> session.abortTransaction()2018-06-30T15:06:04.162+0800 E QUERY    [js] Error: Cannot call abortTransaction after calling commitTransaction. :abortTransaction@src/mongo/shell/session.js:764:1abortTransaction@src/mongo/shell/session.js:934:17@(shell):1:1

第三次尝试(成功提交):

[10.240.129.36:30001_primary@admin]> session=db.getMongo().startSession()session { "id" : UUID("e7935985-160d-4969-adfe-3bae401e155f") }[10.240.129.36:30001_primary@admin]> session.startTransaction()[10.240.129.36:30001_primary@admin]> coll_1=session.getDatabase('dba').coll_1dba.coll_1[10.240.129.36:30001_primary@admin]> coll_2=session.getDatabase('dba').coll_2;dba.coll_2[10.240.129.36:30001_primary@admin]> coll_1.insertOne({'not_test':false}){    "acknowledged" : true,    "insertedId" : ObjectId("5b373202d0b208b7386e9c20")}[10.240.129.36:30001_primary@admin]> coll_2.insertOne({'not_test':false}){    "acknowledged" : true,    "insertedId" : ObjectId("5b373202d0b208b7386e9c21")}[10.240.129.36:30001_primary@admin]> session.commitTransaction()[10.240.129.36:30001_primary@admin]> use dbaswitched to db dba[10.240.129.36:30001_primary@dba]> db.coll_1.find(){ "_id" : ObjectId("5b371e120d6160b73a9fcee6"), "test" : true }{ "_id" : ObjectId("5b371e6d0d6160b73a9fcee7"), "test" : true }{ "_id" : ObjectId("5b373202d0b208b7386e9c20"), "not_test" : false }[10.240.129.36:30001_primary@dba]> db.coll_2.find(){ "_id" : ObjectId("5b373202d0b208b7386e9c21"), "not_test" : false }

第四次尝试(测试回滚):

[10.240.129.36:30001_primary@admin]> session=db.getMongo().startSession()session { "id" : UUID("e7935985-160d-4969-adfe-3bae401e155f") }[10.240.129.36:30001_primary@admin]> session.startTransaction()[10.240.129.36:30001_primary@admin]> coll_1=session.getDatabase('dba').coll_1dba.coll_1[10.240.129.36:30001_primary@admin]> coll_2=session.getDatabase('dba').coll_2;dba.coll_2[10.240.129.36:30001_primary@admin]> coll_1.insertOne({'not_test':false}){    "acknowledged" : true,    "insertedId" : ObjectId("5b373202d0b208b7386e9c20")}[10.240.129.36:30001_primary@admin]> coll_2.insertOne({'not_test':false}){    "acknowledged" : true,    "insertedId" : ObjectId("5b373202d0b208b7386e9c21")}[10.240.129.36:30001_primary@admin]> session.commitTransaction()[10.240.129.36:30001_primary@admin]> use dbaswitched to db dba[10.240.129.36:30001_primary@dba]> db.coll_1.find(){ "_id" : ObjectId("5b371e120d6160b73a9fcee6"), "test" : true }{ "_id" : ObjectId("5b371e6d0d6160b73a9fcee7"), "test" : true }{ "_id" : ObjectId("5b373202d0b208b7386e9c20"), "not_test" : false }[10.240.129.36:30001_primary@dba]> db.coll_2.find(){ "_id" : ObjectId("5b373202d0b208b7386e9c21"), "not_test" : false }[10.240.129.36:30001_primary@dba]> session=db.getMongo().startSession()session { "id" : UUID("cc0e2d23-1766-4ea5-87a5-957efcae28ad") }[10.240.129.36:30001_primary@dba]> session.startTransaction()[10.240.129.36:30001_primary@dba]> coll_1=session.getDatabase('dba').coll_1dba.coll_1[10.240.129.36:30001_primary@dba]> coll_2=session.getDatabase('dba').coll_2;dba.coll_2[10.240.129.36:30001_primary@dba]> coll_1.insertOne({'not_test':false}){    "acknowledged" : true,    "insertedId" : ObjectId("5b3732f0d0b208b7386e9c22")}[10.240.129.36:30001_primary@dba]> coll_2.insertOne({'not_test':false}){    "acknowledged" : true,    "insertedId" : ObjectId("5b3732f1d0b208b7386e9c23")}[10.240.129.36:30001_primary@dba]> session.abortTransaction()[10.240.129.36:30001_primary@dba]> db.coll_2.find(){ "_id" : ObjectId("5b373202d0b208b7386e9c21"), "not_test" : false }[10.240.129.36:30001_primary@dba]> db.coll_1.find(){ "_id" : ObjectId("5b371e120d6160b73a9fcee6"), "test" : true }{ "_id" : ObjectId("5b371e6d0d6160b73a9fcee7"), "test" : true }{ "_id" : ObjectId("5b373202d0b208b7386e9c20"), "not_test" : false }

注意

默认事务超时时间有点短,1分钟
默认锁等待时间短,5毫秒
事务的操作有些繁琐,不如RDS的 begin commit rollback方便(指SHELL操作)
一个事务的对应的oplog不能超过16M(指BSON格式的文件大小)
显式事务功能的开启对WT缓存提出了新的要求。类似于IBP事务不按时提交,类似undo用于存放快照或者旧版本的东西肯定会占用大量WT的空间。

0