MongoDB Golang驱动mgo的连接池使用问题
Posted 讯飞技术沙龙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MongoDB Golang驱动mgo的连接池使用问题相关的知识,希望对你有一定的参考价值。
现网遇到的问题
Xpush推送历史消息存储组件mgoproxy。该组件功能为接收推送消息,处理后将该消息存入MongoDB中,在网络异常、网络抖动的情况下,推送消息无法写入MongoDB,日志报EOF;在服务请求压力过高的情况下,可发现TCP发送队列有较高的数据积压,请求处理缓慢,后修改Mgo连接模式,问题得到解决
IM的web服务后端组件webServer。该组件功能为接收HTTP请求,对请求进行处理,将数据存入MongoDB数据库或从数据库中读取必要的数据,之后将结果返回给客户端。与上面的情况类似,同样是在网络异常的情况下,会出现数据库操作EOF错误,在请求量过大的情况下,数据库操作耗时过长,响应返回很慢的问题。修改连接模式后,问题解决。
Mgo数据结构简介
Session简介
Mgo客户端和MongoDB数据库通信是通过Session来完成的,Session的结构如下:
type Session struct {
m sync.RWMutex
cluster_ *mongoCluster
slaveSocket *mongoSocket
masterSocket *mongoSocket
slaveOk bool
consistency Mode
queryConfig query
safeOp *queryOp
syncTimeout time.Duration
sockTimeout time.Duration
defaultdb string
sourcedb string
dialCred *Credential
creds []Credential
poolLimit int
bypassValidation bool
}
下面是几个相对比较重要的参数:
slaveSocket——Session到MongoDB从节点的物理连接缓存
masterSocket——Session到MongoDB主节点的物理连接缓存
m——mgo.Session的并发锁,Session实例线程安全
consistency——决定Session是否缓存物理连接,由一致性模式决定
cluster_——连接的MongoDB集群的引用
Session的策略总是优先使用缓存的连接,是否使用缓存连接,由consistency即该Session的模式决定,除了Eventual consistency mode不使用缓存的连接外,剩余的连接模式均会使用缓存中的连接。缓存的一主一从连接,Session本身不负责维护,即连接断开后,Session不会重置缓存,Session的使用者如果不主动重置缓存,调用者得到的永远是EOF。这种情况在数据库主从切换、网络断连及网络抖动时都会发生。后面的集成使用Mgo的注意事项中会讲到这一点。
Mgo连接池
Session中缓存的连接来自于Mgo维护的连接池,连接池以MongoDB数据库服务器为最小单位,每个MongoDB都会在Mgo内部都会对应一个mongoServer结构体的实例,一个实例代表着Mgo持有的到该数据库的连接。该数据结构被封装在mongoServer结构中,如下所示:
type mongoServer struct {
sync.RWMutex
Addr string
ResolvedAddr string
tcpaddr *net.TCPAddr
unusedSockets[]*mongoSocket
liveSockets []*mongoSocket
closed bool
abended bool
sync chan bool
dial dialer
pingValue time.Duration
pingIndex int
pingCount uint32
pingWindow [6]time.Duration
info *mongoServerInfo
}
其中和连接密切相关的两个成员变量是unusedSockets和liveSockets,这两个slice就是连接池,unusedSockets存储当前空闲的可用连接,liveSockets存储当前活跃的连接,Session缓存的连接同时存放在liveSockets中。
MongoDB集群
每个mongoServer结构都会隶属于一个mongoCluster结构,相当于在Mgo内部,模拟出了MongoDB数据库集群的模型。其结构如下:
type mongoCluster struct {
sync.RWMutex
serverSyncedsync.Cond
userSeeds []string
dynaSeeds []string
servers mongoServers
masters mongoServers
references int
syncing bool
direct bool
failFast bool
syncCount uint
setName string
cachedIndex map[string]bool
sync chan bool
dial dialer
}
mongoCluster包括所有mongoServer实例,以主从结构分散到两个数组中。
Session一致性模式介绍
Strong consistency mode
在强一致模式下,读写都是在主服务节点上通过同一个连接实现,这样读写是完全一致、有序并且查询到的数据也是最新的。这种模式在分布式负载架构下没有多少益处,但是保证了读写数据的强一致性。由于该模式下对数据库的读写都是在单独的TCP连接上进行的,所以效率不高,在高负载下难免会出现数据积压,上面所说的两个问题,也是这个原因导致的,所以在生产环境中,如果对数据一致性的要求不高,建议不要使用这种模式。
Monotonic consistency mode
在Monotonic一致性模式下,读到的数据不一定是最新的,但是在同一个session中的一系列读保持着数据一致性。session 的读操作开始是向某个 secondary 服务器发起,只要出现了一次写操作,session 的连接就会切换至 primary 服务器。由此可见此模式下,能够分散一些读操作到 secondary 服务器,但是读操作不一定能够获得最新的数据。
Eventual consistency mode
session 的读操作会向任意的 secondary 服务器发起,多次读操作并不一定使用相同的连接,也就是读操作不一定有序。session 的写操作总是向 primary 服务器发起,但是会使用不同的连接,也就是写操作也不一定有序。Eventual 一致性模式最快,其是一种资源友好(resource-friendly)的模式,在高压力环境下,应该选用该连接模式。
总结来说,如下表所示:
Strong |
Monotonic |
Eventual |
|
Read |
read to primary server |
read to arbitrary secondary |
read to any secondary |
Write |
write to primary server |
write to primary |
write to primary |
Connection |
unique |
unique |
independent |
Guarantee |
the most |
some useful |
the least |
集成使用Mgo的注意事项
Session拷贝
在与MongoDB通信前,首先要创建一个Session,但是后续的通信,我们不会直接使用这个Session,而是基于这个全局的Session,在每次操作的时候Copy一个临时Session出来,来与数据库进行通信,原因有两点:
实际的生产环境中,会有网络断连和网络抖动的情况,在网络连接恢复后,由于Session不会重置缓存的连接,所以会导致数据库操作失败(在不使用Eventual模式情况下),而如果在每次数据库操作前Copy一个临时Session,由于临时Session的缓存连接为空(Copy函数中的Refresh会将缓存置空),所以会从物理连接池中获得一个可用连接,来完成后续的数据库操作。
有些模式的数据库读写在一个Session中是在一个连接上完成的,如果只使用一个全局的Session,这会降低服务在高并发情况下的处理能力。
Eventual模式
通过Dial获取的Session如果没有设置,默认是Strong连接模式。可通过SetMode接口修改连接模式为Eventual模式,该模式不使用缓存连接,因此不需要每次操作数据时Copy一个临时Session,另外该模式下,数据库读写操作会分布到很多TCP连接中,因此服务在高并发情况下的处理能力也有所提升。
Q&A
Q1:Mgo三种一致性模式的区别是什么?
A1:主要的区别就是读写方式的不同。在强一致模式下,读写都是在主服务节点上通过同一个连接实现,这样读写是完全一致、有序并且查询到的数据也是最新的。这种模式在分布式负载架构下没有多少益处,但是保证了读写数据的强一致性。在Monotonic一致性模式下,读到的数据不一定是最新的,但是在同一个session中的一系列读保持着数据一致性;session 的读操作开始是向某个 secondary 服务器发起,只要出现了一次写操作,session 的连接就会切换至 primary 服务器;在Eventual模式下,session 的读操作会向任意的 secondary 服务器发起,多次读操作并不一定使用相同的连接,也就是读操作不一定有序。session 的写操作总是向 primary 服务器发起,但是会使用不同的连接,也就是写操作也不一定有序;总结就是文中的表格。
Q2:Monotonic模式下,开始在secondary 服务器读,然后有一次写之后转到了primary 服务器,那再读是在primary 服务器吗?这样数据会不会不一样?
A2:在数据同步的时间段内,会有几率导致刚写的数据读不到,如果想保持读写数据一致性,建议使用Strong连接模式
Q3:Strong模式在一个Session中是通过单个连接进行读写的,还是通过一组连接?
A3:在单个Session中,是通过单个连接进行数据读写的。
Q4:MongoDB的主节点挂点之后会出现什么问题?
A4:mongodb 底层的主从(即复制集)是用raft协议保证数据一致性和failover故障切换。数据库类的主从切换一般是代理负责,比如mysql的proxy或者是mongodb的mongos,至于数据库类为什么不用多路复用,而用连接池模式,是因为数据库类的连接会有些连接相关属性或者会话变量的设置,还有一些事物相关的操作,多路复用只会带来复杂度,并不能提高性能,至于客户端的连接池,主要是负责对proxy代理的异常检测就行了,proxy可以认为是均等的,具体内部的切换和连接都是proxy来决定的。下面是MongoDB拓扑图:
其中shard代表着分片,针对集合可以做水平范围切分,实现水平扩展。
Q5:MongoDB的文件系统是什么?
A5:文件系统用的操作系统的ext4.xfs等,mongod内部实现的数据格式叫bson
以上是关于MongoDB Golang驱动mgo的连接池使用问题的主要内容,如果未能解决你的问题,请参考以下文章
用golang的mgo驱动,mongodb时区怎么设置,总是慢8小时
Golang mongodb 从集合中删除所有项目 [mgo.v2]