mongodb 事务学习总结


mongodb 事务学习总结

 

一、预备工作

1.MongoDB需要4.0版本+

2.需要自己搭建MongoDB复制集,单个mongodb server 不支持事务。

事务原理:mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。mongodb各个节点常见的搭配方式为:一主一从、一主多从。主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

3.搭建复制集步骤

  • 启动mongo主节点实例,bin目录下命令窗口执行,复制集命名为doudou, 8080端口的数据库文件位于db1目录下,--dbpath=路径写自己的,启动后勿关闭命令窗口
    1 mongod --replSet doudou --dbpath=E:\mongoDb\data\db1 --port=8080  
  • 启动mongo从节点实例,bin目录下命令窗口执行,复制集命名为doudou, 8081端口的数据库文件位于db2目录下,--dbpath=路径写自己的,启动后勿关闭命令窗口
    1 mongod --replSet doudou --dbpath=E:\mongoDb\data\db2 --port=8081
  • 两个节点启动后,bin目录下打开命令窗口,连接主节点
    1 mongo --port=8080
  • 命令初始化
    1 rs.initiate()
  • 两个节点启动后,bin目录下打开命令窗口,连接主节点
    1 mongo --port=8080

    成功后如图:

  • 命令初始化

    1 rs.initiate()

    效果如图:

    成功的结果是(ok项是1,失败是0)

    查看是否是主节点 rs.isMaster()

    查看复制集状态 rs.status()

  • 初始化配置
    1 rs.conf()
  • 向主节点添加从节点
    1 rs.add("localhost:8081")
  • 查看主节点状态
    1 rs.status()

    复制集配置完成。

二、复制集中的坑点

1.需使用mongoose.connection对集合进行事务操作,其他model的CRUD方法不支持事务

1 mongoose.connection.collection('集合名'// 注:集合名需要小写且加s,如model为Cat,集合名这里应写为cats

2.触发Schema定义的中间件默认值需要构造model实例

1 2 3 4 5 6 7 8 9 10 11 12 13 14 const CatSchema = new Schema({     name: {         type: String         default'cat'     },     created: {      type: Date,      default: Date.now   } })   const Cat = mongoose.model('Cat', CatSchema)   new Cat() // 触发中间件

 3.insertOne,findOneAndUpdate等方法对数据的新增,需上面第二点进行依赖,否则直接insertOne 插入一条数据,定义的默认值不会触发,如created字段,chema内部定义的type: Schema.ObjectId的相应字段,insertOne插入后都会变成字符串类型,不是Schema.ObjectId类型

1 2 3 4 5 6 7 8 9 10 11 12 13 14 // 解决方式 //新增   const Cat= new Cat(); const data = {name: 5} for (let key in data) {       Cat[key] = data[key];     } db.collection('cats').insertOne(Cat);   // 查询修改   db.collection('cats') .findOneAndUpdate({_id: mongoose.Types.ObjectId(你的id)}, {$set: {name: 修改值}})

三、开始事务

注:以下皆为egg实例代码

  • 封装获取session函数
    1 2 3 4 5 6 7 8 9 10 // 获取session,回滚事务   async getSession(opt = {     readConcern: { level: "snapshot" },     writeConcern: { w: "majority" }   }) {     const { mongoose } = this.app     const session = await mongoose.startSession(opt);     await session.startTransaction();     return session   }
  • 执行事务逻辑

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const { mongoose } = this.ctx.app; const session = await this.ctx.getSession(); const db = mongoose.connection; try {   const data = this.ctx.request.body;   const Cat = new this.ctx.model.Cat();   for (let key in data) {     Cat[key] = data[key]   }   await db     .collection('cats')     .insertOne(Cat, { session });   // 提交事务   await session.commitTransaction();   this.ctx.end(); catch (err) {   // 回滚事务   await session.abortTransaction();   this.ctx.logger.error(new Error(err)); } finally {   await session.endSession(); }
with client.start_session() as s:
    s.start_transaction()
    collection_one.insert_one(doc_one, session=s)
    collection_two.insert_one(doc_two, session=s)
    s.commit_transaction()
try (ClientSession clientSession = client.startSession()) {
   clientSession.startTransaction();
   collection.insertOne(clientSession, docOne);
   collection.insertOne(clientSession, docTwo);
   clientSession.commitTransaction();
}
 
 
> s = db.getMongo().startSession()
session { "id" : UUID("3bf55e90-5e88-44aa-a59e-a30f777f1d89") }
> s.startTransaction()
> session.getDatabase("mytest").coll01.insert({x: 1, y: 1})
WriteResult({ "nInserted" : 1 })
> session.getDatabase("mytest").coll02.insert({x: 1, y: 1})
WriteResult({ "nInserted" : 1 })
> s.commitTransaction()  (或者 s.abortTransaction()回滚事务)