简介

mgo是由Golang编写的开源mongodb驱动。由于mongodb官方并没有开发Golang驱动,因此这款驱动被广泛使用。mongodb官网也推荐了这款开源驱动,并且作者在github也表示受到了mongodb官方的赞助。但由于作者的个人安排原因,该驱动的更新、bug修复、issue维护略微受到诟病。

mgo在功能方面还是比较完善的,api使用也方便。由于mongodb丰富的玩法,mgo代码庞大,其中大部分是与mongodb的协议代码。核心的处理连接和请求的结构,逻辑上还是比较清晰的。

简单的使用

1

2

3

func dial() {

    session ,_ := mgo.Dial("mongodb://127.0.0.1")

}

mgo面向调用者的核心数据结构是mgo.Session,dial函数演示了如何获取一个session

1

2

3

func foo1() {

    session.DB("test").C("coll").Insert(bson.M{"name":"zhangsan"})

}

foo1函数通过生成的session,向test数据库的coll集合写入了一条数据。

但mgo的正确使用方法并非如此,而是应该在每次使用时从源session拷贝

1

2

3

4

5

func foo2() {

    s := session.Copy()

    defer s.Close()

    s.DB("test").C("coll").Insert(bson.M{"name":"zhangsan"})

}

foo2函数从源session拷贝出了一个临时的session,使用临时session写入一条数据,在函数退出时关闭这个临时的session。

session的拷贝与并发

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

1

2

3

4

5

6

7

8

9

10

11

type Session struct {

    m                sync.RWMutex

    ...

    slaveSocket      *mongoSocket

    masterSocket     *mongoSocket

    ...

    consistency      Mode

    ...

    poolLimit        int

    ...

}

这里列出了mgo.Session的五个私有成员变量,与Copy机制有关的是,m,slaveSocket,masterSocket。

mmgo.Session的并发锁,因此所有的Session实例都是线程安全的。

slaveSocket,masterSocket代表了该Session到mongodb主节点和从节点的一个物理连接的缓存。而Session的策略总是优先使用缓存的连接。是否缓存连接,由consistency也就是该Session的模式决定。假设在并发程序中,使用同一个Session实例,不使用Copy,而该Session实例的模式又恰好会缓存连接,那么,所有的通过该Session实例的操作,都会通过同一条连接到达mongodb。虽然mongodb本身的网络模型是非阻塞通信,请求可以通过一条链路,非阻塞地处理;但经过比较简陋的性能测试,在mongodb3.0中,10条连接并发写比单条连接的效率高一倍(在mongodb3.4中基本没有差别)。所以,使用Session Copy的一个重要原因是,可以将请求并发地分散到多个连接中。

以上只是效率问题,但第二个问题是致命的。mgo.Session缓存的一主一从连接,实例本身不负责维护。也就是说,当slaveSocket,masterSocket任意其一,连接断开,Session自己不会重置缓存,该Session的使用者如果不主动重置缓存,调用者得到的将永远是EOF。这种情况在主从切换时就会发生,在网络抖动时也会发生。在业务代码中主动维护数据库Session的可用性,显然是不招人喜欢的。

1

2

3

4

5

6

7

func (s *Session) Copy() *Session {

    s.m.Lock()

    scopy := copySession(s, true)

    s.m.Unlock()

    scopy.Refresh()

    return scopy

}

以上是Copy函数的实现,解决了使用全局Session的两个问题。其中,copySession将源Session浅拷贝到临时Session中,这样源Session的配置就拷贝到了临时Session中。关键的Refresh,将源Session浅拷贝到临时Session的连接缓存指针,也就是slaveSocket,masterSocket置为空,这样临时Session就不存在缓存连接,而转为去尝试获取一个空闲的连接。

Session的连接从哪里来?连接池

明确了使用Session Copy机制的必要性,那么问题来了,Copy出来的临时Session是怎么获取一个到mongodb的物理连接的。答案就是连接池。mgo自身维护了一套到mongodb集群的连接池。这套连接池机制以mongodb数据库服务器为最小单位,每个mongodb都会在mgo内部,对应一个mongoServer结构体的实例,一个实例代表着mgo持有的到该数据库的连接。来看该连接池的定义。

1

2

3

4

5

6

7

8

type mongoServer struct {

    sync.RWMutex

    ...

    unusedSockets []*mongoSocket

    liveSockets   []*mongoSocket

    ...

    info          *mongoServerInfo

}

