SQLite学习之路⑬ wal读写事物(2021SC@SDUSC)

SQLite 学习之路 第十二节 wal读写事物


2021SC@SDUSC

开始一个读事务时,读者记录WAL中最后一个有效的框的索引。读者用myFrame值记录所有的子顺序读操作。新的事务能被添加到WAl中,只要读者使用原始的myFrame值并且忽略新的增加的内容它将看到一个快照。这个技术允许多事务并行读不同的数据库内容。

/*
** Begin a read transaction on the database.
**
** This routine used to be called sqlite3OpenSnapshot() and with good reason:
** it takes a snapshot of the state of the WAL and wal-index for the current
** instant in time.  The current thread will continue to use this snapshot.
** Other threads might append new content to the WAL and wal-index but
** that extra content is ignored by the current thread.
**
** If the database contents have changes since the previous read
** transaction, then *pChanged is set to 1 before returning.  The
** Pager layer will use this to know that its cache is stale and
** needs to be flushed.
*/
SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
 int rc;                         /* Return code */
 int cnt = 0;                    /* Number of TryBeginRead attempts */
#ifdef SQLITE_ENABLE_SNAPSHOT
 int bChanged = 0;
 WalIndexHdr *pSnapshot = pWal->pSnapshot;
#endif

 assert( pWal->ckptLock==0 );

#ifdef SQLITE_ENABLE_SNAPSHOT
 if( pSnapshot ){
   if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
     bChanged = 1;
   }

   /* It is possible that there is a checkpointer thread running
   ** concurrent with this code. If this is the case, it may be that the
   ** checkpointer has already determined that it will checkpoint
   ** snapshot X, where X is later in the wal file than pSnapshot, but
   ** has not yet set the pInfo->nBackfillAttempted variable to indicate
   ** its intent. To avoid the race condition this leads to, ensure that
   ** there is no checkpointer process by taking a shared CKPT lock
   ** before checking pInfo->nBackfillAttempted.  */
   (void)walEnableBlocking(pWal);
   rc = walLockShared(pWal, WAL_CKPT_LOCK);
   walDisableBlocking(pWal);

   if( rc!=SQLITE_OK ){
     return rc;
   }
   pWal->ckptLock = 1;
 }
#endif

 do{
   rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
 }while( rc==WAL_RETRY );
 testcase( (rc&0xff)==SQLITE_BUSY );
 testcase( (rc&0xff)==SQLITE_IOERR );
 testcase( rc==SQLITE_PROTOCOL );
 testcase( rc==SQLITE_OK );

#ifdef SQLITE_ENABLE_SNAPSHOT
 if( rc==SQLITE_OK ){
   if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
     /* At this point the client has a lock on an aReadMark[] slot holding
     ** a value equal to or smaller than pSnapshot->mxFrame, but pWal->hdr
     ** is populated with the wal-index header corresponding to the head
     ** of the wal file. Verify that pSnapshot is still valid before
     ** continuing.  Reasons why pSnapshot might no longer be valid:
     **
     **    (1)  The WAL file has been reset since the snapshot was taken.
     **         In this case, the salt will have changed.
     **
     **    (2)  A checkpoint as been attempted that wrote frames past
     **         pSnapshot->mxFrame into the database file.  Note that the
     **         checkpoint need not have completed for this to cause problems.
     */
     volatile WalCkptInfo *pInfo = walCkptInfo(pWal);

     assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
     assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );

     /* Check that the wal file has not been wrapped. Assuming that it has
     ** not, also check that no checkpointer has attempted to checkpoint any
     ** frames beyond pSnapshot->mxFrame. If either of these conditions are
     ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
     ** with *pSnapshot and set *pChanged as appropriate for opening the
     ** snapshot.  */
     if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
      && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
     ){
       assert( pWal->readLock>0 );
       memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
       *pChanged = bChanged;
     }else{
       rc = SQLITE_ERROR_SNAPSHOT;
     }

     /* A client using a non-current snapshot may not ignore any frames
     ** from the start of the wal file. This is because, for a system
     ** where (minFrame < iSnapshot < maxFrame), a checkpointer may
     ** have omitted to checkpoint a frame earlier than minFrame in
     ** the file because there exists a frame after iSnapshot that
     ** is the same database page.  */
     pWal->minFrame = 1;

     if( rc!=SQLITE_OK ){
       sqlite3WalEndReadTransaction(pWal);
     }
   }
 }

 /* Release the shared CKPT lock obtained above. */
 if( pWal->ckptLock ){
   assert( pSnapshot );
   walUnlockShared(pWal, WAL_CKPT_LOCK);
   pWal->ckptLock = 0;
 }
#endif
 return rc;
}

从数据库中读一页(假设为页P),读者首先检查WAL来确定是否包含页P,如果包含,那么页P的最后一个有效实例是在该 WAL框中。如果WAl没有包含页P,那么页P就从数据库文件中读入。

结束读事务:当读事务结束时,调用此方法,释放锁.

/*
** Finish with a read transaction.  All this does is release the
** read-lock.
*/
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
sqlite3WalEndWriteTransaction(pWal);
if( pWal->readLock>=0 ){
  walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  pWal->readLock = -1;
}
}

写事务开始前应该有个一个sqlite3WalBeginWriteTransaction(Wal *pWal)已经被调用开始了一个读事务。 写事务开启前必须读事务在运行

/*
** This function starts a write transaction on the WAL.
**
** A read transaction must have already been started by a prior call
** to sqlite3WalBeginReadTransaction().
**
** If another thread or process has written into the database since
** the read transaction was started, then it is not possible for this
** thread to write as doing so would cause a fork.  So this routine
** returns SQLITE_BUSY in that case and no write transaction is started.
**
** There can only be a single writer active at a time.
*/
SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
int rc;

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/* If the write-lock is already held, then it was obtained before the
** read-transaction was even opened, making this call a no-op.
** Return early. */
if( pWal->writeLock ){
  assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) );
  return SQLITE_OK;
}
#endif

/* Cannot start a write transaction without first holding a read
** transaction. */
assert( pWal->readLock>=0 );
assert( pWal->writeLock==0 && pWal->iReCksum==0 );

if( pWal->readOnly ){
  return SQLITE_READONLY;
}

/* Only one writer allowed at a time.  Get the write lock.  Return
** SQLITE_BUSY if unable.
*/
rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
if( rc ){
  return rc;
}
pWal->writeLock = 1;

/* If another connection has written to the database file since the
** time the read transaction on this connection was started, then
** the write is disallowed.
*/
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
  walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
  pWal->writeLock = 0;
  rc = SQLITE_BUSY_SNAPSHOT;
}

return rc;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值