分享
  1. 首页
  2. 文章

mgo和mongo-go-driver使用心得比较

sixgo · · 3911 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

mgo和mongo-go-driver比较

库介绍

  • mgo:是MongoDB的Go语言驱动,它用基于Go语法的简单API实现了丰富的特性,并经过良好测试。使用起来很顺手,文档足够,前期一直在使用,可惜是不维护了;

  • mongo-go-driver:官方的驱动,设计的很底层,从mgo转来的时候不是很顺手,主要是使用事务;

用法介绍

连接数据库

  • mgo

    import ( 
    "gopkg.in/mgo.v2" 
    "gopkg.in/mgo.v2/bson" 
    )
    session, err := mgo.Dial("127.0.0.1:27017")
  • mongo-go-driver

    import (
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    )
    client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))

两者在数据库的连接上都很简单,后者使用了options,可以设置连接数,连接时间,socket时间,超时时间等;

索引

  • mgo

    func createUniqueIndex(collection string, keys ...string) {
     ms, c := Connect(setting.DatabaseSetting.DBName, collection)
     defer ms.Close()
     // 设置统计表唯一索引
     index := mgo.Index{
     Key: keys, // 索引键
     Unique: true, // 唯一索引
     DropDups: true, // 存在数据后创建, 则自动删除重复数据
     Background: true, // 不长时间占用写锁
     }
     // 创建索引
     err := c.EnsureIndex(index)
     if err != nil {
     Logger.Error("EnsureIndex error", zap.String("error", err.Error()))
     }
    }
  • mongo-go-driver

    func createUniqueIndex(collection string, keys ...string) {
     db := DB.Mongo.Database(setting.DatabaseSetting.DBName).Collection(collection)
     opts := options.CreateIndexes().SetMaxTime(10 * time.Second)
     indexView := db.Indexes()
     keysDoc := bsonx.Doc{}
     
     // 复合索引
     for _, key := range keys {
     if strings.HasPrefix(key, "-") {
     keysDoc = keysDoc.Append(strings.TrimLeft(key, "-"), bsonx.Int32(-1))
     } else {
     keysDoc = keysDoc.Append(key, bsonx.Int32(1))
     }
     }
     // 创建索引
     result, err := indexView.CreateOne(
     context.Background(),
     mongo.IndexModel{
     Keys: keysDoc,
     Options: options.Index().SetUnique(true),
     },
     opts,
     )
     if result == "" || err != nil {
     Logger.Error("EnsureIndex error", zap.String("error", err.Error()))
     }
    }

mgo可以直接构建复合索引,按顺序传入多个参数就可以createIndex(addrTrxC, "address_id", "asset_id"),但是mongo-go-driver,需要自己做一下处理

查询

  • mgo

    func FindProNode() ([]DBNode, error) {
     var nodes []DBNode
     ms, c := Connect(setting.DatabaseSetting.DBName, superNodeC)
     defer ms.Close()
     err := c.Find(M{}).Sort("-vote_count").Limit(10).All(&nodes)
     if err != nil {
     return nil, err
     }
     return nodes, nil
    }
  • mongo-go-driver

    func FindNodes() ([]DBNode, error) {
     var nodes []DBNode
     c := Connect(setting.DatabaseSetting.DBName, superNodeC)
     ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
     defer cancel()
     opts := options.Find().SetSort(bsonx.Doc{{"vote_count", bsonx.Int32(-1)}})
     cursor, err := c.Find(ctx, M{}, opts)
     if err != nil {
     return nil, err
     }
     for cursor.Next(context.Background()) {
     var node DBNode
     if err = cursor.Decode(&node); err != nil {
     return nil, err
     } else {
     nodes = append(nodes, node)
     }
     }
     return nodes, nil
    }

在查询单个元素的时候,两个驱动都都方便,但是,当查询多个元素的时候,mongo-go-driver不能直接解析到数组,需要借助cursor这个类型,遍历解析所有的数据(麻烦,需要自己封装)。

插入

  • mgo

    // 通用
    func Insert(db, collection string, docs ...interface{}) error {
     ms, c := Connect(db, collection)
     defer ms.Close()
     return c.Insert(docs...)
    }
    //插入
    func InsertNode(node DBNode) error {
     err := Insert(setting.DatabaseSetting.DBName, superNodeC, node)
     return err
    }
  • mongo-go-driver

    func Insert(db, collection string, docs ...interface{}) (*mongo.InsertManyResult, error) {
     c := Connect(db, collection)
     ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
     defer cancel()
     return c.InsertMany(ctx, docs)
    }
    func InsertNode(node DBNode) error {
     _, err := Insert(setting.DatabaseSetting.DBName, superNodeC, node)
     return err
    }

插入的区别也不是很大

