openGauss SLRU及multixact缓存管理介绍

SLRU

openGauss使用SLRU来管理事务相关日志的缓冲区,包括CLOG、CSNLOG、multixact log。SLRU是简化版的LRU,使用数组的方式存储元素,每次换入换出是会遍历数组找到lru count最小的buffer

主要接口

SimpleLruInit

初始化缓冲区

SlruSelectLRUPage

用于选择一个可以重用的缓冲区槽位,当需要一个缓冲区slot用于度入新页面时,需要调用该函数获取一个可用的缓冲区slot。

其执行流程如下所示:

  1. 首先逐个比较缓冲池中的缓冲区,如果所需要的页面已经在一个缓冲区中,那么直接返回该缓冲区即可
  2. 如果能找到一个状态为“空”的缓冲区,那么直接返回该缓冲区以供使用。
  3. 如果依然没有一个可用的缓冲区,那么在所有缓冲区中找到一个LRU值最大的缓冲区(需要注意的是,当前页面号最大的页面并不参与到这个LRU选择过程中)
  4. 然后判断找到的缓冲区的状态,如果为“干净”,那么直接返回该缓冲区;如果该缓冲区状态为“正在读入”,那么调用SimpleLruReadPage函数继续完成读入动作;如果缓冲区状态为“脏”或“正在写人”,那么调用 SimpleLruWritePage 函数继续完成写人动作。
  5. 最后返回第一步,继续完成选择缓冲区的任务,直至选出一个可供重用的缓冲区为止。

SimpleLruZeroPage

初始化一个缓冲区槽位。

  1. 调用 SlruSelectLRUPage 函数以获取一个可用的缓冲区。
  2. 然后将该缓冲区页面号设置为指定页面号,并设置其状态为“脏”,同时设置其为最近使用的页面,即该页面的 LRU值初始化为0,而其他页面的LRU值增一。
  3. 最后初始化该缓冲区为全0,并设置当前最大页面号为指定的页面号。最后返回所选择的
    缓冲区号。
  4. 缓冲池页面的读操作

SimpleLruReadPage

从缓冲池中读取指定的buffer。执行该函数前,线程持有control_lock的x锁。如果指定页面已经在缓冲池中,那么直接返回其所在的缓冲区;否则调用

  1. 调用SlruSelectLRUPage从缓冲池中寻找指定页面,找不到就返回一个可以淘汰的槽位。
  2. 占用该槽位shared->page_number[slotno] = pageno,并标记为shared->page_status[slotno] = SLRU_PAGE_READ_IN_PROGRESS,即为该槽位正在读
  3. 加buffer锁的x锁(void)LWLockAcquire(shared->buffer_locks[slotno], LW_EXCLUSIVE);
  4. 释放控制锁LWLockRelease(shared->control_lock);
  5. 读页面SlruPhysicalReadPage
  6. 加控制锁的x锁(void)LWLockAcquire(shared->control_lock, LW_EXCLUSIVE)
  7. 如果成功了shared->page_status[slotno] = SLRU_PAGE_VALID
  8. 释放buffer锁LWLockRelease(shared->buffer_locks[slotno]);

SimpleLruWritePage

  • SimpleLruWritePage
  • SlruInternalWritePage
  1. 如果槽位状态为SLRU_PAGE_WRITE_IN_PROGRESS,说明其他线程正在写该页面,等页面写完。
  2. 如果页面非脏、页面在读或写、页面被替换,直接返回。
  3. 槽位状态置为SLRU_PAGE_WRITE_IN_PROGRESS
  4. 加buffer锁的x锁
  5. 写页面ok = SlruPhysicalWritePage(ctl, pageno, slotno, fdata);
  6. 状态置为有效shared->page_status[slotno] = SLRU_PAGE_VALID;
  7. 释放buffer锁LWLockRelease(shared->buffer_locks[slotno]);

CLOG

CLOG中事务的四种状态

#define CLOG_XID_STATUS_IN_PROGRESS 0x00
#define CLOG_XID_STATUS_COMMITTED 0x01
#define CLOG_XID_STATUS_ABORTED 0x02

#define CLOGDIR (g_instance.datadir_cxt.clogDir)