其中,info代表了该实例对应的数据库服务器在集群中的信息——是否master,ReplicaSetName等。而两个Slice,就是传说中的连接池。unusedSockets存储当前空闲的连接,liveSockets存储当前活跃中的连接,Session缓存的连接就同时存放在liveSockets切片中,而临时Session获取到的连接就位于unusedSockets切片中。

每个mongoServer都会隶属于一个mongoCluster结构,相当于mgo在内部,模拟出了mongo数据库集群的模型。

1

2

3

4

5

6

7

8

9

type mongoCluster struct {

    sync.RWMutex

    ...

    servers      mongoServers

    masters      mongoServers

    ...

    setName      string

    ...

}

如定义所示,mongoCluster持有一系列mongoServer的实例,以主从结构分散到两个数组中。

每个Session都会存储自己对应的,要操作的mongoCluster的引用。

长途跋涉的Session

以下描述一个Copy出来的临时Session是如何获取到一个mongodb物理连接的。

当临时Session被Copy出来,并且通过调用一系列api,将一次数据库操作设置到了Session内部后,此时万事俱备,只差连接。新生的Session首先会检查自己的缓存里是否有连接可用,初来乍到的他当然不知道自己是一个一无所有的光杆司令。由于mgo的实现,可怜的他还要去检查两次,一次使用读锁,一次使用写锁。作者的意图应该是期望在对同一个session并发操作时,能在第二次排他锁检查之前,恰巧缓存到一条连接,那么就可以减少一次对连接池的操作。但这次,这种好事没有发生在这个Session身上,“摸”了两次“口袋”反复确认以后,他终于还是发现自己身无分文。没有连接的他向组织求救,也就是这个session所要操作的mongodb集群,也就是所提到的mongoCluster结构。

“组织”问了这个Session一系列问题,其中最主要的是两个问题,一是”你要主库连接还是从库连接”,二是“你期望的连接池最大大小是多少”。第一个问题,Session很好回答,他首先看了看自己的模式,是必须到主库还是必须到从库,还是两者皆可看情况而定。再看了看自己手里的操作是读还是写,写操作当然不可能到从库去完成。第二个问题就有点强人所难,但是他不用自己思考,因为这是从源Session那里拿过来的配置,也算是一点祖产吧。

这个Cluster此时表现得像一个掌柜,他先根据主从,从自己手下的mongoServer里挑出了一个,然后问他,你现在手里有没有空闲的连接。如果有,那幸运的Session就可以顺利地获取到这个空闲的连接,高高兴兴的揣到兜里回家干活。但如果不巧,正好unusedSockets为空,那么掌柜会问另一个问题,你有没有超过这个家伙的期望的最大连接数。如果没有超过,那还好,作为伙计的mongoServer就干活了,他会跑到他负责的数据库服务器那里去申请一条全新的连接,亲手交到Session的手里。但如果这个伙计算了算,还去申请新连接的话,恐怕就超限了,那就Session同学对不起了您,您等吧。每100ms,伙计自旋一次,等着unusedSockets里出现可用的连接。

当然有人会问,那这么自旋下去,如果连接一直被其他Session占用,会不会就死循环了呢,答案是不会。这个伙计作为一个数据库服务器的管理员吧可以说,他自己也要常常去确认他负责的这个服务器是不是还活着。因此,伙计同学每15s会给服务器发一个ping命令。作为管理员,伙计可就不管什么连接池大小超不超的问题了,那是他们那些普通session要考虑的琐事。伙计同学要ping的时候,也去unusedSockets里看,如果有最好,就拿一个来用;没有的话,直接去问服务器要新的。ping完之后,新的连接就会被放入unusedSockets中。这样的话,自旋中的获取连接请求,就可以拿到连接了。

经过摸口袋,找组织,问伙计,伙计再干点小活,临时Session终于拿到了梦寐以求的数据库物理连接,把他放到了自己的口袋里(当然有些模式的Session不会这么干)。心满意足地将自己手里的操作通过这条连接写了出去,等到数据库给了他想要的应答,他的生命也就结束了。通过Close方法,我们剥夺了他口袋里的得之不易的连接,放回到了对应mongoServerunusedSockets中。不久之后,GC又杀死了这个Session。