修改

  • mgo

    func UpsertNode(node DBNode) (*mgo.ChangeInfo, error) {
     findM := M{"pub_key": node.PubKey}
     ms, c := Connect(setting.DatabaseSetting.DBName, superNodeC)
     defer ms.Close()
     updateM := bson.M{"$inc": M{"vote_count": node.VoteCount},
     "$set": M{
     "name": node.Name,
     "address": node.Address,
     "first_timestamp": node.FirstTimestamp}}
     return c.Upsert(findM, updateM)
    }
  • mongo-go-driver

    func Update(db, collection string, query, update interface{}) (*mongo.UpdateResult, error) {
     c := Connect(db, collection)
     ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
     defer cancel()
     
     opts := options.Update().SetUpsert(true)
     return c.UpdateOne(ctx, query, update,opts)
    }

两者都可以更新一个mgo(update) mongo-go-driver(updateOne),和更新多个mgo(updateAll) mongo-go-driver(updateMany);但是在upsert的时候,mgo直接使用upsert就可以,mongo-go-driver需要设置opts := options.Update().SetUpsert(true)

删除

  • mgo

    func Remove(db, collection string, query interface{}) error {
     ms, c := Connect(db, collection)
     defer ms.Close()
     return c.Remove(query)
    }
    func RemoveNode(pubKey string) error {
     findM := M{"pub_key": pubKey}
     err := Remove(setting.DatabaseSetting.DBName, superNodeC, findM)
     return err
    }
  • mongo-go-driver

    func Remove(db, collection string, query interface{}) (*mongo.DeleteResult, error) {
     c := Connect(db, collection)
     ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
     defer cancel()
     return c.DeleteOne(ctx, query)
    }
    func RemoveNode(pubKey string) error {
     findM := M{"pub_key": pubKey}
     _, err := Remove(setting.DatabaseSetting.DBName, superNodeC, findM)
     return err
    }
    

删除也比较简单,大致相同

事务

  • mgo
    不支持
  • mongo-go-driver

    • 需要使用SessionContext
    • 所有的修改之前需要查询的,请都使用SessionContext(即都使用事务)

      因为多处使用,所以封装了一个方法;
      在这个方法中需要实现的方法是Exec的operator
      type DBTransaction struct {
       Commit func(mongo.SessionContext) error
       Run func(mongo.SessionContext, func(mongo.SessionContext, DBTransaction) error) error
       Logger *logging.Logger
      }
      func NewDBTransaction(logger *logging.Logger) *DBTransaction {
       var dbTransaction = &DBTransaction{}
       dbTransaction.SetLogger(logger)
       dbTransaction.SetRun()
       dbTransaction.SetCommit()
       return dbTransaction
      }
      func (d *DBTransaction) SetCommit() {
       d.Commit = func(sctx mongo.SessionContext) error {
       err := sctx.CommitTransaction(sctx)
       switch e := err.(type) {
       case nil:
       d.Logger.Info("Transaction committed.")
       return nil
       default:
       d.Logger.Error("Error during commit...")
       return e
       }
       }
      }
      func (d *DBTransaction) SetRun() {
       d.Run = func(sctx mongo.SessionContext, txnFn func(mongo.SessionContext, DBTransaction) error) error {
       err := txnFn(sctx, *d) // Performs transaction.
       if err == nil {
       return nil
       }
       d.Logger.Error("Transaction aborted. Caught exception during transaction.",
       zap.String("error", err.Error()))
       return err
       }
      }
      func (d *DBTransaction) SetLogger(logger *logging.Logger) {
       d.Logger = logger
      }
      func (d *DBTransaction) Exec(mongoClient *mongo.Client, operator func(mongo.SessionContext, DBTransaction) error) error {
       ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute)
       defer cancel()
       return mongoClient.UseSessionWithOptions(
       ctx, options.Session().SetDefaultReadPreference(readpref.Primary()),
       func(sctx mongo.SessionContext) error {
       return d.Run(sctx, operator)
       },
       )
      }
      //具体调用
      func SyncBlockData(node models.DBNode) error {
       dbTransaction := db_session_service.NewDBTransaction(Logger)
       // Updates two collections in a transaction.
       updateEmployeeInfo := func(sctx mongo.SessionContext, d db_session_service.DBTransaction) error {
       err := sctx.StartTransaction(options.Transaction().
       SetReadConcern(readconcern.Snapshot()).
       SetWriteConcern(writeconcern.New(writeconcern.WMajority())),
       )
       if err != nil {
       return err
       }
       err = models.InsertNodeWithSession(sctx, node)
       if err != nil {
       _ = sctx.AbortTransaction(sctx)
       d.Logger.Info("caught exception during transaction, aborting.")
       return err
       }
       return d.Commit(sctx)
       }
       return dbTransaction.Exec(models.DB.Mongo, updateEmployeeInfo)
      }
      

总结

  • 转过来主要是官方支持事务,在切换的时候,官方用例中有很多使用的是bsonx,在mgo中使用的bson,踩了一些坑,但是你也可以保持使用bson
  • 事务还是要保证整个事务执行的所有语句都使用事务,不要混合使用(sessionContent)
  • 尽可能做一些代码封装吧

有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:sixgo

查看原文:mgo和mongo-go-driver使用心得比较

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
3911 次点击 ∙ 1 赞
1 回复 | 直到 2019年09月12日 09:16:54
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