Mongo的主从和复制集结构提供良好的读写分离环境,Mongo的java-driver也实现了读写分离的参数,这给程序开发减少了很多工作。现在我们看一下Mongo-Java-Driver读写分离的一些机制。
MongoJavaDriver的读是通过设置ReadReference参数,顾名思义,读参照,或者读偏好。与之对应的是WriteConcern,字面意思写涉及,就是规定了写的一些参数,比如是否是一致写,对应Mongo中的w,j,fync等参数,我们暂不讨论。在MongoDB 2.0/Java Driver 2.7版本之前,是通过MongoOption的slaveOk参数控制从库的读,在之后的版本已经废弃。
ReadPreference使用方法:
m. setReadPreference( new ReadPreference( ) . SECONDARY) ;
ReadReference是一个类,定义了三种读的参数:Primary、Secondery、Taged,Taged是一个内部类,使用DbObject赋值。这个方便程序自定义读参数,暂不讨论。
我们还是使用复制集连接代码,见此篇博客
读测试代码:
public static void main( String args[ ] ) {
DB db = m. getDB( "test" ) ;
db. authenticate ( "test" , "123" . toCharArray ( ) ) ;
while ( true) {
DBCollection dbcol = db. getCollection ( "things" ) ;
System . out. println ( dbcol. findOne( ) ) ;
try {
Thread . sleep ( 500) ;
} catch ( InterruptedException e) {
// TODO Auto-generated catch block
e. printStackTrace ( ) ;
}
}
}
每隔0.5秒查询一次数据库,观察mongostat状态。当我们不设置读参数的时候,Mongo只在主库上读,这跟Mongo文档中写的有出入,我们的观察结果页印证了这点,两个从库上都是是没有任何读的。
当我们设置了
m. setReadPreference( new ReadPreference( ) . SECONDARY) ;
之后,再观察两个从库
已经有读了,也可以看出两个从库是交替读的(并不严格,后面会说),而主库没有任何读。
这是为什么呢,我们来看他的实现机制。
首先从findOne()函数开始,这个函数重载了很多方法,最终都是调用:
public DBObject findOne( DBObject o, DBObject fields, ReadPreference readPref ) {
Iterator < DBObject> i = __find( o , fields , 0 , - 1 , 0, getOptions ( ) , readPref, getDecoder( ) ) ;
DBObject obj = ( i = = null ? null : i. next ( ) ) ;
if ( obj ! = null & & ( fields ! = null & & fields. keySet ( ) . size ( ) > 0 ) ) {
obj. markAsPartialObject( ) ;
}
return obj;
}
这是最上层返回数据的函数,我们看到_find()方法中已经存在readPref参数了,这个函数是个抽象函数,DBApiLayer类实现了此方法,继续往下走:
Response res = _connector. call ( _db , this , query , null , 2, readPref, decoder ) ;
函数中调用了_connector. call (),这估计就是执行命令的函数了,continue:
if ( readPref = = null )
readPref = ReadPreference. PRIMARY;
if ( readPref = = ReadPreference. PRIMARY & & m. hasOption( Bytes. QUERYOPTION_SLAVEOK ) )
readPref = ReadPreference. SECONDARY;
...
final DBPort port = mp.get( false , readPref, hostNeeded );
...
res = port.call( m , coll, readPref, decoder );
...
我们看到了我们开头讲的参数设置的判断,如果不设置readPref,那么默认PRIMARY,由于Bytes.QUERYOPTION_SLAVEOK这个参数已经废弃,而且默认是false,所以其他的情况下就是SECONDARY了。
程序是通过DBPort这个类去执行Mongo命令的,我们看得到port的mp.get()函数:
if ( ! ( readPref = = ReadPreference. PRIMARY) & & _rsStatus ! = null ) {
// if not a primary read set, try to use a secondary
// Do they want a Secondary, or a specific tag set?
if ( readPref = = ReadPreference. SECONDARY) {
ServerAddress slave = _rsStatus. getASecondary( ) ;
if ( slave ! = null ) {
return _portHolder. get ( slave ) . get ( ) ;
}
} else if ( readPref instanceof ReadPreference. TaggedReadPreference) {
// Tag based read
ServerAddress secondary = _rsStatus. getASecondary( ( ( TaggedReadPreference) readPref ) . getTags ( ) ) ;
if ( secondary ! = null )
return _portHolder. get ( secondary ) . get ( ) ;
else
throw new MongoException( "Could not find any valid secondaries with the supplied tags ('" +
( ( TaggedReadPreference) readPref ) . getTags ( ) + "'" ) ;
}
}
....
// use master
DBPort p = _masterPortPool.get();
if ( keep && _inRequest ) {
// if within request, remember port to stick to same server
_requestPort = p;
}
....
这就比较明了了,通过上一篇文章提到的ReplicaSetStatus类的getASecondary()去得到slave:
int start = pRandom. nextInt ( pNodes. size ( ) ) ;
Node n = pNodes.get( ( start + i ) % nodeCount );
if ( ! n.secondary() ){
mybad++;
continue;
} else if (pTagKey != null && !n.checkTag( pTagKey, pTagValue )){
mybad++;
continue;
}
通过一个random的nextInt选择从库,所以说是随即的,不是Round-Robin,交替读也不是这么严格的,但是基本可以这么认为,不是问题。
至于主库的选择,那个实现的比较复杂,他会去判断是不是读的时候主库已经切换,等等严格的检查。
结语:通过简单的设置ReadPreference就可以实现Mongo的读写分离,这对程序再简单不过了。但是由于Mongo跟Mysql都是通过读日志实现的数据同步,短暂的延迟是必然的,而且Mongo现在的版本是全局锁,主从同步也是个问题,特别是设置了严格同步写入的时候。当然这不是Mongo擅长做的事情,你可以用在商品评论,SNS等不在意数据延迟的应用中,真的很奏效。
运维网声明
1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com