mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Two optimizations to the pager: (1) Write dirty pages back to the database
file in order and (2) Keep a separate list of in-memory pages that are in the checkpoint journal in order to speed a checkpoint commit. (CVS 783) FossilOrigin-Name: a6ef6657a4377684dc2fce7be2bbf009fd2d2f37
This commit is contained in:
12
manifest
12
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Try\sto\sbetter\sdetect\swhen\sthe\slibrary\sis\scompiled\sfor\slarge\sfile\ssupport\s(LFS)\nbut\sthe\ssupport\sis\snot\savailable\sin\sthe\shost\sOS\skernel.\s(CVS\s782)
|
C Two\soptimizations\sto\sthe\spager:\s(1)\sWrite\sdirty\spages\sback\sto\sthe\sdatabase\nfile\sin\sorder\sand\s(2)\sKeep\sa\sseparate\slist\sof\sin-memory\spages\sthat\sare\sin\nthe\scheckpoint\sjournal\sin\sorder\sto\sspeed\sa\scheckpoint\scommit.\s(CVS\s783)
|
||||||
D 2002-11-09T00:33:16
|
D 2002-11-10T23:32:57
|
||||||
F Makefile.in d6c9a85c2a5e696843201d090dcf8bf2f8716f2a
|
F Makefile.in d6c9a85c2a5e696843201d090dcf8bf2f8716f2a
|
||||||
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
|
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
|
||||||
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
||||||
@@ -32,7 +32,7 @@ F src/main.c f04f93b8928d6d85976e5137fea146a46de1fd6e
|
|||||||
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
|
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
|
||||||
F src/os.c caf5a34b35a2d99a58457517261c879ac29b0a05
|
F src/os.c caf5a34b35a2d99a58457517261c879ac29b0a05
|
||||||
F src/os.h 1caaea972d1c0401cfe6300aba51fb0f6fe435c9
|
F src/os.h 1caaea972d1c0401cfe6300aba51fb0f6fe435c9
|
||||||
F src/pager.c 292853d08658df23f1044fba1a793a210475964e
|
F src/pager.c 27d9b94641e968bafabfd2119d9ba4304ccb69a4
|
||||||
F src/pager.h 6991c9c2dc5e4c7f2df4d4ba47d1c6458f763a32
|
F src/pager.h 6991c9c2dc5e4c7f2df4d4ba47d1c6458f763a32
|
||||||
F src/parse.y 469c9636ff713e63c00234662209f11668671ae9
|
F src/parse.y 469c9636ff713e63c00234662209f11668671ae9
|
||||||
F src/printf.c 5c50fc1da75c8f5bf432b1ad17d91d6653acd167
|
F src/printf.c 5c50fc1da75c8f5bf432b1ad17d91d6653acd167
|
||||||
@@ -149,7 +149,7 @@ F www/speed.tcl a20a792738475b68756ea7a19321600f23d1d803
|
|||||||
F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
|
F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
|
||||||
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
|
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
|
||||||
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
|
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
|
||||||
P 2008b56fe11e49d52e28f47d14ccd70504e6c094
|
P a29d60ecc5ee3f535142a81f56eecbef7875ef22
|
||||||
R 4ac4711a79ff9ee10d8741795898da69
|
R 52d1e18697b72d70d44fdac37bed7445
|
||||||
U drh
|
U drh
|
||||||
Z c8abc0129b7b7c57014c7437e465381e
|
Z 7623b80b72f443af5ce24d861a13e847
|
||||||
|
@@ -1 +1 @@
|
|||||||
a29d60ecc5ee3f535142a81f56eecbef7875ef22
|
a6ef6657a4377684dc2fce7be2bbf009fd2d2f37
|
162
src/pager.c
162
src/pager.c
@@ -18,7 +18,7 @@
|
|||||||
** file simultaneously, or one process from reading the database while
|
** file simultaneously, or one process from reading the database while
|
||||||
** another is writing.
|
** another is writing.
|
||||||
**
|
**
|
||||||
** @(#) $Id: pager.c,v 1.56 2002/11/09 00:33:16 drh Exp $
|
** @(#) $Id: pager.c,v 1.57 2002/11/10 23:32:57 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "os.h" /* Must be first to enable large file support */
|
#include "os.h" /* Must be first to enable large file support */
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
@@ -74,6 +74,8 @@ struct PgHdr {
|
|||||||
int nRef; /* Number of users of this page */
|
int nRef; /* Number of users of this page */
|
||||||
PgHdr *pNextFree, *pPrevFree; /* Freelist of pages where nRef==0 */
|
PgHdr *pNextFree, *pPrevFree; /* Freelist of pages where nRef==0 */
|
||||||
PgHdr *pNextAll, *pPrevAll; /* A list of all pages */
|
PgHdr *pNextAll, *pPrevAll; /* A list of all pages */
|
||||||
|
PgHdr *pNextCkpt, *pPrevCkpt; /* List of pages in the checkpoint journal */
|
||||||
|
PgHdr *pSort; /* Next in list of pages to be written */
|
||||||
u8 inJournal; /* TRUE if has been written to journal */
|
u8 inJournal; /* TRUE if has been written to journal */
|
||||||
u8 inCkpt; /* TRUE if written to the checkpoint journal */
|
u8 inCkpt; /* TRUE if written to the checkpoint journal */
|
||||||
u8 dirty; /* TRUE if we need to write back changes */
|
u8 dirty; /* TRUE if we need to write back changes */
|
||||||
@@ -130,6 +132,7 @@ struct Pager {
|
|||||||
u8 *aInCkpt; /* One bit for each page in the database */
|
u8 *aInCkpt; /* One bit for each page in the database */
|
||||||
PgHdr *pFirst, *pLast; /* List of free pages */
|
PgHdr *pFirst, *pLast; /* List of free pages */
|
||||||
PgHdr *pAll; /* List of all pages */
|
PgHdr *pAll; /* List of all pages */
|
||||||
|
PgHdr *pCkpt; /* List of pages in the checkpoint journal */
|
||||||
PgHdr *aHash[N_PG_HASH]; /* Hash table to map page number of PgHdr */
|
PgHdr *aHash[N_PG_HASH]; /* Hash table to map page number of PgHdr */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -253,6 +256,45 @@ static int pager_errcode(Pager *pPager){
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Add or remove a page from the list of all pages that are in the
|
||||||
|
** checkpoint journal.
|
||||||
|
**
|
||||||
|
** The Pager keeps a separate list of pages that are currently in
|
||||||
|
** the checkpoint journal. This helps the sqlitepager_ckpt_commit()
|
||||||
|
** routine run MUCH faster for the common case where there are many
|
||||||
|
** pages in memory but only a few are in the checkpoint journal.
|
||||||
|
*/
|
||||||
|
static void page_add_to_ckpt_list(PgHdr *pPg){
|
||||||
|
Pager *pPager = pPg->pPager;
|
||||||
|
if( pPg->inCkpt ) return;
|
||||||
|
assert( pPg->pPrevCkpt==0 && pPg->pNextCkpt==0 );
|
||||||
|
pPg->pPrevCkpt = 0;
|
||||||
|
if( pPager->pCkpt ){
|
||||||
|
pPager->pCkpt->pPrevCkpt = pPg;
|
||||||
|
}
|
||||||
|
pPg->pNextCkpt = pPager->pCkpt;
|
||||||
|
pPager->pCkpt = pPg;
|
||||||
|
pPg->inCkpt = 1;
|
||||||
|
}
|
||||||
|
static void page_remove_from_ckpt_list(PgHdr *pPg){
|
||||||
|
if( !pPg->inCkpt ) return;
|
||||||
|
if( pPg->pPrevCkpt ){
|
||||||
|
assert( pPg->pPrevCkpt->pNextCkpt==pPg );
|
||||||
|
pPg->pPrevCkpt->pNextCkpt = pPg->pNextCkpt;
|
||||||
|
}else{
|
||||||
|
assert( pPg->pPager->pCkpt==pPg );
|
||||||
|
pPg->pPager->pCkpt = pPg->pNextCkpt;
|
||||||
|
}
|
||||||
|
if( pPg->pNextCkpt ){
|
||||||
|
assert( pPg->pNextCkpt->pPrevCkpt==pPg );
|
||||||
|
pPg->pNextCkpt->pPrevCkpt = pPg->pPrevCkpt;
|
||||||
|
}
|
||||||
|
pPg->pNextCkpt = 0;
|
||||||
|
pPg->pPrevCkpt = 0;
|
||||||
|
pPg->inCkpt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Find a page in the hash table given its page number. Return
|
** Find a page in the hash table given its page number. Return
|
||||||
** a pointer to the page or NULL if not found.
|
** a pointer to the page or NULL if not found.
|
||||||
@@ -752,6 +794,41 @@ int sqlitepager_ref(void *pData){
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The parameters are pointers to the head of two sorted lists
|
||||||
|
** of page headers. Merge these two lists together and return
|
||||||
|
** a single sorted list. This routine forms the core of the
|
||||||
|
** merge-sort algorithm that sorts dirty pages into accending
|
||||||
|
** order prior to writing them back to the disk.
|
||||||
|
**
|
||||||
|
** In the case of a tie, left sorts in front of right.
|
||||||
|
**
|
||||||
|
** Headers are sorted in order of ascending page number.
|
||||||
|
*/
|
||||||
|
static PgHdr *page_merge(PgHdr *pLeft, PgHdr *pRight){
|
||||||
|
PgHdr sHead;
|
||||||
|
PgHdr *pTail;
|
||||||
|
pTail = &sHead;
|
||||||
|
pTail->pSort = 0;
|
||||||
|
while( pLeft && pRight ){
|
||||||
|
if( pLeft->pgno<=pRight->pgno ){
|
||||||
|
pTail->pSort = pLeft;
|
||||||
|
pLeft = pLeft->pSort;
|
||||||
|
}else{
|
||||||
|
pTail->pSort = pRight;
|
||||||
|
pRight = pRight->pSort;
|
||||||
|
}
|
||||||
|
pTail = pTail->pSort;
|
||||||
|
}
|
||||||
|
if( pLeft ){
|
||||||
|
pTail->pSort = pLeft;
|
||||||
|
}else if( pRight ){
|
||||||
|
pTail->pSort = pRight;
|
||||||
|
}
|
||||||
|
return sHead.pSort;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Sync the journal and then write all free dirty pages to the database
|
** Sync the journal and then write all free dirty pages to the database
|
||||||
** file.
|
** file.
|
||||||
@@ -768,10 +845,24 @@ int sqlitepager_ref(void *pData){
|
|||||||
** If we are writing to temporary database, there is no need to preserve
|
** If we are writing to temporary database, there is no need to preserve
|
||||||
** the integrity of the journal file, so we can save time and skip the
|
** the integrity of the journal file, so we can save time and skip the
|
||||||
** fsync().
|
** fsync().
|
||||||
|
**
|
||||||
|
** This routine goes to the extra trouble of sorting all the dirty
|
||||||
|
** pages by their page number prior to writing them. Tests show that
|
||||||
|
** writing pages in order by page number gives a modest speed improvement
|
||||||
|
** under Linux.
|
||||||
*/
|
*/
|
||||||
static int syncAllPages(Pager *pPager){
|
static int syncAllPages(Pager *pPager){
|
||||||
PgHdr *pPg;
|
PgHdr *pPg;
|
||||||
|
PgHdr *pToWrite;
|
||||||
|
# define NSORT 28
|
||||||
|
Pgno lastPgno;
|
||||||
|
int i;
|
||||||
|
PgHdr *apSorter[NSORT];
|
||||||
int rc = SQLITE_OK;
|
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->needSync ){
|
||||||
if( !pPager->tempFile ){
|
if( !pPager->tempFile ){
|
||||||
rc = sqliteOsSync(&pPager->jfd);
|
rc = sqliteOsSync(&pPager->jfd);
|
||||||
@@ -779,13 +870,57 @@ static int syncAllPages(Pager *pPager){
|
|||||||
}
|
}
|
||||||
pPager->needSync = 0;
|
pPager->needSync = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Create a list of all dirty pages
|
||||||
|
*/
|
||||||
|
pToWrite = 0;
|
||||||
for(pPg=pPager->pFirst; pPg; pPg=pPg->pNextFree){
|
for(pPg=pPager->pFirst; pPg; pPg=pPg->pNextFree){
|
||||||
if( pPg->dirty ){
|
if( pPg->dirty ){
|
||||||
|
pPg->pSort = pToWrite;
|
||||||
|
pToWrite = pPg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sort the list of dirty pages into accending order by
|
||||||
|
** page number
|
||||||
|
*/
|
||||||
|
for(i=0; i<NSORT; i++){
|
||||||
|
apSorter[i] = 0;
|
||||||
|
}
|
||||||
|
while( pToWrite ){
|
||||||
|
pPg = pToWrite;
|
||||||
|
pToWrite = pPg->pSort;
|
||||||
|
pPg->pSort = 0;
|
||||||
|
for(i=0; i<NSORT-1; i++){
|
||||||
|
if( apSorter[i]==0 ){
|
||||||
|
apSorter[i] = pPg;
|
||||||
|
break;
|
||||||
|
}else{
|
||||||
|
pPg = page_merge(apSorter[i], pPg);
|
||||||
|
apSorter[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( i>=NSORT-1 ){
|
||||||
|
apSorter[NSORT-1] = page_merge(apSorter[NSORT-1],pPg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pToWrite = 0;
|
||||||
|
for(i=0; i<NSORT; i++){
|
||||||
|
pToWrite = page_merge(apSorter[i], pToWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write all dirty pages back to the database and mark
|
||||||
|
** them all clean.
|
||||||
|
*/
|
||||||
|
lastPgno = 0;
|
||||||
|
for(pPg=pToWrite; pPg; pPg=pPg->pSort){
|
||||||
|
if( lastPgno==0 || pPg->pgno!=lastPgno-1 ){
|
||||||
sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE);
|
sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE);
|
||||||
|
}
|
||||||
rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
||||||
if( rc!=SQLITE_OK ) break;
|
if( rc!=SQLITE_OK ) break;
|
||||||
pPg->dirty = 0;
|
pPg->dirty = 0;
|
||||||
}
|
lastPgno = pPg->pgno;
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -984,10 +1119,11 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
|||||||
}else{
|
}else{
|
||||||
pPg->inJournal = 0;
|
pPg->inJournal = 0;
|
||||||
}
|
}
|
||||||
if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize ){
|
if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize
|
||||||
pPg->inCkpt = (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0;
|
&& (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0 ){
|
||||||
|
page_add_to_ckpt_list(pPg);
|
||||||
}else{
|
}else{
|
||||||
pPg->inCkpt = 0;
|
page_remove_from_ckpt_list(pPg);
|
||||||
}
|
}
|
||||||
pPg->dirty = 0;
|
pPg->dirty = 0;
|
||||||
pPg->nRef = 1;
|
pPg->nRef = 1;
|
||||||
@@ -1248,7 +1384,7 @@ int sqlitepager_write(void *pData){
|
|||||||
pPg->inJournal = 1;
|
pPg->inJournal = 1;
|
||||||
if( pPager->ckptInUse ){
|
if( pPager->ckptInUse ){
|
||||||
pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
||||||
pPg->inCkpt = 1;
|
page_add_to_ckpt_list(pPg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1268,7 +1404,7 @@ int sqlitepager_write(void *pData){
|
|||||||
}
|
}
|
||||||
assert( pPager->aInCkpt!=0 );
|
assert( pPager->aInCkpt!=0 );
|
||||||
pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
||||||
pPg->inCkpt = 1;
|
page_add_to_ckpt_list(pPg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the database size and return.
|
/* Update the database size and return.
|
||||||
@@ -1352,14 +1488,14 @@ void sqlitepager_dont_rollback(void *pData){
|
|||||||
pPg->inJournal = 1;
|
pPg->inJournal = 1;
|
||||||
if( pPager->ckptInUse ){
|
if( pPager->ckptInUse ){
|
||||||
pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
||||||
pPg->inCkpt = 1;
|
page_add_to_ckpt_list(pPg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
|
if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
|
||||||
assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
|
assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
|
||||||
assert( pPager->aInCkpt!=0 );
|
assert( pPager->aInCkpt!=0 );
|
||||||
pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
||||||
pPg->inCkpt = 1;
|
page_add_to_ckpt_list(pPg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1521,15 +1657,19 @@ ckpt_begin_failed:
|
|||||||
*/
|
*/
|
||||||
int sqlitepager_ckpt_commit(Pager *pPager){
|
int sqlitepager_ckpt_commit(Pager *pPager){
|
||||||
if( pPager->ckptInUse ){
|
if( pPager->ckptInUse ){
|
||||||
PgHdr *pPg;
|
PgHdr *pPg, *pNext;
|
||||||
sqliteOsSeek(&pPager->cpfd, 0);
|
sqliteOsSeek(&pPager->cpfd, 0);
|
||||||
sqliteOsTruncate(&pPager->cpfd, 0);
|
sqliteOsTruncate(&pPager->cpfd, 0);
|
||||||
pPager->ckptInUse = 0;
|
pPager->ckptInUse = 0;
|
||||||
sqliteFree( pPager->aInCkpt );
|
sqliteFree( pPager->aInCkpt );
|
||||||
pPager->aInCkpt = 0;
|
pPager->aInCkpt = 0;
|
||||||
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
|
for(pPg=pPager->pCkpt; pPg; pPg=pNext){
|
||||||
|
pNext = pPg->pNextCkpt;
|
||||||
|
assert( pPg->inCkpt );
|
||||||
pPg->inCkpt = 0;
|
pPg->inCkpt = 0;
|
||||||
|
pPg->pPrevCkpt = pPg->pNextCkpt = 0;
|
||||||
}
|
}
|
||||||
|
pPager->pCkpt = 0;
|
||||||
}
|
}
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user