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


    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})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

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

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

    // 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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    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")
    }
    
    • 1
    • 2
    • 3
    • 4

    网上代码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()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    【重要】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})
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    五、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的文档中找到。
  • 相关阅读:
    基于hadoop MapReduce的新闻平台用户点击视频统计分析
    C#中Math类中的常用函数
    IDEA的DEUG模式技巧和使用
    Keras CIFAR-10分类 自定义simple CNN篇
    假脱机技术——实现独占设备变成共享设备
    拼多多自动浏览商品
    zlMediaKit 4 buffer模块--buffer什么都能装&不只有send还有sendmsg/sendmmsg
    Android 内容提供者和内容观察者:数据共享和实时更新的完美组合
    【计算机毕业设计】java ssm网上宠物商店系统
    4(5)-碘乙酰氨基荧光素,CAS号: 63368-54-7
  • 原文地址:https://blog.csdn.net/inthat/article/details/127136301