pg内核实现细节

TID(Tuple Id)

tid表示一行数据在数据页上的逻辑地址,通过block id和offset来定义一个page内具体数据位置。定义如下:

arduino 复制代码
struct ItemPointerData
{
  BlockIdData blkid; // 页号, u32类型
  OffsetNumber posid;// 业内slotid, u16类型
}

页数据组织形式

和oracle一样,pg也是以页为单位来在存储介质和内存中记录行数据,通过tup可定位到对应page的某个TupleId, 然后找到存储实际行数据的tuple.

行级mvcc实现

pg修改行数据时,保留旧行,并插入新行。每个事务都有唯一的事务id称为xid。

每行数据称为一个元组tuple,行头包含四个属性和一些标志位(行头总共有20字节,存储开销大,oracle只有3字节):

  • xmin:创建一个元组时,将事务xid写入该属性;
  • xmax:默认为0,删除一个元组时,将事务xid写入该属性,update时会将老元组的xmax改成update所在事务xid;
  • cmin、cmax:记录同一个事务中不同语句顺序,从0开始;
  • ctid:该行数据对应的tid;
  • t_ctid:多版本下,通过该属性将同一行多版本串联起来,记录的是下一个多版本行的ctid;

每个事务开启时,获取一个32bit的xid值,修改数据时,会创建新的元组记录xid到xmin中,并且写入到表数据中(新旧元组同时存在)。并标记老元组。

伪码示例

sql 复制代码
create table t1(c1 int, c2 int);

create index idx1(t1.c2);

insert into t1 values(1,2);
insert into t1 values(2,3);

#主表行信息:
ctid    xmin  xmax  t_ctid  data
(0,1)   100    0    (0,1)   [1 2]
(0,2)   100    0    (0,2)   [2 3]

#执行delete删除第二行:delete from t1 where c1=2

#查询主表只会返回一行数据,但是存储的真实行数没有变化,只是将xmax更改为delete语句对应的xid:200,行本身不会真的删除
ctid    xmin  xmax  t_ctid  data
(0,1)   100    0    (0,1)   [1 2]
(0,2)   100    200  (0,2)   [2 3]

#执行update语句更新第一行:update t1 set c2 = 5 where c1=1
#pg会拷贝第一行数据生成一个新的tuple, 然后更新old tuple的xmax和t_ctid, 来和new tuple链接起来
ctid    xmin  xmax  t_ctid  data
(0,1)   100    300  (0,3)   [1 2]
(0,2)   100    200  (0,2)   [2 3]
(0,3)   300    0    (0,3)   [1 5]

#执行查询语句时, 会根据版本号判断返回第一行还是第三行给客户端

#由于c2列上存在索引,上述操作也会影响到索引表,索引表数据也会修改,和主表一样不会被"delete"掉;
#pg的索引表没有多版本概念,而是借用主表的多版本来找到"可见"的最新行;因此索引表的tuple没有xmin/xmax等列;
#索引表的tuple通过htid列来关联上主表的tuple;
#更新主表索引列时,索引表也会做相应的修改;
#更新主表非索引列时,索引表不会做修改,通过上面介绍的版本链来找到最新tuple(HOT机制:heap-only tuple);

#可以看出pg的多版本会很容易导致磁盘空间膨胀,需要vacuum操作来释放磁盘空间

vacuum机制:清理死元组解决表膨胀问题+解决xid回绕问题

  • 事务提交后,新元组可以被其他事务看到,老元组不会被用到了,也不会立即被清理,可能导致表数据空间越来越大;
  • xid由32bit构成,最多存几亿个事务,数值越界时,会出现问题,vaccum机制会将已提交事务的产生的所有元组中隐藏列记录的xid改成2,1是系统xid,从3开始是正常事务可用值。因此只要是正常事务xid都会大于2,即都可以看到已提交事务的修改。
相关推荐
2603_965148114 小时前
如何解析JSON数据?API返回的商品信息处理教程
开发语言·数据库·python·自动化·json·api
ltl4 小时前
RocksDB 经典故障排查:L0、compaction 与 write stall
数据库
xfhuangfu5 小时前
Oracle中建立到CDB和PDB的连接
数据库·oracle·rpc
小小龙学IT5 小时前
DuckDB 深度实战:用 C++ 在进程内跑一个「分析型数据库」
数据库·c++
NineData5 小时前
DTCC 2026 预告|NineData CEO& 创始人叶正盛:面向 AI Agent 的数据库 DevOps 与数据复制实践
数据库·人工智能·数据库开发·devops·ninedata·数据库技术·dtcc
J_bean6 小时前
MySQL 事务是否必须手动开启?
数据库·mysql·数据库事务·自动提交·手动提交
J_bean6 小时前
MySQL InnoDB 如何检测死锁、判定死锁、处理死锁
数据库·mysql·死锁·死锁检测·处理死锁·死锁判定
浪子明X6 小时前
从 MongoDB 文档到关系模型:构建可重跑、可对账的数据迁移流水线
数据库·mongodb·oracle
杜子不疼.6 小时前
国产数据库撑起固井软件自主化:金仓 × 中海油服「海恒 Cemsol」落地解析
数据库
隔窗听雨眠7 小时前
GBase 8s并发控制深度解析:封锁机制、隔离级别与死锁处理全攻略
服务器·数据库·oracle