- 求職 : Linux運維
- 論壇徽章:
- 203
|
最近遇到一個user case,因為集合數(shù)量太多,導致Secondary節(jié)點無法進行initial sync(主備同步的第一步,可理解為從Primary上全量拷貝數(shù)據(jù))。
副本集使用wiredtiger存儲引擎,一共60,000+集合,平均每個集合4個索引,wiredtiger的集合及每個索引都對應一個單獨的文件來存儲,數(shù)據(jù)目錄下總共300,000+文件,listDatabases命令執(zhí)行時,會遍歷所有DB的每個集合,獲取集合及其索引文件占用的存儲空間信息,實現(xiàn)類似如下的偽代碼。
listDatabases() {
dbNames = getAllDatabaseNames();
for db in dbNames {
sizeOnDisk = 0;
for coll in db.getAllColletions() {
size += coll.size();
for index in coll.getAllIndexes() {
size += index.size();
}
}
addToOutput(db, size);
}
}
使用wiredtiger引擎時,獲取集合及索引文件大小信息時,需要打開一個特殊的cursor來讀取,整個listDatabases需要遍歷300,000+個文件來逐個獲取大小信息,導致整個命令的執(zhí)行開銷很大,總耗時在30s以上。
Secondary在執(zhí)行initial sync時,其過程類似如下
刪除本地除local外的所有數(shù)據(jù)庫
執(zhí)行l(wèi)istDatabases命令,獲取同步源上所有DB的列表
針對每個DB,調用listCollections獲取所有集合信息,并遍歷所有集合,同步集合內的文檔并建立索引。
……
initial sync執(zhí)行時,設置了socket timeout為30s,而listDatabases的執(zhí)行時間是超過30s的,導致同步在listDatabases時就一直超時失敗,10次重試后仍然失敗,Secondary進程就直接退出了。
2016-06-27T20:59:46.494+0800 I REPL [rsSync] ******
2016-06-27T20:59:46.495+0800 I REPL [rsSync] initial sync pending
2016-06-27T20:59:46.499+0800 I REPL [rsSync] no valid sync sources found in current replset to do an initial sync
2016-06-27T20:59:47.499+0800 I REPL [rsSync] initial sync pending
2016-06-27T20:59:47.517+0800 I REPL [rsSync] initial sync drop all databases
2016-06-27T20:59:47.517+0800 I STORAGE [rsSync] dropAllDatabasesExceptLocal 1
2016-06-27T20:59:47.517+0800 I REPL [rsSync] initial sync clone all databases
2016-06-27T21:00:17.517+0800 I NETWORK [rsSync] Socket recv() timeout 10.182.4.106:27017
2016-06-27T21:00:17.517+0800 I NETWORK [rsSync] SocketException: remote: (NONE):0 error: 9001 socket exception [RECV_TIMEOUT] server [10.1.1.6:27017]
2016-06-27T21:00:17.519+0800 E REPL [rsSync] 6 network error while attempting to run command 'listDatabases' on host '10.1.1.6:27017'
2016-06-27T21:00:17.519+0800 E REPL [rsSync] initial sync attempt failed, 9 attempts remaining
問題已反饋給官方團隊,詳情見SERVER-24948,在3.4版本里會去掉socket timeout來解決這個問題。
實際上,同步過程中l(wèi)istDatabases只需要獲取所有DB的名稱即可,并不需要size信息,所以我們的想法是給listDatabases命令加一個 {nameOnly: true}的選項,讓命令只返回所有DB的名稱信息,這樣整個開銷會很小,SERVER-3181也遇到類似問題,提了相同的解決思路,我們會在MongoDB云數(shù)據(jù)庫里加上這個選項用于主備同步,github pull request。
使用MongoDB時,建議用戶還是合理的控制下集合數(shù)量(集合數(shù)量太多,很可能存儲設計上也有問題,得好好review下),集合數(shù)量一旦大了,有可能面臨各種問題,就如同上述的場景l(fā)istDatabases耗時長,想監(jiān)控數(shù)據(jù)庫的容量使用情況都難。
上述問題其他的解決方案
Primary(同步源)上使用mmapv1引擎,內存足夠的情況下,listDatabases的開銷要更小些。
Secondary同步時不設置或設置更長的socket超時時間
加新節(jié)點上,將Primary的數(shù)據(jù)拷貝到新的節(jié)點,跳過initial sync |
|