轉發自http://blog.chinaunix.net/uid-15795819-id-3075952.html
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);
我們還是使用複製集連接代碼,見此篇博客
讀測試代碼:
-
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();
-
}
-
}
-
- }
當我們設置了
- 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;
- }
- Response res = _connector.call( _db , this , query , null , 2, readPref, decoder );
-
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 );
-
...
程序是通過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;
} -
....
- 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;
}
至於主庫的選擇,那個實現的比較複雜,他會去判斷是不是讀的時候主庫已經切換,等等嚴格的檢查。
結語:通過簡單的設置ReadPreference就可以實現Mongo的讀寫分離,這對程序再簡單不過了。但是由於Mongo跟Mysql都是通過讀日誌實現的數據同步,短暫的延遲是必然的,而且Mongo現在的版本是全局鎖,主從同步也是個問題,特別是設置了嚴格同步寫入的時候。當然這不是Mongo擅長做的事情,你可以用在商品評論,SNS等不在意數據延遲的應用中,真的很奏效。