go---- mgo相关推荐

  1. golang的mongodb操作(mgo)

    2019独角兽企业重金招聘Python工程师标准>>> 使用mgo http://godoc.org/labix.org/v2/mgo package mainimport (&qu ...

  2. Go使用mgo增删改查聚合操作

    包 github.com/globalsign/mgo 查询 查询所有 query := func(c *mgo.Collection) (interface{}, error) {q := bson ...

  3. 基 于 svm 的 图 像 分 类_CeO2和MgO助烧剂对矾土基莫来石合成料烧结的影响

    为更好地发挥我国高铝矾土的资源优势, 应加快发展矾土基合成料, 包括均质料.改性料和转型料, 以提高高铝矾土资源的综合利用率,并促进矾土基耐火材料质量.品位和附加值的提高.本研究的前期工作是以矿山积压 ...

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

    mgo和mongo-go-driver使用心得比较 >> 资源下载: https://72k.us/file/14896800-396374653 mgo和mongo-go-driver比 ...

  5. MongoDB的Go语言驱动----mgo的使用指南

    转自:https://blog.csdn.net/skh2015java/article/details/53033473 mgo简介 mgo(音mango)是MongoDB的Go语言驱动,它用基于G ...

  6. mgo简介以及使用说明

    简介: mgo是Go语言操作monggodb数据库的一个驱动,封装了基于Go语法的API 官网地址:http://labix.org/mgo 文档地址: mgo GoDoc GoWalker mgo/ ...

  7. Golang 的 mgo 连接池

    mgo 的 Session 与连接池 简介 mgo 是由 Golang 编写的开源 mongodb 驱动.由于 mongodb 官方并没有开发 Golang 驱动,因此这款驱动被广泛使用.mongod ...

  8. go mgo包 简单封装 mongodb 数据库驱动

    mgo是go编写的mongodb的数据库驱动,集成到项目中进行mongodb的操作很流畅,以下是对其的一些简单封装,具体使用可随意改动封装. 安装 go get gopkg.in/mgo.v2 使用 ...

  9. mgo.v2无法连接MongoDB 5.0.x

    mgo.v2无法连接MongoDB 5.0.x 问题描述: MongoDB升级到5.0.1之后,使用gopkg.in/mgo.v2来连接MongoDB数据库时,发现验证时出现验证错误了.server ...

  10. Go语言mgo使用情况

    本文重点介绍mgo使用,仅简单介绍mongodb. mongodb特性 mongdb简单介绍 注意: 上图已经告知我们mongo不支持事务,在开发项目应用时,想要保证数据的完整性请考虑关系型数据库(经 ...

最新文章

  1. 集成Lua到你的Android游戏(常见问题补充,解决,)
  2. 用Android写代码,用css写个android机器人代码分享
  3. shiro教程(2)- shiro介绍
  4. WebBrowser 操作(从网上收集)
  5. 关于 SAP Spartacus defaultCmsContentProvider 和默认 layoutConfig 的一些测试
  6. Ajax学习总结+案例
  7. 断点调试 debug模式 1006
  8. 电商商家可以用到的小工具和素材资源网站
  9. linux正则表达式脚本实例,Shell下的正则表达式及实例
  10. 学习React基本渲染数据操作(-)
  11. vhdl入门8位全减器
  12. selenium模拟登录QQ空间
  13. c# wifi串口通信_C#串口通信 SerialPort类
  14. android判断一个控件是否获得光标,Android View获取焦点
  15. Android 实现自动抢微信红包
  16. Access数据类型
  17. docker学习 --Compose 容器编排,常用命令等.集成spring。mysql。redis
  18. http/2与http/1的区别
  19. 单因子——数据Wind
  20. 进位计数制及其相互转换

热门文章

  1. 面经 - 阿里巴巴 - 视频二面
  2. uc 浏览器不能打开网页
  3. ts中的never类型
  4. 从大厂裸辞后,面阿里、字节全都挂掉,连货拉拉都不要自己...
  5. DLM 通讯初始化语句
  6. AppleParty(苹果派)v3 支持 App Store 新定价机制 - 批量配置自定价格和销售范围
  7. [Matlab]脚本实现Excel单元格内容首尾空格删除
  8. mount挂载硬盘出错 linux 下分区格式为lvm
  9. 尚有缺者,方为完美。小人求全,智者求阙——《致温弟沅弟》曾国藩
  10. 阿里原来这么容易就能进去…