/* 使用方法 */
BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
uint8 mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
/* 计算公式 */
// 页面能使用的空间:MAPSIZE = 8168 = 8192 - 24
#define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData))
// 一个字节能存几个页面信息 = 一个字节8位/一个页面需要两位 = HEAPBLOCKS_PER_BYTE = 8 / 2 = 4
#define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
// 一个vm页面能存多少个堆页面的可见性信息 = MAPSIZE * HEAPBLOCKS_PER_BYTE = 8168 * 4 = 32672
#define HEAPBLOCKS_PER_PAGE (MAPSIZE * HEAPBLOCKS_PER_BYTE)
// 一个vm页面存32672个信息,这里除32672确定在那个页面上
#define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE)
// 一个字节存4个信息,这里除4确定在那个字节上
#define HEAPBLK_TO_MAPBYTE(x) (((x) % HEAPBLOCKS_PER_PAGE) / HEAPBLOCKS_PER_BYTE)
// 一个字节8位,两位一个信息,这里先%4等于0-3,确定在哪个信息上
#define HEAPBLK_TO_OFFSET(x) (((x) % HEAPBLOCKS_PER_BYTE) * BITS_PER_HEAPBLOCK)
/* 例子 */
heapBlk = 32706
mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk) = 32706/32672 = 1
mapByte = HEAPBLK_TO_MAPBYTE(heapBlk) = 32706%32672/4 = 34/4 = 8
mapOffset = HEAPBLK_TO_OFFSET(heapBlk) = 32706%4*2 = 2*2 = 4
注意写都是有写锁的,基本加锁顺序是 页面写锁、vm页面写锁、写。页面写锁后,vm加锁发现vm不在内存中是要避免的,见下面“四、race condition”。
0x01 VISIBILITYMAP_ALL_VISIBLE
和0x02 VISIBILITYMAP_ALL_FROZEN
- 这里有一个race condition:拿着页面锁,PD\_ALL\_VISIBLE被配置了。这种情况需要放页面锁,pin vm,加页面锁,在加vm页面锁,在写vm。
VM_ALL_VISIBLE
、VM_ALL_FROZEN
详见下图:
VISIBILITYMAP_ALL_VISIBLE
或VISIBILITYMAP_ALL_FROZEN
映射算法:
/* 使用方法 */
BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
uint8 mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
/* 计算公式 */
// 页面能使用的空间:MAPSIZE = 8168 = 8192 - 24
#define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData))
// 一个字节能存几个页面信息 = 一个字节8位/一个页面需要两位 = HEAPBLOCKS_PER_BYTE = 8 / 2 = 4
#define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
// 一个vm页面能存多少个堆页面的可见性信息 = MAPSIZE * HEAPBLOCKS_PER_BYTE = 8168 * 4 = 32672
#define HEAPBLOCKS_PER_PAGE (MAPSIZE * HEAPBLOCKS_PER_BYTE)
// 一个vm页面存32672个信息,这里除32672确定在那个页面上
#define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE)
// 一个字节存4个信息,这里除4确定在那个字节上
#define HEAPBLK_TO_MAPBYTE(x) (((x) % HEAPBLOCKS_PER_PAGE) / HEAPBLOCKS_PER_BYTE)
// 一个字节8位,两位一个信息,这里先%4等于0-3,确定在哪个信息上
#define HEAPBLK_TO_OFFSET(x) (((x) % HEAPBLOCKS_PER_BYTE) * BITS_PER_HEAPBLOCK)
/* 例子 */
heapBlk = 32706
mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk) = 32706/32672 = 1
mapByte = HEAPBLK_TO_MAPBYTE(heapBlk) = 32706%32672/4 = 34/4 = 8
mapOffset = HEAPBLK_TO_OFFSET(heapBlk) = 32706%4*2 = 2*2 = 4
visibilitymap_set可配的可见性状态:
代码分析:
void
visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf,
XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid,
uint8 flags)
{
BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
uint8 mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
Page page;
uint8 *map;
...
page = BufferGetPage(vmBuf);
map = (uint8 *) PageGetContents(page);
LockBuffer(vmBuf, BUFFER_LOCK_EXCLUSIVE);
// 要配置为flags的标志位,但是当前的标志位和flags不同,进入配置
if (flags != (map[mapByte] >> mapOffset & VISIBILITYMAP_VALID_BITS))
{
START_CRIT_SECTION();
map[mapByte] |= (flags << mapOffset);
MarkBufferDirty(vmBuf);
if (RelationNeedsWAL(rel))
{
if (XLogRecPtrIsInvalid(recptr))
{
// 写一条XLOG_HEAP2_VISIBLE日志发到备机
recptr = log_heap_visible(rel->rd_node, heapBuf, vmBuf,
cutoff_xid, flags);
...
}
// 注意这里:要给vm页面记录这条日志的lsn
PageSetLSN(page, recptr);
}
END_CRIT_SECTION();
}
// 放锁
LockBuffer(vmBuf, BUFFER_LOCK_UNLOCK);
}
给堆页面加锁写后处理、并MarkBufferDirty
bool
visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer buf, uint8 flags)
{
BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
int mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
int mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
uint8 mask = flags << mapOffset;
char *map;
bool cleared = false;
...
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
map = PageGetContents(BufferGetPage(buf));
if (map[mapByte] & mask)
{
map[mapByte] &= ~mask;
MarkBufferDirty(buf);
cleared = true;
}
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
return cleared;
}
读的时候不需要指定vmbuffer,只需给到堆页面的heapBlk,函数会计算出来vmbuffer并打开,并通过第三个参数返回。
uint8
visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *buf)
{
BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
uint8 mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
char *map;
uint8 result;
if (BufferIsValid(*buf))
{
if (BufferGetBlockNumber(*buf) != mapBlock)
{
ReleaseBuffer(*buf);
*buf = InvalidBuffer;
}
}
if (!BufferIsValid(*buf))
{
*buf = vm_readbuf(rel, mapBlock, false);
if (!BufferIsValid(*buf))
return false;
}
map = PageGetContents(BufferGetPage(*buf));
result = ((map[mapByte] >> mapOffset) & VISIBILITYMAP_VALID_BITS);
return result;
}
1、SMGR打开表文件
2、ReadBufferExtended打开指定blkno
3、如果是新页面PageInit
注意:ReadBufferExtended读出来默认会Pin上
1、保证当前rel的vm页面不少与vm_nblocks
2、调用smgrextend自己批量扩展(ReadBuffer_common也能扩展,传invalid block num即可,不过只扩展一个)
3、扩展完了读的时候再PageInit