1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Fix multiple performance regressions (ticket #2298 among them)

and add tests to make sure they do not come back. (CVS 3839)

FossilOrigin-Name: 32bb2d5859906b4fb0f6083eedd7f3a81b9cf5e2
This commit is contained in:
drh
2007-04-13 02:14:30 +00:00
parent 994c80af27
commit 538f570cd1
9 changed files with 339 additions and 98 deletions

View File

@@ -18,7 +18,7 @@
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.326 2007/04/09 11:20:54 danielk1977 Exp $
** @(#) $Id: pager.c,v 1.327 2007/04/13 02:14:30 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
@@ -162,6 +162,7 @@ struct PgHdr {
u8 dirty; /* TRUE if we need to write back changes */
u8 needSync; /* Sync journal before writing this page */
u8 alwaysRollback; /* Disable DontRollback() for this page */
u8 needRead; /* Read content if PagerWrite() is called */
short int nRef; /* Number of users of this page */
PgHdr *pDirty, *pPrevDirty; /* Dirty pages */
u32 notUsed; /* Buffer space */
@@ -297,15 +298,22 @@ struct Pager {
};
/*
** If SQLITE_TEST is defined then increment the variable given in
** the argument
** The following global variables hold counters used for
** testing purposes only. These variables do not exist in
** a non-testing build. These variables are not thread-safe.
*/
#ifdef SQLITE_TEST
# define TEST_INCR(x) x++
int sqlite3_pager_readdb_count = 0; /* Number of full pages read from DB */
int sqlite3_pager_writedb_count = 0; /* Number of full pages written to DB */
int sqlite3_pager_writej_count = 0; /* Number of pages written to journal */
int sqlite3_pager_pgfree_count = 0; /* Number of cache pages freed */
# define PAGER_INCR(v) v++
#else
# define TEST_INCR(x)
# define PAGER_INCR(v)
#endif
/*
** Journal files begin with the following magic string. The data
** was obtained from /dev/random. It is used only as a sanity check.
@@ -898,6 +906,8 @@ static void pager_reset(Pager *pPager){
PgHdr *pPg, *pNext;
if( pPager->errCode ) return;
for(pPg=pPager->pAll; pPg; pPg=pNext){
IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
PAGER_INCR(sqlite3_pager_pgfree_count);
pNext = pPg->pNextAll;
sqliteFree(pPg);
}
@@ -1219,51 +1229,6 @@ delmaster_out:
return rc;
}
#if 0
/*
** Make every page in the cache agree with what is on disk. In other words,
** reread the disk to reset the state of the cache.
**
** This routine is called after a rollback in which some of the dirty cache
** pages had never been written out to disk. We need to roll back the
** cache content and the easiest way to do that is to reread the old content
** back from the disk.
*/
static int pager_reload_cache(Pager *pPager){
PgHdr *pPg;
int rc = SQLITE_OK;
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
char *zBuf = pPager->pTmpSpace; /* Temp storage for one page */
if( !pPg->dirty ) continue;
if( (int)pPg->pgno <= pPager->origDbSize ){
rc = sqlite3OsSeek(pPager->fd, pPager->pageSize*(i64)(pPg->pgno-1));
if( rc==SQLITE_OK ){
rc = sqlite3OsRead(pPager->fd, zBuf, pPager->pageSize);
}
PAGERTRACE3("REFETCH %d page %d\n", PAGERID(pPager), pPg->pgno);
if( rc ) break;
CODEC1(pPager, zBuf, pPg->pgno, 2);
}else{
memset(zBuf, 0, pPager->pageSize);
}
if( pPg->nRef==0 || memcmp(zBuf, PGHDR_TO_DATA(pPg), pPager->pageSize) ){
memcpy(PGHDR_TO_DATA(pPg), zBuf, pPager->pageSize);
if( pPager->xReiniter ){
pPager->xReiniter(pPg, pPager->pageSize);
}else{
memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra);
}
}
pPg->needSync = 0;
pPg->dirty = 0;
#ifdef SQLITE_CHECK_PAGES
pPg->pageHash = pager_pagehash(pPg);
#endif
}
pPager->pDirty = 0;
return rc;
}
#endif
static void pager_truncate_cache(Pager *pPager);
@@ -2049,6 +2014,8 @@ static void pager_truncate_cache(Pager *pPager){
ppPg = &pPg->pNextAll;
}else{
*ppPg = pPg->pNextAll;
IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
PAGER_INCR(sqlite3_pager_pgfree_count);
unlinkPage(pPg);
makeClean(pPg);
sqliteFree(pPg);
@@ -2470,9 +2437,10 @@ static int pager_write_pagelist(PgHdr *pList){
if( pList->pgno<=pPager->dbSize ){
char *pData = CODEC2(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
PAGERTRACE3("STORE %d page %d\n", PAGERID(pPager), pList->pgno);
IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno))
IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno));
rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize);
TEST_INCR(pPager->nWrite);
PAGER_INCR(sqlite3_pager_writedb_count);
PAGER_INCR(pPager->nWrite);
}
#ifndef NDEBUG
else{
@@ -2674,6 +2642,8 @@ int sqlite3PagerReleaseMemory(int nReq){
pTmp->pNextAll = pPg->pNextAll;
}
nReleased += sqliteAllocSize(pPg);
IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
PAGER_INCR(sqlite3_pager_pgfree_count);
sqliteFree(pPg);
}
@@ -2706,7 +2676,9 @@ static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){
rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg),
pPager->pageSize);
}
IOTRACE(("PGIN %p %d\n", pPager, pgno))
PAGER_INCR(sqlite3_pager_readdb_count);
PAGER_INCR(pPager->nRead);
IOTRACE(("PGIN %p %d\n", pPager, pgno));
PAGERTRACE3("FETCH %d page %d\n", PAGERID(pPager), pPg->pgno);
CODEC1(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
return rc;
@@ -2839,6 +2811,7 @@ static int pagerSharedLock(Pager *pPager){
if( iChangeCounter!=pPager->iChangeCount ){
pager_reset(pPager);
pPager->iChangeCount = iChangeCounter;
}
}
}
@@ -2959,12 +2932,20 @@ pager_allocate_out:
** Since _lookup() never goes to disk, it never has to deal with locks
** or journal files.
**
** If clrFlag is false, the page contents are actually read from disk.
** If clfFlag is true, it means the page is about to be erased and
** rewritten without first being read so there is no point it doing
** the disk I/O.
** If noContent is false, the page contents are actually read from disk.
** If noContent is true, it means that we do not care about the contents
** of the page at this time, so do not do a disk read. Just fill in the
** page content with zeros. But mark the fact that we have not read the
** content by setting the PgHdr.needRead flag. Later on, if
** sqlite3PagerWrite() is called on this page, that means that the
** content is needed and the disk read should occur at that point.
*/
int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag){
int sqlite3PagerAcquire(
Pager *pPager, /* The pager open on the database file */
Pgno pgno, /* Page number to fetch */
DbPage **ppPage, /* Write a pointer to the page here */
int noContent /* Do not bother reading content from disk if true */
){
PgHdr *pPg;
int rc;
@@ -3000,7 +2981,7 @@ int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag){
/* The requested page is not in the page cache. */
int nMax;
int h;
TEST_INCR(pPager->nMiss);
PAGER_INCR(pPager->nMiss);
rc = pagerAllocatePage(pPager, &pPg);
if( rc!=SQLITE_OK ){
return rc;
@@ -3036,18 +3017,22 @@ int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag){
/* Populate the page with data, either by reading from the database
** file, or by setting the entire page to zero.
*/
if( nMax<(int)pgno || MEMDB || (clrFlag && !pPager->alwaysRollback) ){
if( nMax<(int)pgno || MEMDB || noContent ){
memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize);
pPg->needRead = noContent;
IOTRACE(("ZERO %p %d\n", pPager, pgno));
}else{
rc = readDbPage(pPager, pPg, pgno);
if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
pPg->pgno = 0;
sqlite3PagerUnref(pPg);
return rc;
}else{
TEST_INCR(pPager->nRead);
}
}
/* If this was page 1, then restore the value of Pager.iChangeCount */
if( pgno==1 ){
pPager->iChangeCount = retrieve32bits(pPg, 24);
}
/* Link the page into the page hash table */
h = pgno & (pPager->nHash-1);
@@ -3065,7 +3050,7 @@ int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag){
}else{
/* The requested page is in the page cache. */
assert(pPager->nRef>0 || pgno==1);
TEST_INCR(pPager->nHit);
PAGER_INCR(pPager->nHit);
page_ref(pPg);
}
*ppPage = pPg;
@@ -3365,6 +3350,23 @@ static int pager_write(PgHdr *pPg){
CHECK_PAGE(pPg);
/* If this page was previously acquired with noContent==1, that means
** we didn't really read in the content of the page. This can happen
** (for example) when the page is being moved to the freelist. But
** now we are (perhaps) moving the page off of the freelist for
** reuse and we need to know its original content so that content
** can be stored in the rollback journal. So do the read at this
** time.
*/
if( pPg->needRead ){
rc = readDbPage(pPager, pPg, pPg->pgno);
if( rc==SQLITE_OK ){
pPg->needRead = 0;
}else{
return rc;
}
}
/* Mark the page as dirty. If the page has already been written
** to the journal then we can return right away.
*/
@@ -3425,7 +3427,8 @@ static int pager_write(PgHdr *pPg){
put32bits(pData2, pPg->pgno);
rc = sqlite3OsWrite(pPager->jfd, pData2, szPg);
IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
pPager->journalOff, szPg))
pPager->journalOff, szPg));
PAGER_INCR(sqlite3_pager_writej_count);
pPager->journalOff += szPg;
PAGERTRACE4("JOURNAL %d page %d needSync=%d\n",
PAGERID(pPager), pPg->pgno, pPg->needSync);
@@ -3608,7 +3611,7 @@ int sqlite3PagerOverwrite(Pager *pPager, Pgno pgno, void *pData){
/*
** A call to this routine tells the pager that it is not necessary to
** write the information on page "pgno" back to the disk, even though
** write the information on page pPg back to the disk, even though
** that page might be marked as dirty.
**
** The overlying software layer calls this routine when all of the data
@@ -3630,13 +3633,11 @@ int sqlite3PagerOverwrite(Pager *pPager, Pgno pgno, void *pData){
** page contains critical data, we still need to be sure it gets
** rolled back in spite of the sqlite3PagerDontRollback() call.
*/
void sqlite3PagerDontWrite(Pager *pPager, Pgno pgno){
PgHdr *pPg;
void sqlite3PagerDontWrite(DbPage *pDbPage){
PgHdr *pPg = pDbPage;
Pager *pPager = pPg->pPager;
if( MEMDB ) return;
pPg = pager_lookup(pPager, pgno);
assert( pPg!=0 ); /* We never call _dont_write unless the page is in mem */
pPg->alwaysRollback = 1;
if( pPg->dirty && !pPager->stmtInUse ){
assert( pPager->state>=PAGER_SHARED );
@@ -3650,8 +3651,8 @@ void sqlite3PagerDontWrite(Pager *pPager, Pgno pgno){
** corruption during the next transaction.
*/
}else{
PAGERTRACE3("DONT_WRITE page %d of %d\n", pgno, PAGERID(pPager));
IOTRACE(("CLEAN %p %d\n", pPager, pgno))
PAGERTRACE3("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager));
IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
makeClean(pPg);
#ifdef SQLITE_CHECK_PAGES
pPg->pageHash = pager_pagehash(pPg);
@@ -3665,6 +3666,11 @@ void sqlite3PagerDontWrite(Pager *pPager, Pgno pgno){
** it is not necessary to restore the data on the given page. This
** means that the pager does not have to record the given page in the
** rollback journal.
**
** If we have not yet actually read the content of this page (if
** the PgHdr.needRead flag is set) then this routine acts as a promise
** that we will never need to read the page content in the future.
** so the needRead flag can be cleared at this point.
*/
void sqlite3PagerDontRollback(DbPage *pPg){
Pager *pPager = pPg->pPager;
@@ -3676,6 +3682,7 @@ void sqlite3PagerDontRollback(DbPage *pPg){
assert( pPager->aInJournal!=0 );
pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
pPg->inJournal = 1;
pPg->needRead = 0;
if( pPager->stmtInUse ){
pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
}