/*
 * A "subcommitted" transaction is a committed subtransaction whose parent
 * hasn't committed or aborted yet.
 */
#define CLOG_XID_STATUS_SUB_COMMITTED 0x03

由于CLOG只记录事务的四个状态,因此一个事务状态信息的CLOG日志只需要2个比特,一个byte存储4个事务的CLOG,一个8k页面存储215个事务的CLOG。一个段文件(32个页面组成)可记录的CLOG日志记录数为220个。CLOG统一保存在$PGDATA/pg_clog目录下。

通过一个4元组<segmentno, pageno, byte, bindex>可以定位一条CLOG日志记录。其中Segmentno为段号,即实际的段文件名称。pageno为日志记录所在的段内的页偏移;byte为页面便宜;bindex为字节内偏移。

通过事务ID获取其日志记录对应的4元组。

#define TransactionIdToPage(xid) ((xid) / (TransactionId)CLOG_XACTS_PER_PAGE)
#define TransactionIdToPgIndex(xid) ((xid) % (TransactionId)CLOG_XACTS_PER_PAGE)
#define TransactionIdToByte(xid) (TransactionIdToPgIndex(xid) / CLOG_XACTS_PER_BYTE)
#define TransactionIdToBIndex(xid) ((xid) % (TransactionId)CLOG_XACTS_PER_BYTE)
#define PAGE_TO_TRANSACTION_ID(pageno) ((pageno) * (TransactionId)CLOG_XACTS_PER_PAGE)

CLOG日志缓冲池就是一个SLRU缓冲池,注册在共享内存中,其名称为“CLOG Ctl”,openGauss对CLOG做了分区处理

/* CLog lwlock partition*/
#define CBufHashPartition(hashcode) \
    ((hashcode) % NUM_CLOG_PARTITIONS)
#define CBufMappingPartitionLock(hashcode) \
    (&t_thrd.shemem_ptr_cxt.mainLWLockArray[FirstCBufMappingLock + CBufHashPartition(hashcode)].lock)
#define CBufMappingPartitionLockByIndex(i) \
    (&t_thrd.shemem_ptr_cxt.mainLWLockArray[FirstCBufMappingLock + i].lock)

CLOG日志的写操作

函数:CLogSetTreeStatus

void CLogSetTreeStatus(TransactionId xid, int nsubxids, TransactionId *subxids, CLogXidStatus status, XLogRecPtr lsn)

将事务和事务的子事务状态置为CLOG_XID_STATUS_COMMITTED或CLOG_XID_STATUS_ABORTED

  1. CLogSetTreeStatus,为保证事务原子性

    记录提交日志中一个事务及其子事务树的事务条目的最终状态。确保这一过程高效且尽可能原子化。
    xid 是要设置状态的单个 xid。通常这是顶层提交或中止的顶层 transactionid。它也可以是子事务,当我们记录事务中止时。
    subxids 是一个长度为 nsubxids 的 xids 数组,表示 xid 树中的子事务。在某些情况下,nsubxids 可能为零。
    lsn 必须是记录异步提交时提交记录的 WAL 位置。对于同步提交,它可以是 InvalidXLogRecPtr,因为调用者保证在这种情况下提交记录已经刷新。对于中止情况,它也应该是 InvalidXLogRecPtr。
    在提交的情况下,原子性受限于所有 subxids 是否在与 xid 相同的 CLOG 页面上。如果它们都在同一页面上,则只需获取一次锁,并直接将状态设置为已提交。否则,我们必须:
    	 1. 将所有不在主 xid 同一页面上的 subxids 设置为子已提交
    	 2. 原子性地将主 xid 和同一页面上的 subxids 设置为已提交
    	 3. 再次遍历第一批,将它们设置为已提交
    注意,就并发检查者而言,主事务的提交整体上仍然是原子的。
    示例:
    	TransactionId t 提交并具有子 xids t1、t2、t3、t4
    	t 在页面 p1,t1 也在 p1,t2 和 t3 在 p2,t4 在 p3
    	1. 更新页面 2-3:
    				页面 2:将 t2、t3 设置为子已提交
    				页面 3:将 t4 设置为子已提交
    	2. 更新页面 1:
    				将 t1 设置为子已提交,
    				然后将 t 设置为已提交,
    				然后将 t1 设置为已提交
    	3. 更新页面 2-3:
    				页面 2:将 t2、t3 设置为已提交
    				页面 3:将 t4 设置为已提交
    注意:这是一个低级例程,并不是大多数使用的首选入口点;transam.c 中的函数是预期的调用者。
    XXX 考虑对我们需要但尚未在缓存中的页面发出 FADVISE_WILLNEED,以及提示页面不要过早从缓存中消失。
    
  2. CLogSetPageStatus,更新前某一CLOG页面中的的事务状态前,调用LWLockConditionalAcquire(ClogCtl(pageno)->shared->control_lock, LW_EXCLUSIVE),加对应分区SLRU的x锁。

  3. CLogSetPageStatusInternal,先调用SimpleLruReadPage方法读取指定页面,再调用CLogSetStatusBit将该页面上所有的子事务状态置为CLOG_XID_STATUS_SUB_COMMITTED,再将主事务的状态置为status,再将该页面上所有子事务状态置为status

CLOG日志页面的初始化

ZeroCLOGPage

CLOG日志端的创建

BootStrapCLOG

multixact

之前一直认为Postgres中tuple头部信息的xmax只记录了删除/更新这条记录的事务号,为PG的MVCC机制服务。调试了一下PG行锁的实现机制,发现xmax所包含的信息没有那么简单,它还负责记录该tuple上的行锁以及行锁的模式信息(EXCLUDE/SHARE),以及持有该锁的事务号; 另外,因为多个事务可以持有某个行的SHARE锁,PG不可能将所有持有这个行锁的事务的事务号信息都放在tuple的头部,所以又引入了一个multixact机制,将这多个事务号”合并”起来放在共享内存里,tuple头部信息保存一个映射指向共享内存里这个”合并”的多个事务号即可。

multixact日志是PostgreSQL系统用来记录组合事务ID的一种日志。由于openGauss采用了多版本并发控制,因此同一个元组相关联的事务ID可能有多个,为了在加锁(行共享锁)的时候统一操作,openGauss将与该元组相关联的多个事务ID组合起来用一个multixactId代替管理。

multiXactID是一个多对一的映射关系(多个事务ID映射到一个multixactID),需要在事务ID数组标记哪一段映射到一个multiXacTID。openGauss用一个事务ID队列来存储multixactID的映射关系,具体而言openGauss将multixact的存储方式拆成offset和member两个部分:

  • multixactoffset存储的时某个multixact id对应的offset,可以通过offset去multixactmember中找该multixact id映射哪些事务id。

  • multixactmember存储的时所有multixact对应的事务id的列表,通过offset作为列表的其实点,两个相邻的offset之间的事务id就是该offset对应事务id列表。

    在这里插入图片描述

multixact日志管理器

multixact日志管理器是基于SLRU缓冲池实现的,只不过需要两个缓冲池,MultiXactOffsetCtl和MultiXactMemberCtl

multixact存储格式

  • offset日志:每个日志文件相当于一个段,每个段32个页,由于每个offset是64位即8字节,所以每页可以存8K/8=1K个,每个文件可以存32*1K=32K个。
  • members文件:每个日志相当于一个段,每个段32个页,由于每个TransactionId是64位即8字节,所以每页可以存8K/8=1K个,每个文件可以存32*1K=64K个。
#define MultiXactIdToOffsetPage(xid) ((xid) / (MultiXactOffset)MULTIXACT_OFFSETS_PER_PAGE)
#define MultiXactIdToOffsetEntry(xid) ((xid) % (MultiXactOffset)MULTIXACT_OFFSETS_PER_PAGE)

#define MXOffsetToMemberPage(xid) ((xid) / (TransactionId)MULTIXACT_MEMBERS_PER_PAGE)
#define MXOffsetToMemberEntry(xid) ((xid) % (TransactionId)MULTIXACT_MEMBERS_PER_PAGE)

offset可以通过三元组定位<segno, pageno, offno>,其中

  • segno: 段号
  • 段内页码
  • 页内偏移

给定一个MultiXactID可以通过三元组计算出offset的位置,其公式为:

segno = mxid / 32768
pageno = mxid / 1024
offno = mxid % 1024 * 8

member也可以通过三元组来定位其位置,给定一个offset,其计算公式为

segno = offset / 32768
pageno = offset / 1024
offno = offset % 1024 * 8

例如,mxid为2050, offset位置为<0,1,8>, 假如得出其中一个的offset值为1836,其member位置<0,1,1004>
Multixact一般包含多个member,其他的members的位置一般紧邻着第一个members存放,所以只要知道第一个members的位置和member的数量即可,数量可以通过mxid+1的offset值减去mxid的offset值获得。

部分函数

MultiXactShmemInit

初始化缓冲池

CreateMultiXactId

创建一个新的multixactid

RecordNewMultiXact

将一个新的multixact分别记录到MultiXactOffsetCtl和MultiXactMemberCtl。

GetMultiXactIdMembers

通过multixactId获取映射的事务id列表

TruncateMultiXact

vacuum时调用,清理掉没用的MultiXactOffset 和 MultiXactMember 段文件

测试

创建表t5,插入数据,查询元组内容。

openGauss=#  create table t5(id int,name varchar(60));
CREATE TABLE
openGauss=# insert into t5 values (1, 'test1'), (2, 'test2'), (3, 'test3'), (4, 'test4'), (5, 'test5');
INSERT 0 5
openGauss=# insert into t5 values (1, 'test1'), (2, 'test^C), (3, 'test3'), (4, 'test4'), (5, 'test5');
openGauss=#  select * from heap_page_items(get_raw_page('T5',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits |
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-
  1 |   8152 |        1 |     34 |  14225 |      0 |        0 | (0,1)  |           2 |       2050 |     24 |        |
  2 |   8112 |        1 |     34 |  14225 |      0 |        0 | (0,2)  |           2 |       2050 |     24 |        |
  3 |   8072 |        1 |     34 |  14225 |      0 |        0 | (0,3)  |           2 |       2050 |     24 |        |
  4 |   8032 |        1 |     34 |  14225 |      0 |        0 | (0,4)  |           2 |       2050 |     24 |        |
  5 |   7992 |        1 |     34 |  14225 |      0 |        0 | (0,5)  |           2 |       2050 |     24 |        |
(5 rows)

会话一创建事务,执行select for share,发现,tuple 1的xmax变成14233即当前事务xid,infomask=386(HEAP_XMIN_COMMITTED, HEAP_XMAX_SHARED_LOCK, HEAP_HASVARWIDTH),infomask2=2050(HEAP_ONLY_TUPLE, )

openGauss=# begin;
BEGIN
openGauss=# select pg_backend_pid(),txid_current();
 pg_backend_pid  | txid_current
-----------------+--------------
 281443410172352 |        14233
(1 row)

openGauss=# select id,name from t5 where id=1 for share;
 id | name
----+-------
  1 | test1
(1 row)

openGauss=# select * from heap_page_items(get_raw_page('T5',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits |
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-
  1 |   8152 |        1 |     34 |  14232 |  14233 |        0 | (0,1)  |        2050 |        386 |     24 |        |
  2 |   8112 |        1 |     34 |  14232 |      0 |        0 | (0,2)  |           2 |       2306 |     24 |        |
  3 |   8072 |        1 |     34 |  14232 |      0 |        0 | (0,3)  |           2 |       2306 |     24 |        |
  4 |   8032 |        1 |     34 |  14232 |      0 |        0 | (0,4)  |           2 |       2306 |     24 |        |
  5 |   7992 |        1 |     34 |  14232 |      0 |        0 | (0,5)  |           2 |       2306 |     24 |        |
(5 rows)

会话二创建事务,执行select for update,tuple 1的xmax变成2,infomask变成4482(0x1182 新增了HEAP_XMAX_IS_MULTI,意为xmax是multixact)

openGauss=# begin;
BEGIN
openGauss=# select pg_backend_pid(),txid_current();
 pg_backend_pid  | txid_current
-----------------+--------------
 281443234011584 |        14234
(1 row)

openGauss=# select id,name from t5 where id=1 for share;
 id | name
----+-------
  1 | test1
(1 row)

openGauss=# select * from heap_page_items(get_raw_page('T5',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits |
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-
  1 |   8152 |        1 |     34 |  14232 |      2 |        0 | (0,1)  |        2050 |       4482 |     24 |        |
  2 |   8112 |        1 |     34 |  14232 |      0 |        0 | (0,2)  |           2 |       2306 |     24 |        |
  3 |   8072 |        1 |     34 |  14232 |      0 |        0 | (0,3)  |           2 |       2306 |     24 |        |
  4 |   8032 |        1 |     34 |  14232 |      0 |        0 | (0,4)  |           2 |       2306 |     24 |        |
  5 |   7992 |        1 |     34 |  14232 |      0 |        0 | (0,5)  |           2 |       2306 |     24 |        |
(5 rows)

会话三再创建事务再执行select for update,发现multixact又涨了,说明如果再有事务对同一个页面加锁,会生成新的multixact,不会修改已有的multixact对应事务列表

openGauss=# begin;
BEGIN
openGauss=# select id,name from t5 where id=1 for share;
 id | name
----+-------
  1 | test1
(1 row)

openGauss=# select * from heap_page_items(get_raw_page('T5',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------
  1 |   8152 |        1 |     34 |  14232 |      4 |        0 | (0,1)  |        2050 |       4482 |     24 |        |
  2 |   8112 |        1 |     34 |  14232 |      0 |        0 | (0,2)  |           2 |       2306 |     24 |        |
  3 |   8072 |        1 |     34 |  14232 |      0 |        0 | (0,3)  |           2 |       2306 |     24 |        |
  4 |   8032 |        1 |     34 |  14232 |      0 |        0 | (0,4)  |           2 |       2306 |     24 |        |
  5 |   7992 |        1 |     34 |  14232 |      0 |        0 | (0,5)  |           2 |       2306 |     24 |        |
(5 rows)

下面是 pg_multixact/offsets的文件内容,按照multixact的顺序排列。保存的是每个multixact的MultiXactOffset,MultiXactOffset为uint64,即每8个字节一个MultiXactOffset,其中multixact = 4时,对应member为(0500 0000 0000 0000),即5,即 pg_multixact/members中从位置5开始到下一个multixact的offset位置,都是这个multixact对应的事务id。

[zhoucong_normal@openGauss111 pg_multixact]$ xxd offsets/000000000000 |more
00000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000010: 0100 0000 0000 0000 0300 0000 0000 0000  ................
00000020: 0500 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................

下面是 pg_multixact/members的文件内容,按照offset的循序排列。保存的是每个offset的TransactionId列表,TransactionId为uint64,即每8个字节一个TransactionId,其中offset = 5时,对应的事务id为(9b37 0000 0000 0000,9c37 0000 0000 000 , 9d37 0000 0000 0000),即(0x379b, 0x379c, 0x379d),即multixact = 4对应三个事务id(14235, 14236, 14237)。

[zhoucong_normal@openGauss111 members]$ xxd 000000000000 | more
00000000: 0000 0000 0000 0000 9937 0000 0000 0000  .........7......
00000010: 9a37 0000 0000 0000 9b37 0000 0000 0000  .7.......7......
00000020: 9c37 0000 0000 0000 9b37 0000 0000 0000  .7.......7......
00000030: 9c37 0000 0000 0000 9d37 0000 0000 0000  .7.......7......
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................

infomask

infomask相关的标记

其中0x1000表示max是multixact

/*
 * information stored in t_infomask:
 */
#define HEAP_HASNULL 0x0001          /* has null attribute(s) */
#define HEAP_HASVARWIDTH 0x0002      /* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL 0x0004      /* has external stored attribute(s) */
#define HEAP_HASOID 0x0008           /* has an object-id field */
#define HEAP_COMPRESSED 0x0010       /* has compressed data */
#define HEAP_COMBOCID 0x0020         /* t_cid is a combo cid */
#define HEAP_XMAX_EXCL_LOCK 0x0040   /* xmax is exclusive locker */
#define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */
/* xmax is a key-shared locker */
#define HEAP_XMAX_KEYSHR_LOCK (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_SHARED_LOCK)
#define HEAP_LOCK_MASK (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_SHARED_LOCK | HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */
#define HEAP_XMIN_INVALID 0x0200   /* t_xmin invalid/aborted */
#define HEAP_XMIN_FROZEN (HEAP_XMIN_INVALID | HEAP_XMIN_COMMITTED)
#define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */
#define HEAP_XMAX_INVALID 0x0800   /* t_xmax invalid/aborted */
#define HEAP_XMAX_IS_MULTI 0x1000  /* t_xmax is a MultiXactId */
#define HEAP_UPDATED 0x2000        /* this is UPDATEd version of row */

#define HEAP_HAS_8BYTE_UID (0x4000) /* tuple has 8 bytes uid */
#define HEAP_UID_MASK (0x4000)
#define NDP_HANDLED_TUPLE (0x8000) /* tuple is from ndp backend */

#define HEAP_XACT_MASK (0x3FE0) /* visibility-related bits */

infomask2相关的标记

/*
 * information stored in t_infomask2:
 */
#define HEAP_NATTS_MASK 0x07FF /* 11 bits for number of attributes */
#define HEAP_XMAX_LOCK_ONLY 0x0800 /* xmax, if valid, is only a locker */
#define HEAP_KEYS_UPDATED 0x1000 /* tuple was updated and key cols modified, or tuple deleted */
#define HEAP_HAS_REDIS_COLUMNS 0x2000 /* tuple has hidden columns added by redis */
#define HEAP_HOT_UPDATED 0x4000 /* tuple was HOT-updated */
#define HEAP_ONLY_TUPLE 0x8000  /* this is heap-only tuple */

#define HEAP2_XACT_MASK 0xD800 /* visibility-related bits */

#define HeapTupleHeaderSetHotUpdated(tup) ((tup)->t_infomask2 |= HEAP_HOT_UPDATED)

#define HeapTupleHeaderClearHotUpdated(tup) ((tup)->t_infomask2 &= ~HEAP_HOT_UPDATED)

#define HeapTupleHeaderIsHeapOnly(tup) (((tup)->t_infomask2 & HEAP_ONLY_TUPLE) != 0)

#define HeapTupleHeaderSetHeapOnly(tup) ((tup)->t_infomask2 |= HEAP_ONLY_TUPLE)

#define HeapTupleHeaderClearHeapOnly(tup) ((tup)->t_infomask2 &= ~HEAP_ONLY_TUPLE)

#define HeapTupleHeaderHasRedisColumns(tup) (((tup)->t_infomask2 & HEAP_HAS_REDIS_COLUMNS) != 0)

#define HeapTupleHeaderSetRedisColumns(tup) ((tup)->t_infomask2 |= HEAP_HAS_REDIS_COLUMNS)

#define HeapTupleHeaderUnsetRedisColumns(tup) ((tup)->t_infomask2 &= ~HEAP_HAS_REDIS_COLUMNS)

#define HeapTupleHeaderHasMatch(tup) (((tup)->t_infomask2 & HEAP_TUPLE_HAS_MATCH) != 0)

#define HeapTupleHeaderSetMatch(tup) ((tup)->t_infomask2 |= HEAP_TUPLE_HAS_MATCH)

#define HeapTupleHeaderClearMatch(tup) ((tup)->t_infomask2 &= ~HEAP_TUPLE_HAS_MATCH)

参考内容

multixact

https://www.modb.pro/db/14939

https://blog.youkuaiyun.com/mirai_D_zoro/article/details/136970358

SLRU

Columns(tup) ((tup)->t_infomask2 &= ~HEAP_HAS_REDIS_COLUMNS)

#define HeapTupleHeaderHasMatch(tup) (((tup)->t_infomask2 & HEAP_TUPLE_HAS_MATCH) != 0)

#define HeapTupleHeaderSetMatch(tup) ((tup)->t_infomask2 |= HEAP_TUPLE_HAS_MATCH)

#define HeapTupleHeaderClearMatch(tup) ((tup)->t_infomask2 &= ~HEAP_TUPLE_HAS_MATCH)




# 参考内容

**multixact**

https://www.modb.pro/db/14939

https://blog.youkuaiyun.com/mirai_D_zoro/article/details/136970358

**SLRU**

https://blog.youkuaiyun.com/mirai_D_zoro/article/details/136970287
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值