📄 bufmgr.c
字号:
ShowBufferUsage(void){ StringInfoData str; float hitrate; float localhitrate; initStringInfo(&str); if (ReadBufferCount == 0) hitrate = 0.0; else hitrate = (float) BufferHitCount *100.0 / ReadBufferCount; if (ReadLocalBufferCount == 0) localhitrate = 0.0; else localhitrate = (float) LocalBufferHitCount *100.0 / ReadLocalBufferCount; appendStringInfo(&str, "!\tShared blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%\n", ReadBufferCount - BufferHitCount, BufferFlushCount, hitrate); appendStringInfo(&str, "!\tLocal blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%\n", ReadLocalBufferCount - LocalBufferHitCount, LocalBufferFlushCount, localhitrate); appendStringInfo(&str, "!\tDirect blocks: %10ld read, %10ld written\n", NDirectFileRead, NDirectFileWrite); return str.data;}voidResetBufferUsage(void){ BufferHitCount = 0; ReadBufferCount = 0; BufferFlushCount = 0; LocalBufferHitCount = 0; ReadLocalBufferCount = 0; LocalBufferFlushCount = 0; NDirectFileRead = 0; NDirectFileWrite = 0;}/* * AtEOXact_Buffers - clean up at end of transaction. * * As of PostgreSQL 8.0, buffer pins should get released by the * ResourceOwner mechanism. This routine is just a debugging * cross-check that no pins remain. */voidAtEOXact_Buffers(bool isCommit){#ifdef USE_ASSERT_CHECKING if (assert_enabled) { int i; for (i = 0; i < NBuffers; i++) { Assert(PrivateRefCount[i] == 0); } }#endif AtEOXact_LocalBuffers(isCommit); /* Make sure we reset the strategy hint in case VACUUM errored out */ StrategyHintVacuum(false);}/* * InitBufferPoolBackend --- second-stage initialization of a new backend * * This is called after we have acquired a PGPROC and so can safely get * LWLocks. We don't currently need to do anything at this stage ... * except register a shmem-exit callback. AtProcExit_Buffers needs LWLock * access, and thereby has to be called at the corresponding phase of * backend shutdown. */voidInitBufferPoolBackend(void){ on_shmem_exit(AtProcExit_Buffers, 0);}/* * Ensure we have released all shared-buffer locks and pins during backend exit */static voidAtProcExit_Buffers(int code, Datum arg){ int i; AbortBufferIO(); UnlockBuffers(); for (i = 0; i < NBuffers; i++) { if (PrivateRefCount[i] != 0) { volatile BufferDesc *buf = &(BufferDescriptors[i]); /* * We don't worry about updating ResourceOwner; if we even got * here, it suggests that ResourceOwners are messed up. */ PrivateRefCount[i] = 1; /* make sure we release shared pin */ UnpinBuffer(buf, false, false /* don't change freelist */ ); Assert(PrivateRefCount[i] == 0); } } /* localbuf.c needs a chance too */ AtProcExit_LocalBuffers();}/* * Helper routine to issue warnings when a buffer is unexpectedly pinned */voidPrintBufferLeakWarning(Buffer buffer){ volatile BufferDesc *buf; int32 loccount; Assert(BufferIsValid(buffer)); if (BufferIsLocal(buffer)) { buf = &LocalBufferDescriptors[-buffer - 1]; loccount = LocalRefCount[-buffer - 1]; } else { buf = &BufferDescriptors[buffer - 1]; loccount = PrivateRefCount[buffer - 1]; } /* theoretically we should lock the bufhdr here */ elog(WARNING, "buffer refcount leak: [%03d] " "(rel=%u/%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d)", buffer, buf->tag.rnode.spcNode, buf->tag.rnode.dbNode, buf->tag.rnode.relNode, buf->tag.blockNum, buf->flags, buf->refcount, loccount);}/* * FlushBufferPool * * Flush all dirty blocks in buffer pool to disk at the checkpoint time. * Local relations do not participate in checkpoints, so they don't need to be * flushed. */voidFlushBufferPool(void){ BufferSync(); smgrsync();}/* * Do whatever is needed to prepare for commit at the bufmgr and smgr levels */voidBufmgrCommit(void){ /* Nothing to do in bufmgr anymore... */ smgrcommit();}/* * BufferGetBlockNumber * Returns the block number associated with a buffer. * * Note: * Assumes that the buffer is valid and pinned, else the * value may be obsolete immediately... */BlockNumberBufferGetBlockNumber(Buffer buffer){ volatile BufferDesc *bufHdr; Assert(BufferIsPinned(buffer)); if (BufferIsLocal(buffer)) bufHdr = &(LocalBufferDescriptors[-buffer - 1]); else bufHdr = &BufferDescriptors[buffer - 1]; /* pinned, so OK to read tag without spinlock */ return bufHdr->tag.blockNum;}/* * BufferGetFileNode * Returns the relation ID (RelFileNode) associated with a buffer. * * This should make the same checks as BufferGetBlockNumber, but since the * two are generally called together, we don't bother. */RelFileNodeBufferGetFileNode(Buffer buffer){ volatile BufferDesc *bufHdr; if (BufferIsLocal(buffer)) bufHdr = &(LocalBufferDescriptors[-buffer - 1]); else bufHdr = &BufferDescriptors[buffer - 1]; return bufHdr->tag.rnode;}/* * FlushBuffer * Physically write out a shared buffer. * * NOTE: this actually just passes the buffer contents to the kernel; the * real write to disk won't happen until the kernel feels like it. This * is okay from our point of view since we can redo the changes from WAL. * However, we will need to force the changes to disk via fsync before * we can checkpoint WAL. * * The caller must hold a pin on the buffer and have share-locked the * buffer contents. (Note: a share-lock does not prevent updates of * hint bits in the buffer, so the page could change while the write * is in progress, but we assume that that will not invalidate the data * written.) * * If the caller has an smgr reference for the buffer's relation, pass it * as the second parameter. If not, pass NULL. */static voidFlushBuffer(volatile BufferDesc *buf, SMgrRelation reln){ XLogRecPtr recptr; ErrorContextCallback errcontext; /* * Acquire the buffer's io_in_progress lock. If StartBufferIO returns * false, then someone else flushed the buffer before we could, so we need * not do anything. */ if (!StartBufferIO(buf, false)) return; /* Setup error traceback support for ereport() */ errcontext.callback = buffer_write_error_callback; errcontext.arg = (void *) buf; errcontext.previous = error_context_stack; error_context_stack = &errcontext; /* Find smgr relation for buffer */ if (reln == NULL) reln = smgropen(buf->tag.rnode); /* * Force XLOG flush up to buffer's LSN. This implements the basic WAL * rule that log updates must hit disk before any of the data-file changes * they describe do. */ recptr = BufferGetLSN(buf); XLogFlush(recptr); /* * Now it's safe to write buffer to disk. Note that no one else should * have been able to write it while we were busy with log flushing because * we have the io_in_progress lock. */ /* To check if block content changes while flushing. - vadim 01/17/97 */ LockBufHdr_NoHoldoff(buf); buf->flags &= ~BM_JUST_DIRTIED; UnlockBufHdr_NoHoldoff(buf); smgrwrite(reln, buf->tag.blockNum, (char *) BufHdrGetBlock(buf), false); BufferFlushCount++; /* * Mark the buffer as clean (unless BM_JUST_DIRTIED has become set) and * end the io_in_progress state. */ TerminateBufferIO(buf, true, 0); /* Pop the error context stack */ error_context_stack = errcontext.previous;}/* * RelationGetNumberOfBlocks * Determines the current number of pages in the relation. */BlockNumberRelationGetNumberOfBlocks(Relation relation){ /* Open it at the smgr level if not already done */ RelationOpenSmgr(relation); return smgrnblocks(relation->rd_smgr);}/* * RelationTruncate * Physically truncate a relation to the specified number of blocks. * * As of Postgres 8.1, this includes getting rid of any buffers for the * blocks that are to be dropped; previously, callers had to do that. */voidRelationTruncate(Relation rel, BlockNumber nblocks){ /* Open it at the smgr level if not already done */ RelationOpenSmgr(rel); /* Make sure rd_targblock isn't pointing somewhere past end */ rel->rd_targblock = InvalidBlockNumber; /* Do the real work */ smgrtruncate(rel->rd_smgr, nblocks, rel->rd_istemp);}/* --------------------------------------------------------------------- * DropRelFileNodeBuffers * * This function removes from the buffer pool all the pages of the * specified relation that have block numbers >= firstDelBlock. * (In particular, with firstDelBlock = 0, all pages are removed.) * Dirty pages are simply dropped, without bothering to write them * out first. Therefore, this is NOT rollback-able, and so should be * used only with extreme caution! * * Currently, this is called only from smgr.c when the underlying file * is about to be deleted or truncated (firstDelBlock is needed for * the truncation case). The data in the affected pages would therefore * be deleted momentarily anyway, and there is no point in writing it. * It is the responsibility of higher-level code to ensure that the * deletion or truncation does not lose any data that could be needed * later. It is also the responsibility of higher-level code to ensure * that no other process could be trying to load more pages of the * relation into buffers. * * XXX currently it sequentially searches the buffer pool, should be * changed to more clever ways of searching. However, this routine * is used only in code paths that aren't very performance-critical, * and we shouldn't slow down the hot paths to make it faster ... * -------------------------------------------------------------------- */voidDropRelFileNodeBuffers(RelFileNode rnode, bool istemp, BlockNumber firstDelBlock){ int i; if (istemp) { DropRelFileNodeLocalBuffers(rnode, firstDelBlock); return; } for (i = 0; i < NBuffers; i++) { volatile BufferDesc *bufHdr = &BufferDescriptors[i]; LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rnode) && bufHdr->tag.blockNum >= firstDelBlock) InvalidateBuffer(bufHdr); /* releases spinlock */ else UnlockBufHdr(bufHdr); }}/* --------------------------------------------------------------------- * DropBuffers * * This function removes all the buffers in the buffer cache for a * particular database. Dirty pages are simply dropped, without * bothering to write them out first. This is used when we destroy a * database, to avoid trying to flush data to disk when the directory * tree no longer exists. Implementation is pretty similar to * DropRelFileNodeBuffers() which is for destroying just one relation. * -------------------------------------------------------------------- */voidDropBuffers(Oid dbid){ int i; volatile BufferDesc *bufHdr; /* * We needn't consider local buffers, since by assumption the target * database isn't our own. */ for (i = 0; i < NBuffers; i++) { bufHdr = &BufferDescriptors[i]; LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid) InvalidateBuffer(bufHdr); /* releases spinlock */ else UnlockBufHdr(bufHdr); }}/* ----------------------------------------------------------------- * PrintBufferDescs * * this function prints all the buffer descriptors, for debugging * use only. * ----------------------------------------------------------------- */#ifdef NOT_USEDvoidPrintBufferDescs(void){ int i; volatile BufferDesc *buf = BufferDescriptors; for (i = 0; i < NBuffers; ++i, ++buf) { /* theoretically we should lock the bufhdr here */ elog(LOG, "[%02d] (freeNext=%d, rel=%u/%u/%u, " "blockNum=%u, flags=0x%x, refcount=%u %d)", i, buf->freeNext, buf->tag.rnode.spcNode, buf->tag.rnode.dbNode, buf->tag.rnode.relNode, buf->tag.blockNum, buf->flags, buf->refcount, PrivateRefCount[i]); }}#endif#ifdef NOT_USEDvoidPrintPinnedBufs(void){ int i; volatile BufferDesc *buf = BufferDescriptors; for (i = 0; i < NBuffers; ++i, ++buf) { if (PrivateRefCount[i] > 0) { /* theoretically we should lock the bufhdr here */ elog(LOG, "[%02d] (freeNext=%d, rel=%u/%u/%u, " "blockNum=%u, flags=0x%x, refcount=%u %d)", i, buf->freeNext, buf->tag.rnode.spcNode, buf->tag.rnode.dbNode, buf->tag.rnode.relNode, buf->tag.blockNum, buf->flags, buf->refcount, PrivateRefCount[i]); } }}#endif/* --------------------------------------------------------------------- * FlushRelationBuffers * * This function writes all dirty pages of a relation out to disk * (or more accurately, out to kernel disk buffers), ensuring that the * kernel has an up-to-date view of the relation. * * Generally, the caller should be holding AccessExclusiveLock on the * target relation to ensure that no other backend is busy dirtying * more blocks of the relation; the effects can't be expected to last * after the lock is released. * * XXX currently it sequentially searches the buffer pool, should be * changed to more clever ways of searching. This routine is not * used in any performance-critical code paths, so it's not worth * adding additional overhead to normal paths to make it go faster; * but see also DropRelFileNodeBuffers. * -------------------------------------------------------------------- */voidFlushRelationBuffers(Relation rel){ int i; volatile BufferDesc *bufHdr; /* Open rel at the smgr level if not already done */ RelationOpenSmgr(rel); if (rel->rd_istemp) { for (i = 0; i < NLocBuffer; i++) { bufHdr = &LocalBufferDescriptors[i]; if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && (bufHdr->flags & BM_VALID) && (bufHdr->flags & BM_DIRTY)) { ErrorContextCallback errcontext; /* Setup error traceback support for ereport() */ errcontext.callback = buffer_write_error_callback; errcontext.arg = (void *) bufHdr; errcontext.previous = error_context_stack; error_context_stack = &errcontext; smgrwrite(rel->rd_smgr, bufHdr->tag.blockNum, (char *) LocalBufHdrGetBlock(bufHdr), true); bufHdr->flags &= ~(BM_DIRTY | BM_JUST_DIRTIED); /* Pop the error context stack */ error_context_stack = errcontext.previous; } } return; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -