当前位置: 首页 > 工具软件 > mgo > 使用案例 >

golang常用库之-mgo.v2包、MongoDB官方go-mongo-driver包、七牛Qmgo包 | go操作mongodb、mongodb bson

阎知
2023-12-01

golang常用库之-mgo.v2包、MongoDB官方go-mongo-driver包、七牛Qmgo包 | go操作mongodb、mongodb bson

一、【不推荐】mgo.v2包

  • mgo.v2包
    官网:http://labix.org/mgo
    引用包: gopkg.in/mgo.v2

mgo.v2包,已经跟不上mongo版本了,很早之前就停止维护了,后面会有更多的兼容性问题。不支持最新版本的mongo(5.x以上)

【推荐】github.com/vinllen/mgo

国人fork的一个mgo版本,github.com/vinllen/mgo 这个库支持新版本,而且不用改之前mgo代码。

该fork 进行了一些改进,添加了一些新功能,但大多用于改进性能和错误修复的变化。

使用mgo整理思路

  1. 创建 DialInfo 结构体实例,它包含与MongoDB群集建立会话的选项
  2. 调用 mgo.DialWithInfo 建立新会话,返回会话实例
  3. 在程序启动时,我创建了一个主 mgo.Session,然后,对于每个处理的请求,我复制主会话session.Copy() 并在完成后关闭它。

伪代码示例:

	var dialInfo *mgo.DialInfo
	//dialInfo 实例各种配置赋值
	
	dialInfo.Timeout = time.Second * 5
	m.dbSession, err = mgo.DialWithInfo(dialInfo) 
	
	sess := m.dbSession.Copy()
	defer sess.Close()
	err = m.dbSession.DB("").C(colName).Create(&mgo.CollectionInfo{Capped: true, MaxBytes: colCapMaxSizeBytes})

注意:如果Capped是true,那么当collection 满了。必须设置MaxBytes定义集合wraps 的大小。
MaxDocs是可选的,可以定义wraps的文档数,但仍需要设置MaxBytes。

做插入文档操作,使用的是Insert方法,如下:
伪代码

sess := m.dbSession.Copy()
defer sess.Close()
sess.DB("").C(collectionName).Insert(dataSet...)

