📄 bufmgr.c
字号:
{ BgWriterStats.m_maxwritten_clean++; break; } } else if (buffer_state & BUF_REUSABLE) reusable_buffers++; } BgWriterStats.m_buf_written_clean += num_written;#ifdef BGW_DEBUG elog(DEBUG1, "bgwriter: recent_alloc=%u smoothed=%.2f delta=%ld ahead=%d density=%.2f reusable_est=%d upcoming_est=%d scanned=%d wrote=%d reusable=%d", recent_alloc, smoothed_alloc, strategy_delta, bufs_ahead, smoothed_density, reusable_buffers_est, upcoming_alloc_est, bufs_to_lap - num_to_scan, num_written, reusable_buffers - reusable_buffers_est);#endif /* * Consider the above scan as being like a new allocation scan. * Characterize its density and update the smoothed one based on it. This * effectively halves the moving average period in cases where both the * strategy and the background writer are doing some useful scanning, * which is helpful because a long memory isn't as desirable on the * density estimates. */ strategy_delta = bufs_to_lap - num_to_scan; recent_alloc = reusable_buffers - reusable_buffers_est; if (strategy_delta > 0 && recent_alloc > 0) { scans_per_alloc = (float) strategy_delta / (float) recent_alloc; smoothed_density += (scans_per_alloc - smoothed_density) / smoothing_samples;#ifdef BGW_DEBUG elog(DEBUG2, "bgwriter: cleaner density alloc=%u scan=%ld density=%.2f new smoothed=%.2f", recent_alloc, strategy_delta, scans_per_alloc, smoothed_density);#endif }}/* * SyncOneBuffer -- process a single buffer during syncing. * * If skip_recently_used is true, we don't write currently-pinned buffers, nor * buffers marked recently used, as these are not replacement candidates. * * Returns a bitmask containing the following flag bits: * BUF_WRITTEN: we wrote the buffer. * BUF_REUSABLE: buffer is available for replacement, ie, it has * pin count 0 and usage count 0. * * (BUF_WRITTEN could be set in error if FlushBuffers finds the buffer clean * after locking it, but we don't care all that much.) * * Note: caller must have done ResourceOwnerEnlargeBuffers. */static intSyncOneBuffer(int buf_id, bool skip_recently_used){ volatile BufferDesc *bufHdr = &BufferDescriptors[buf_id]; int result = 0; /* * Check whether buffer needs writing. * * We can make this check without taking the buffer content lock so long * as we mark pages dirty in access methods *before* logging changes with * XLogInsert(): if someone marks the buffer dirty just after our check we * don't worry because our checkpoint.redo points before log record for * upcoming changes and so we are not required to write such dirty buffer. */ LockBufHdr(bufHdr); if (bufHdr->refcount == 0 && bufHdr->usage_count == 0) result |= BUF_REUSABLE; else if (skip_recently_used) { /* Caller told us not to write recently-used buffers */ UnlockBufHdr(bufHdr); return result; } if (!(bufHdr->flags & BM_VALID) || !(bufHdr->flags & BM_DIRTY)) { /* It's clean, so nothing to do */ UnlockBufHdr(bufHdr); return result; } /* * Pin it, share-lock it, write it. (FlushBuffer will do nothing if the * buffer is clean by the time we've locked it.) */ PinBuffer_Locked(bufHdr); LWLockAcquire(bufHdr->content_lock, LW_SHARED); FlushBuffer(bufHdr, NULL); LWLockRelease(bufHdr->content_lock); UnpinBuffer(bufHdr, true); return result | BUF_WRITTEN;}/* * Return a palloc'd string containing buffer usage statistics. */char *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);}/* * 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); 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);}/* * CheckPointBuffers * * Flush all dirty blocks in buffer pool to disk at checkpoint time. * * Note: temporary relations do not participate in checkpoints, so they don't * need to be flushed. */voidCheckPointBuffers(int flags){ CheckpointStats.ckpt_write_t = GetCurrentTimestamp(); BufferSync(flags); CheckpointStats.ckpt_sync_t = GetCurrentTimestamp(); smgrsync(); CheckpointStats.ckpt_sync_end_t = GetCurrentTimestamp();}/* * 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(buf); buf->flags &= ~BM_JUST_DIRTIED; UnlockBufHdr(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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -