SQLite入门与分析(四)---Page Cache之事务处理(2)
写在前面:个人认为pager层是SQLite实现最为核心的模块,它具有四大功能:I/O,页面缓存,并发控制和日志恢复。而这些功能不仅是上层Btree的基础,而且对系统的性能和健壮性有关至关重要的影响。其中并发控制和日志恢复是事务处理实现的基础。SQLite并发控制的机制非常简单——封锁机制;别外,它的查询优化机制也非常简单——基于索引。这一切使得整个SQLite的实现变得简单,SQLite变得很小,运行速度也非常快,所以,特别适合嵌入式设备。好了,接下来讨论事务的剩余部分。
7、日志文件刷入磁盘(Flushing The Rollback Journal File To Mass Storage)
代码如下:
/* **Sync日志文件,保证所有的脏页面写入磁盘日志文件 */ staticintsyncJournal(Pager*pPager){ PgHdr*pPg; intrc=SQLITE_OK; Syncthejournalbeforemodifyingthemaindatabase **(assumingthereisajournalanditneedstobesynced.) if(pPager->needSync){ if(!pPager->tempFile){ assert(pPager->journalOpen); assert(!pPager->noSync);//noSyncmightbesetifsynchronous **wasturnedoffafterthetransactionwasstarted.Ticket#615*/ #ifndefNDEBUG { MakesurethepPager->nReccounterwearekeepingagrees **withthenReccomputedfromthesizeofthejournalfile. */ i64jSz; rc=sqlite3OsFileSize(pPager->jfd,&jSz); if(rc!=0)returnrc; assert(pPager->journalOff==jSz); } #endif { WritethenRecvalueintothejournalfileheader.Ifin **full-synchronousmode,syncthejournalfirst.Thisensuresthat **alldatahasreallyhitthediskbeforenRecisupdatedtomark **itasacandidateforrollback. if(pPager->fullSync){ TRACE2("SYNCjournalof%dn",PAGERID(pPager)); //首先保证脏页面中所有的数据都已经写入日志文件 rc=sqlite3OsSync(pPager->jfd,0); returnrc; } rc=sqlite3OsSeek(pPager->jfd, pPager->journalHdr+sizeof(aJournalMagic)); if(rc)returnrc; 页面的数目写入日志文件 rc=write32bits(pPager->jfd,pPager->nRec); returnrc; rc=sqlite3OsSeek(pPager->jfd,pPager->journalOff); returnrc; } TRACE2( rc=sqlite3OsSync(pPager->jfd,pPager->full_fsync); returnrc; pPager->journalStarted=1; } pPager->needSync=0; ErasetheneedSyncflagfromeverypage. 清除needSync标志位 for(pPg=pPager->pAll;pPg;pPg=pPg->pNextAll){ pPg->needSync=0; } pPager->pFirstSynced=pPager->pFirst; } #ifndefNDEBUG IfthePager.needSyncflagisclearthenthePgHdr.needSync **flagmustalsobeclearforallpages.Verifythatthis **invariantistrue. else{ for(pPg=pPager->pAll;pPg;pPg=pPg->pNextAll){ assert(pPg->needSync==0); } assert(pPager->pFirstSynced==pPager->pFirst); } #endif returnrc; }
8、获取排斥锁(Obtaining An Exclusive Lock)
9、修改的页面写入文件(Writing Changes To The Database File)
以上两步的实现代码:
把所有的脏页面写入数据库 到这里开始获取EXCLUSIVEQ锁,并将页面写回操作系统文件 intpager_write_pagelist(PgHdr*pList){ Pager*pPager; intrc; if(pList==returnSQLITE_OK; pPager=pList->pPager; AtthispointtheremaybeeitheraRESERVEDorEXCLUSIVElockonthe **databasefile.IfthereisalreadyanEXCLUSIVElock,thefollowing **callstosqlite3OsLock()areno-ops. ** **MovingthelockfromRESERVEDtoEXCLUSIVEactuallyinvolvesgoing **throughanintermediatestatePENDING.APENDINGlockpreventsnew **readersfromattachingtothedatabasebutisunsufficientforusto **write.TheideaofaPENDINGlockistopreventnewreadersfrom **cominginwhilewewaitforexistingreaderstoclear. ** **WhilethepagerisintheRESERVEDstate,theoriginaldatabasefile **isunchangedandwecanrollbackwithouthavingtoplaybackthe **journalintotheoriginaldatabasefile.Oncewetransitionto **EXCLUSIVE,itmeansthedatabasefilehasbeenchangedandanyrollback **willrequireajournalplayback. 加EXCLUSIVE_LOCK锁 rc=pager_wait_on_lock(pPager,EXCLUSIVE_LOCK); if(rc!=SQLITE_OK){ returnrc; } while(pList){ assert(pList->dirty); rc=sqlite3OsSeek(pPager->fd,(pList->pgno-1)*(i64)pPager->pageSize); Iftherearedirtypagesinthepagecachewithpagenumbersgreater **thanPager.dbSize,thismeanssqlite3pager_truncate()wascalledto **makethefilesmaller(presumablybyauto-vacuumcode).Donotwrite **anysuchpagestothefile. if(pList->pgno<=pPager->dbSize){ char*pData=CODEC2(pPager,PGHDR_TO_DATA(pList),pList->pgno,128)">6); TRACE3(STORE%dpage%dn->pgno); 写入文件 rc=sqlite3OsWrite(pPager->fd,pData,pPager->pageSize); TEST_INCR(pPager->nWrite); } #ifndefNDEBUG else{ TRACE3(NOSTORE%dpage%dn->pgno); } #endif 设置dirty pList->dirty=0; #ifdefSQLITE_CHECK_PAGES pList->pageHash=pager_pagehash(pList); 指向下一个脏页面 pList=pList->pDirty; } returnSQLITE_OK; }
10、修改结果刷入存储设备(Flushing Changes To Mass Storage) 最后来看看这几步是如何实现的: 其实以上以上几步是在函数sqlite3BtreeSync()---btree.c中调用的(而关于该函数的调用后面再讲)。 代码如下: 同步btree对应的数据库文件 该函数返回之后,只需要提交写事务,删除日志文件intsqlite3BtreeSync(Btree*p,constchar*zMaster){ intrc=SQLITE_OK; if(p->inTrans==TRANS_WRITE){ BtShared*pBt=p->pBt; PgnonTrunc=0; #ifndefSQLITE_OMIT_AUTOVACUUM if(pBt->autoVacuum){ rc=autoVacuumCommit(pBt,&nTrunc); returnrc; } } 调用pager进行sync rc=sqlite3pager_sync(pBt->pPager,zMaster,nTrunc); } 把pager所有脏页面写回文件intsqlite3pager_sync(Pager*pPager,255)">char*zMaster,PgnonTrunc){ intrc=SQLITE_OK; TRACE4(DATABASESYNC:File=%szMaster=%snTrunc=%dn pPager->zFilename,nTrunc); Ifthisisanin-memorydb,ornopageshavebeenwrittento,orthis **functionhasalreadybeencalled,itisano-op. pager不处于PAGER_SYNCED状态,dirtyCache为1, 则进行sync操作if(pPager->state!=PAGER_SYNCED&&!MEMDB&&pPager->dirtyCache){ PgHdr*pPg; assert(pPager->journalOpen); Ifamasterjournalfilenamehasalreadybeenwrittentothe **journalfile,thennosyncisrequired.Thishappenswhenitis **written,thentheprocessfailstoupgradefromaRESERVEDtoan **EXCLUSIVElock.Thenexttimetheprocesstriestocommitthe **transactionthem-jnamewillhavealreadybeenwritten. if(!pPager->setMaster){ pager修改计数 rc=pager_incr_changecounter(pPager); if(rc!=SQLITE_OK)gotosync_exit; #ifndefSQLITE_OMIT_AUTOVACUUM if(nTrunc!=0){ Ifthistransactionhasmadethedatabasesmaller,thenallpages **beingdiscardedbythetruncationmustbewrittentothejournal **file. */ Pgnoi; void*pPage; intiSkip=PAGER_MJ_PGNO(pPager); for(i=nTrunc+1;i<=pPager->origDbSize;i++){ if(!(pPager->aInJournal[i/8]&(1<<(i&7)))&&i!=iSkip){ rc=sqlite3pager_get(pPager,i,&pPage); gotosync_exit; rc=sqlite3pager_write(pPage); sqlite3pager_unref(pPage); gotosync_exit; } } } #endif rc=writeMasterJournal(pPager,zMaster); gotosync_exit; sync日志文件 rc=syncJournal(pPager); gotosync_exit; } #ifndefSQLITE_OMIT_AUTOVACUUM 0){ rc=sqlite3pager_truncate(pPager,nTrunc); gotosync_exit; } Writealldirtypagestothedatabasefile*/ pPg=pager_get_all_dirty_pages(pPager); 把所有脏页面写回操作系统文件 rc=pager_write_pagelist(pPg); Syncthedatabasefile.sync数据库文件if(!pPager->noSync){ rc=sqlite3OsSync(pPager->fd,128)">0); } pPager->state=PAGER_SYNCED; }elseif(MEMDB&&nTrunc!= } sync_exit: returnrc; } 下图可以进一步解释该过程: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |