- 求職 : Linux運(yùn)維
- 論壇徽章:
- 203
|
最近遇到一個(gè)user case,因?yàn)榧蠑?shù)量太多,導(dǎo)致Secondary節(jié)點(diǎn)無法進(jìn)行initial sync(主備同步的第一步,可理解為從Primary上全量拷貝數(shù)據(jù))。
副本集使用wiredtiger存儲(chǔ)引擎,一共60,000+集合,平均每個(gè)集合4個(gè)索引,wiredtiger的集合及每個(gè)索引都對應(yīng)一個(gè)單獨(dú)的文件來存儲(chǔ),數(shù)據(jù)目錄下總共300,000+文件,listDatabases命令執(zhí)行時(shí),會(huì)遍歷所有DB的每個(gè)集合,獲取集合及其索引文件占用的存儲(chǔ)空間信息,實(shí)現(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引擎時(shí),獲取集合及索引文件大小信息時(shí),需要打開一個(gè)特殊的cursor來讀取,整個(gè)listDatabases需要遍歷300,000+個(gè)文件來逐個(gè)獲取大小信息,導(dǎo)致整個(gè)命令的執(zhí)行開銷很大,總耗時(shí)在30s以上。
Secondary在執(zhí)行initial sync時(shí),其過程類似如下
刪除本地除local外的所有數(shù)據(jù)庫
執(zhí)行l(wèi)istDatabases命令,獲取同步源上所有DB的列表
針對每個(gè)DB,調(diào)用listCollections獲取所有集合信息,并遍歷所有集合,同步集合內(nèi)的文檔并建立索引。
……
initial sync執(zhí)行時(shí),設(shè)置了socket timeout為30s,而listDatabases的執(zhí)行時(shí)間是超過30s的,導(dǎo)致同步在listDatabases時(shí)就一直超時(shí)失敗,10次重試后仍然失敗,Secondary進(jìn)程就直接退出了。
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
問題已反饋給官方團(tuán)隊(duì),詳情見SERVER-24948,在3.4版本里會(huì)去掉socket timeout來解決這個(gè)問題。
實(shí)際上,同步過程中l(wèi)istDatabases只需要獲取所有DB的名稱即可,并不需要size信息,所以我們的想法是給listDatabases命令加一個(gè) {nameOnly: true}的選項(xiàng),讓命令只返回所有DB的名稱信息,這樣整個(gè)開銷會(huì)很小,SERVER-3181也遇到類似問題,提了相同的解決思路,我們會(huì)在MongoDB云數(shù)據(jù)庫里加上這個(gè)選項(xiàng)用于主備同步,github pull request。
使用MongoDB時(shí),建議用戶還是合理的控制下集合數(shù)量(集合數(shù)量太多,很可能存儲(chǔ)設(shè)計(jì)上也有問題,得好好review下),集合數(shù)量一旦大了,有可能面臨各種問題,就如同上述的場景l(fā)istDatabases耗時(shí)長,想監(jiān)控?cái)?shù)據(jù)庫的容量使用情況都難。
上述問題其他的解決方案
Primary(同步源)上使用mmapv1引擎,內(nèi)存足夠的情況下,listDatabases的開銷要更小些。
Secondary同步時(shí)不設(shè)置或設(shè)置更長的socket超時(shí)時(shí)間
加新節(jié)點(diǎn)上,將Primary的數(shù)據(jù)拷貝到新的節(jié)點(diǎn),跳過initial sync |
|