MongoDB數(shù)據(jù)緩存刷新機(jī)制
最近配合好幾個(gè)項(xiàng)目測(cè)試了MongoDB的寫(xiě)入性能。在內(nèi)存沒(méi)有用盡的情況下,雖然MongoDB只有一個(gè)更新線(xiàn)程,寫(xiě)入還是非??斓?,基本上能達(dá)到25000/s以上(索引數(shù)據(jù)用uuid_generate_randome和uuid_unparse隨機(jī)產(chǎn)生)。當(dāng)內(nèi)存用盡開(kāi)始往磁盤(pán)上刷臟頁(yè)的時(shí)候,性能有非常大的波動(dòng),即使調(diào)整了syncdelay也沒(méi)有太大改善。在測(cè)試中還出現(xiàn)了一個(gè)莫名其妙的情況:MongoDB會(huì)間歇性地釋放文件系統(tǒng)的cache。除了直接刪除表空間之外,很難想到有什么動(dòng)作可以誘發(fā)這個(gè)現(xiàn)象。在MongoDB開(kāi)發(fā)者論壇里描述了這個(gè)現(xiàn)象,但是 Eliot Horowitz認(rèn)為MongoDB內(nèi)部并沒(méi)有代碼會(huì)釋放文件系統(tǒng)cache。那么,讓我們?nèi)ピ创a里面看一下MongoDB緩存和刷新數(shù)據(jù)的機(jī)制。
首先找到mongod的入口(db/db.cpp),發(fā)現(xiàn)MongoDB的初始化步驟非常簡(jiǎn)單,概括起來(lái)就以下三步:
- int main(int argc, char* argv[], char *envp[] )
- {
- …
- Module::configAll( params );
- dataFileSync.go();
- …
- initAndListen(cmdLine.port, appsrvPath);
- …
- }
顯然,dataFileSync就是我們感興趣的那個(gè)類(lèi)。dataFileSync類(lèi)派生自BackgroundJob類(lèi),而B(niǎo)ackgroundJob 主要的功能就是生成一個(gè)后臺(tái)線(xiàn)程并指派任務(wù)。數(shù)據(jù)的刷新是一個(gè)不斷執(zhí)行的后臺(tái)任務(wù),在dataFileSync.run()里面可以找到刷數(shù)據(jù)的相關(guān)代碼:
- void run()
- {
- …
- Date_t start = jsTime();
- int numFiles = MemoryMappedFile::flushAll( true );
- time_flushing = (int) (jsTime() – start);
- globalFlushCounters.flushed(time_flushing);
- …
- }
從這一段代碼看,MongoDB會(huì)在syncdelay設(shè)定的周期內(nèi),采取同步的形式刷新所有的臟數(shù)據(jù)。再看一下flushAll是怎么刷新所有數(shù)據(jù)的:
- int MongoFile::flushAll( bool sync )
- {
- …
- set seen;
- while ( true ){
- auto_ptr f;
- {
- rwlock lk( mmmutex , false );
- for ( set::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ ){
- MongoFile * mmf = *i;
- if ( ! mmf )
- continue;
- if ( seen.count( mmf ) )
- continue;
- f.reset( mmf->prepareFlush() );
- seen.insert( mmf );
- break;
- }
- }
- if ( ! f.get() )
- break;
- f->flush();
- }
- return seen.size();
- }
上面這一段代碼實(shí)現(xiàn)的功能很簡(jiǎn)單,就是把mmfiles中所有MongoFile指針?biāo)玫膶?duì)象都flush()一次。不過(guò)在執(zhí)行flush()函數(shù)之前,需要先執(zhí)行prepareFlush()確保這個(gè)對(duì)象是可以執(zhí)行flush()函數(shù)的。下面是***真正執(zhí)行刷新操作的代碼:
- void MemoryMappedFile::flush(bool sync)
- {
- if ( view == 0 || fd == 0 )
- return;
- if ( msync(view, len, sync ? MS_SYNC : MS_ASYNC) )
- problem() << “msync ” << errnoWithDescription() << endl;
- }
終于刷新到磁盤(pán)了,呵呵。不過(guò)這篇blog只涉及到了數(shù)據(jù)刷新的代碼,至于如何緩存,且聽(tīng)下回分解。
【編輯推薦】
- 設(shè)計(jì)實(shí)例對(duì)比:MySQL vs MongoDB
- MongoDB基于Java、PHP的一般操作和用戶(hù)安全設(shè)置
- 在Windows環(huán)境下MongoDB搭建和簡(jiǎn)單操作
- 教你如何利用MySQL學(xué)習(xí)MongoDB
- 如何用Java操作MongoDB