开发者社区> 问答> 正文

mongodb有事务吗

mongodb有事务吗

展开
收起
社区秘书 2019-12-10 15:52:07 1010 0
1 条回答
写回答
取消 提交回答
  • MongoDB 4.0 引入的事务功能,支持多文档 ACID 特性,例如使用 mongo shell 进行事务操作。
    
    
    
    
    
    
    
    
    
    > 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()回滚事务)
    
    支持 MongoDB 4.0 的其他语言 Driver 也封装了事务相关接口,用户需要创建一个 Session,然后在 Session 上开启事务,提交事务。例如:
    
    python 版本
    
    
    
    
    
    
    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()
    
    java 版本
    
    
    
    
    
    
    
    try (ClientSession clientSession = client.startSession()) {
    
       clientSession.startTransaction();
    
       collection.insertOne(clientSession, docOne);
    
       collection.insertOne(clientSession, docTwo);
    
       clientSession.commitTransaction();
    
    }
    
    一、预备工作
    
    1、MongoDB需要4.0版本+
    
    2、需要自己搭建MongoDB复制集,单个mongodb server 不支持事务。
    
    事务原理:mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。mongodb各个节点常见的搭配方式为:一主一从、一主多从。主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
    
    3、搭建复制集步骤
    
    启动mongo主节点实例,bin目录下命令窗口执行,复制集命名为doudou, 8080端口的数据库文件位于db1目录下,--dbpath=路径写自己的,启动后勿关闭命令窗口
    
    
    mongod --replSet doudou --dbpath=E:\mongoDb\data\db1 --port=8080  
    
    启动mongo从节点实例,bin目录下命令窗口执行,复制集命名为doudou, 8081端口的数据库文件位于db2目录下,--dbpath=路径写自己的,启动后勿关闭命令窗口
    
    
    mongod --replSet doudou --dbpath=E:\mongoDb\data\db2 --port=8081
    
    两个节点启动后,bin目录下打开命令窗口,连接主节点
    
    
    mongo --port=8080
    
    命令初始化
    
    
    rs.initiate()
    
    两个节点启动后,bin目录下打开命令窗口,连接主节点
    
    
    mongo --port=8080
    
    成功后如图:
    
    10452633dc389ee4a43facd8deac7de.png
    
    命令初始化
    
    
    rs.initiate()
    
    效果如图:
    
    aab138a1a1be37dcdd47c37ec225f38.png
    
    成功的结果是(ok项是1,失败是0)
    
    查看是否是主节点 rs.isMaster()
    
    查看复制集状态 rs.status()
    
    初始化配置
    
    
    rs.conf()
    
    向主节点添加从节点
    
    
    rs.add("localhost:8081")
    
    查看主节点状态
    
    
    rs.status()
    
    复制集配置完成。
    
    二、复制集中的坑点
    
    1、需使用mongoose.connection对集合进行事务操作,其他model的CRUD方法不支持事务。
    
    
    mongoose.connection.collection('集合名') // 注:集合名需要小写且加s,如model为Cat,集合名这里应写为cats
    
    2、触发Schema定义的中间件默认值需要构造model实例
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    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类型。
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    // 解决方式
    
    //新增
    
      
    
    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函数
    
    
    
    
    
    
    
    
    
    
    
    // 获取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
    
      }
    
    执行事务逻辑
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    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();
    
    }
    
    2019-12-10 15:52:29
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
MongoDB多数据中心的方案选型之路 立即下载
阿里云MongoDB云服务构建 立即下载
饿了么高级架构师陈东明:MongoDB是如何逐步提高可靠性的 立即下载

相关镜像