mingjian01 发表于 2013-1-13 00:51:28

(转)SQLite入门与分析(四)---Page Cache之事务处理(2)

<span style="display: block; padding: 10px;">写在前面:个人认为pager层是SQLite实现最为核心的模块,它具有四大功能:I/O,页面缓存,并发控制和日志恢复。而这些功能不仅是上层Btree的基础,而且对系统的性能和健壮性有关至关重要的影响。其中并发控制和日志恢复是事务处理实现的基础。SQLite并发控制的机制非常简单——封锁机制;别外,它的查询优化机制也非常简单——基于索引。这一切使得整个SQLite的实现变得简单,SQLite变得很小,运行速度也非常快,所以,特别适合嵌入式设备。好了,接下来讨论事务的剩余部分。
6、修改位于用户进程空间的页面(Changing Database Pages In User Space)
页面的原始数据写入日志之后,就可以修改页面了——位于用户进程空间。每个数据库连接都有自己私有的空间,所以页面的变化只对该连接可见,而对其它连接的数据仍然是磁盘缓存中的数据。从这里可以明白一件事:一个进程在修改页面数据的同时,其它进程可以继续进行读操作。图中的红色表示修改的页面。
<span style=""><span style="">
http://images.cnblogs.com/cnblogs_com/hustcat/2009/commit-5.gif
7、日志文件刷入磁盘(Flushing The Rollback Journal File To Mass Storage)
接下来把日志文件的内容刷入磁盘,这对于数据库从意外中恢复来说是至关重要的一步。而且这通常也是一个耗时的操作,因为磁盘I/O速度很慢。
这个步骤不只把日志文件刷入磁盘那么简单,它的实现实际上分成两步:首先把日志文件的内容刷入磁盘(即页面数据);然后把日志文件中页面的数目写入日志文件头,再把header刷入磁盘(这一过程在代码中清晰可见)。
 
http://images.cnblogs.com/cnblogs_com/hustcat/2009/commit-6.gif
代码如下:
 
<div class="cnblogs_code">/***Sync 日志文件,保证所有的脏页面写入磁盘日志文件*/static int syncJournal(Pager *pPager){PgHdr *pPg;int rc = SQLITE_OK;/* Sync the journal before modifying the main database** (assuming there is a journal and it needs to be synced.)*/if( pPager->needSync ){    if( !pPager->tempFile ){      assert( pPager->journalOpen );      /* assert( !pPager->noSync ); // noSync might be set if synchronous      ** was turned off after the transaction was started.Ticket #615 */#ifndef NDEBUG      {      /* Make sure the pPager->nRec counter we are keeping agrees      ** with the nRec computed from the size of the journal file.      */      i64 jSz;      rc = sqlite3OsFileSize(pPager->jfd, &jSz);      if( rc!=0 ) return rc;      assert( pPager->journalOff==jSz );      }#endif      {      /* Write the nRec value into the journal file header. If in      ** full-synchronous mode, sync the journal first. This ensures that      ** all data has really hit the disk before nRec is updated to mark      ** it as a candidate for rollback.         */      if( pPager->fullSync ){          TRACE2("SYNC journal of %d\n", PAGERID(pPager));    //首先保证脏页面中所有的数据都已经写入日志文件          rc = sqlite3OsSync(pPager->jfd, 0);          if( rc!=0 ) return rc;      }      rc = sqlite3OsSeek(pPager->jfd,                           pPager->journalHdr + sizeof(aJournalMagic));      if( rc ) return rc;    //页面的数目写入日志文件      rc = write32bits(pPager->jfd, pPager->nRec);      if( rc ) return rc;      rc = sqlite3OsSeek(pPager->jfd, pPager->journalOff);      if( rc ) return rc;      }      TRACE2("SYNC journal of %d\n", PAGERID(pPager));      rc = sqlite3OsSync(pPager->jfd, pPager->full_fsync);      if( rc!=0 ) return rc;      pPager->journalStarted = 1;    }    pPager->needSync = 0;    /* Erase the needSync flag from every page.    */    //清除needSync标志位    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){      pPg->needSync = 0;    }    pPager->pFirstSynced = pPager->pFirst;}#ifndef NDEBUG/* If the Pager.needSync flag is clear then the PgHdr.needSync** flag must also be clear for all pages.Verify that this** invariant is true.*/else{    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){      assert( pPg->needSync==0 );    }    assert( pPager->pFirstSynced==pPager->pFirst );}#endifreturn rc;}
页: [1]
查看完整版本: (转)SQLite入门与分析(四)---Page Cache之事务处理(2)