// Insert 集合中插入一个或多个文档
func (c *Collection) Insert(docs …interface{}) error {

mgo 和核心结构体是 mgo.Session

// mgo.v3: Drop Strong mode, suffix all modes with "Mode".

// When changing the Session type, check if newSession and copySession
// need to be updated too.

// Session represents a communication session with the database.
//
// All Session methods are concurrency-safe and may be called from multiple
// goroutines. In all session modes but Eventual, using the session from
// multiple goroutines will cause them to share the same underlying socket.
// See the documentation on Session.SetMode for more details.
type Session struct {
	defaultdb        string
	sourcedb         string
	syncTimeout      time.Duration
	sockTimeout      time.Duration
	poolLimit        int
	poolTimeout      time.Duration
	consistency      Mode
	creds            []Credential
	dialCred         *Credential
	safeOp           *queryOp
	mgoCluster       *mongoCluster
	slaveSocket      *mongoSocket
	masterSocket     *mongoSocket
	m                sync.RWMutex
	queryConfig      query
	bypassValidation bool
	slaveOk          bool
}

mgo 创建索引(EnsureIndex方法)

Mongodb里面,如果不添加索引,对Mongodb数据表进行查询操作的时候,需要把数据都加载到内存。当数据的数量达到几十万乃至上百万的时候,这样的加载过程会对系统造成较大的冲击,并影响到其他请求的处理过程。

用户表当前的记录数量已经达到45万,每次都加载这么多数据,查询起来肯定是非常慢的。建立索引以后对索引字段进行查询时,仅会加载索引数据,能极大地提高查询速度。

引用mgo,代码中调用

err = c.EnsureIndex(idIndex)
if err != nil {
	return errors.Wrap(err, "failed to ensures an index with the given key exists")
}

网上代码demo:

package main

import (
    "fmt"
    "log"
    "time"

    "gopkg.in/mgo.v2"
)

const (
    MongoDBHosts = "IP:PORT"  //mongodb 地址端口
    AuthDatabase = "dbname"   //autn 库
    AuthUserName = "username" //auth 用户名
    AuthPassword = "password" // auth 密码
    MaxCon       = 300        //连接池socket设置
)

func main() {
    mongoDBDialInfo := &mgo.DialInfo{
        Addrs:    []string{MongoDBHosts},
        Timeout:  60 * time.Second,
        Database: AuthDatabase,
        Username: AuthUserName,
        Password: AuthPassword,
    }

    session, err := mgo.DialWithInfo(mongoDBDialInfo)
    if err != nil {
        log.Fatalf("CreateSession failed:%\n", err)
    }

    //建立collection
    coll := session.DB("test").C("user")

    /*
        EnsureIndexKey ensures an index with the given key exists, creating it if necessary
    */

    //索引存在则不进行操作,不存在自动创建
    // err = coll.EnsureIndexKey("a", "b")
    //fmt.Println("err-----------------:",err)

    //查询所有的已存在索引
    // indexs, err := coll.Indexes()
    // fmt.Println("err-----------------:", err)
    // fmt.Println("indexs--------------:", indexs)

    //删除索引,成功返回nil,如果不存在则返回对应的具体信息
    // err = coll.DropIndex("a", "b")
    // fmt.Println("err-----------------:", err)

    //设置连接池的大小,默认4096可自定义修改根据需求
    session.SetPoolLimit(MaxCon)
    defer session.Close()
}

【重要】golang mgo的mongo连接池设置

golang mgo的mongo连接池设置:必须手动加上maxPoolSize
参考URL: https://www.cnblogs.com/shenguanpu/p/5318727.html
mgo 的 session 与连接池(精)
参考URL: https://www.fecmall.com/topic/581

golang main入口启动时,我们会创建一个全局session,然后每次使用时clone session的信息和连接,用于本次请求,使用后调用session.Close() 释放连接。

session的拷贝与并发
为什么要在每次使用时都Copy,而不是直接使用Dial生成的session实例呢?个人认为,这与mgo.Session的Socket缓存机制有关。来看Session的核心数据结构。

mgo连接池是自带的,你只需要使用session.Copy()拿到一个复制的session,用完之后session.Close()即可。

Clone的方法注释里说明会重用原始session的socket连接,但是并发请求一大,其他协程来不及释放连接,当前协程会怎么办?
不断的创建连接
netstat -nat|grep -i 27017|wc -l

如果每个session 不调用close,会达到恐怖的4096,并堵死其他请求,所以clone或copy session时一定要defer close掉。

启用maxPoolLimit 参数则会限制总连接大小,连接到限制则当前协程会sleep等待 直到可以创建连接

连接池设置方法:

1、配置中 增加

2、代码中 :

dao.GlobalMgoSession.SetPoolLimit(10)

再做压测:

$ netstat -nat|grep -i 27017|wc -l
15

mgo底层socket连接池只在maxPooMaxLimit 范围内实现复用,需要自行优化。

三、MongoDB官方 go-mongo-driver。

go-mongo-driver 功能抽象没有 mgo 那么高级,用起来很零碎。

四、【推荐】七牛Qmgo包

什么是qiniu/qmgo包

github: https://github.com/qiniu/qmgo

七牛研发团队开源Go语言的MongoDB driver Qmgo。
它基于MongoDB官方 mongodb/mongo-go-driver 实现,但是有着更好的易用性,设计上参考了老牌的driver Mgo: go-mgo/mgo(比如Mgo的链式调用)。

相对于其他库,我发现这个库更新算是比较频繁,而且是以公司名义开源,可以试试~

示例查看官方demo即可: https://github.com/qiniu/qmgo/blob/master/README_ZH.md

入库mongodb demo

package main

import (
	"context"
	"fmt"

	"github.com/qiniu/qmgo"
)

type UserInfo struct {
	Name   string `bson:"name"`
	Age    uint16 `bson:"age"`
	Weight uint32 `bson:"weight"`
}

func main() {

	ctx := context.Background()
	// Database:库名  Coll:集合名
	cli, err := qmgo.Open(ctx, &qmgo.Config{Uri: "mongodb://127.0.0.1:27017", Database: "mongo_1", Coll: "collection_1"})

	defer func() {
		if err = cli.Close(ctx); err != nil {
			panic(err)
		}
	}()

	cli.EnsureIndexes(ctx, []string{}, []string{"age", "name,weight"})

	var userInfo = UserInfo{
		Name:   "xm",
		Age:    7,
		Weight: 40,
	}
	// insert one document
	result, err := cli.InsertOne(ctx, userInfo)
	fmt.Println("result", result)
	// find one document
	//one := UserInfo{}
	//err = cli.Find(ctx, bson.M{"name": userInfo.Name}).One(&one)

	//err = cli.Remove(ctx, bson.M{"age": 7})

}

五、mongodb bson

什么是bson

BSON( Binary Serialized Document Format) 是一种二进制形式的存储格式,采用了类似于 C 语言结构体的名称、对表示方法,支持内嵌的文档对象和数组对象,具有轻量性、可遍历性、高效性的特点,可以有效描述非结构化数据和结构化数据。

BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型。

BSON可以做为网络数据交换的一种存储形式,这个有点类似于Google的Protocol Buffer,但是BSON是一种schema-less的存储形式,它的优点是灵活性高,但它的缺点是空间利用率不是很理想。

MongoDB使用了BSON这种结构来存储数据和网络数据交换。把这种格式转化成一文档这个概念(Document),因为BSON是schema-free的,所以在MongoDB中所对应的文档也有这个特征,这里的一个Document也可以理解成关系数据库中的一条记录(Record),只是这里的Document的变化更丰富一些,如Document可以嵌套。

BSON这种格式是专门为MongoDB而开发的,类似json的一种二进制格式。这种格式不一定比json存储的文件小,其优点是解释快。

总结: BSON是一种二进制序列化格式,用于在MongoDB中存储文档和进行远程过程调用。 有关BSON规范请访问 bsonspec.org

Golang中struct tag bson

Go系列:结构体标签
参考URL: https://www.proyy.com/7005465902804123679.html

需求:mongdb golang编码中,根据struct tag 作为mongdb的文档的字段名,而不是结构体变量做为字段名,应该怎么做?

解决方案:
使用bson 结构体标签。
常用的结构体标签Key,指的是那些被一些常用的开源包声明使用的结构体标签键。在这里总结了一些,都是一些我们平时会用到的包,它们是:

  • json: 由encoding/json 包使用,详见json.Marshal()的使用方法和实现逻辑。
  • xml : 由encoding/xml包使用,详见xml.Marshal()。
  • bson: 由gobson包,和mongo-go包使用。
  • protobuf: 由github.com/golang/protobuf/proto 使用,在包文档中有详细说明。
  • yaml: 由gopkg.in/yaml.v2 包使用,详见yaml.Marshal()。
  • gorm: 由gorm.io/gorm包使用,示例可以在GORM的文档中找到。
 类似资料: