PostgreSql源码阅读笔记1(参考husthxd在ITPUB的博客)

参考内容

PostgreSQL 数据页Page解析(1)- 基础
PostgreSQL Page页结构解析(3)- 行数据
PostgreSQL Page页结构解析(5)- B-Tree索引存储结构

知识点:

  • LSN:日志序列号
  • 表的位置 base/25835/1245 对应 ‘’表空间/数据库oid/表oid‘’
  • 表对应vm,fsm:(用于多版本控制)
    • vm(可见性映射):为每个数据块设置一个标志位,标记块中是否没有需要清理的行,用于加快vacuum清理,用于lazy vacuum
    • fsm(空闲空间映射):将每个块(8k)分成256份,用一个字节表示一块剩余大小。一个表最多2^32个块,数据结构由分三层二叉树存储。在执行vacuum操作时或插入操作需要扫描时创建。
  • hexdump命令:hexdump -C 二进制文件目录 -s 起始字节 -n 几个字节
  • X86使用小端模式

页结构在这里插入图片描述

  • 特殊空间用于存储索引访问使用的数据,不同的访问方法数据不同

一行的头部保留信息:

  • xmin 创建的事务id
  • xmax 最新的事务id
  • cid / xvac 插入删除的命令id或vaccum事务的id
  • ctid 当前版本与新版本的指针(块号,偏移号)
  • infomask2 特征数量、flag
  • infomask flag
  • off 元组位置offset
  • bits 用来标记空属性的bitmap

B树索引

  1. 索引页头部与数据页头部一样( 24个字节 )

  2. 索引数据: IndexTupleData(指针(块号,偏移号)、bitinfo(hasnull、hasvar-width、元组大小))+Bitmap+Value

  3. 页最后的特征空间(16字节):left、right、从叶节点起向上第几层、flag

  4. 索引头信息有序,索引数据信息无序

  5. root / branch 块与 leaf 块用 btpo_flags 来标记(在页特殊空间):leaf的ctid指向 heap table block,叶块当btpo_next 不是0时,意味不是最右块,则第一个数据存的是最大key(highkey),第二个起才是由小到大排列的key

参考内容

PostgreSQL 源码解读(1)- 插入数据(heap_insert)

知识点:

  • buffer 是块号的标记,是一个数,对于localbuffer,buffer是负数,对应 LocalBufferBlockPointers 块的序号是 -buffer-1,对于非localbuffer,buffer是正数,本身即序号,对应 BufferBlocks 的 (buffer-1)* BLCKSZ 的位置。

  • 本地 buffer 用于临时表的读写。 共享buffer用于多线程并发。

  • 共享buffer由缓冲区块、缓冲区描述器以及引用计数器组成,主要涉及函数:

    • InitBufferPool
      1. 分配 缓冲区描述器和缓冲区块分配空间
      2. 初始化1000个描述器,每个描述器的next都指向下一个
    • BufferAlloc
      1. 从Hash表找相应缓冲区,有就加pin,等待io结束后将它返回
      2. 没有对应缓冲区就用替换机制找一个缓冲区
      3. 但如果这个缓冲区是脏的,且有排他锁,就需要再换一块,如果没有排他锁就把脏块写出
      4. 得到的缓冲区如果被其它进程占用,就要重新用替换机制找一个缓冲区了,没有占用就可以直接返回
  • 页面替换的 ClockSweep 机制

    按圈一个个找引用计数为0的,对它的usage_count减1,如果usage_count为0,就可以选择这块。usage_count是考虑使用次数多,意味着被其它进程引用的可能性大,最好不要选这样的块。

heap_insert函数

在这里插入图片描述

PageAddItem:
  1. 如果指定了偏移,就覆盖数据(检查下能不能覆盖)或者把这后面的itemid向后挪出一个itemid的校园
  2. 如果没指定就找有无回收空间
  3. 如果没回收空间就在空闲空间插入itemid
  4. itemid指向在空闲空间后面的位置,copy数据到这个位置,并修改页头的upper与lower指针

注:空闲空间不是马上清除,要等vacuum线程统一清理

RelationPutHeapTuple
  1. 根据 buffer (块号)获取块
  2. 执行 pageAddItem
  3. 更新 元组指针 itemid 的 tcid
heap_prepare_insert
  1. 设置元组头部的oid,infomask中包含insert的bit,cmin为当前命令cid,xmax为0(初始版本),表oid。
  2. 对于可以toast且元组长度大于threshold的元组调用toast_insert_or_update返回toast的版本。
RelationGetBufferForTuple
  1. 计算元组需要预留的大小,加上元组大小不能超过元组最大大小
  2. 要插入的页尽可能是刚插入过的页,没有的话就从fsm上获取一页,如果没有fsm信息或fsm不够大,就获取关系表的最后一页
  3. 为获取的页加锁
  4. 检查是否pin和lock成功了这些页,并保证成功(pin操作是在vm里)
  5. 检查空间是否足够,如果空间够了,就返回这个buffer,如果空间不够,就重新获取
  6. 如果fsm中都不够用,就扩展表
  7. 对于扩展后的表,要将页初始化,再返回这个buffer

参考内容

PostgreSQL 源码解读(5)- 插入数据#4(ExecInsert)

知识点

  • with check option 用于创建视图,保证插入删除与更新必须满足创建视图时的条件。
  • 虚拟元组:只有属性值的数组,不是真的元组结构

ExecInsert

在这里插入图片描述

ExecMaterializeSlot:用属性的值生成元组结构,在到相应slot
  1. 如果slot上有本地heaptuple,就在slot的context上分配HeapTupleBuffer,包括:元组长度、itemId、元组头,把这个heaptuple复制到slot的buffer上,释放原buffer
  2. slot上没有heaptuple,就获取所有属性的值,存在slot->PRIVATE_tts_values[i]上(虚拟元组)
  3. 根据虚拟元组生成真正的元组结构(heaptuple_form_to),填充在 slot 的本地 heaptuple,并让buffer指向它
ExecBRInsertTriggers :插入前trigger
  1. 遍历表的trigger:relinfo->ri_TrigDesc->triggers[i]
  2. 对每个 trigger 都用旧元组与trigger函数得到新元组:newtuple = ExecCallTriggerFunc(LocTriggerData, relinfo->ri_TrigFunctions, estate->context),其中 TriggerData 中包含了oldtuple,释放old元组
  3. 在estate->es_trig_tuple_slot上设置新元组描述符(ExecSetSlotDescriptor)并把元组放上去(ExecStoreTuple)
ExecIRInsertTriggers :与上同,是Instead时的事件
ExecWithCheckOptions:执行ExecQual来检查约束
ExecConstraints:检查非空限制与表限制
ExecCheckIndexConstraints:unique限制与exclusion限制
ExecOnConflictUpdate:
  1. 获取更新锁 heap_lock_tuple
  2. 检查元组可见性 ExecCheckHeapTupleVisible
  3. 把已有元组放在特定slot(mtstate->mt_existing,)中 ExecStoreHeapTuple
  4. 判断是否满足冲突条件 ExecQual
  5. 不冲突的话执行 withCheck 的检查
  6. 然后执行投影,放在特定slot上(resultRelInfo->ri_onConflictSetProj->pi_slot)
  7. 然后执行更新 ExecUpdate
HeapTupleSatisfiesUpdate:判断元组被占用状态
  1. xmin未提交
    1. xmin是invalid(有这个标记),返回 HeapTupleInvisible
    2. xmin是当前事务的
      1. cmin比当前命令id大,返回 HeapTupleInvisible
    3. xmax是invalid(有这个标记)
      1. 现在可能别的事务正在用,返回 HeapTupleMayBeUpdated
    4. 元组只是标记为 HEAP_XMAX_LOCK_ONLY,并不update
      1. 如果xmax就一个,就判断是否InProgress,返回 HeapTupleBeingUpdated,否则返回 HeapTupleMayBeUpdated
      2. 如果xmax是多个事务(有这个标记),就要判断每一个了:从 pg_upgraded 获取更新它的事务,这些事务可能是自己,也可能是其它事务,
    5. 如果元组没有这个标记,但是多事务,就取出update的事务,是自己,返回 HeapTupleSelfUpdatedHeapTupleInvisible
  2. xmin正在进行,返回 HeapTupleInvisible
  3. xmin确实提前了的话,就标记上,否则它已经abort了,返回 HeapTupleInvisible

参考内容

PostgreSQL 源码解读(6)- 插入数据#5(ExecModifyTable)
在这里插入图片描述
ExecModifyTable 是插入,更新,删除共同的入口

参考内容

PostgreSQL 源码解读(13)- 插入数据#12(PostgresMain)

知识点

c语言中的try catch可以用setjmp 与 longjmp 实现,setjmp(b) b中保存这时堆栈中的函数地址,longjmp(b, value)时,程序会运行到setjmp的位置,并返回value值,这样就可以通过value来判断错误类型

postgresmain

在这里插入图片描述

参考内容

PostgreSQL 源码解读(19)- 查询语句#4(ParseTree详解)

知识点

parseTree中的节点:

RangeSubselect:from 的子查询

RangeVar:JoinExpr 中的左右表节点

TargetList:select 的 fields

ResTarget:parseTree 中 TargetList 中的元素

当 SelectStmt 中 op 为 UNION,子查询中将有 larg 与 rarg

ColumnRef:parseTree 中的列,其 field 是一个 List,由表名与列名组成

QueryTree中的节点:

RangeTblEntry:from 的表

RangeTblRef:JoinExpr 中的左右表节点,是RangeTblEntry在rtable中的序号

TargetEntry:QueryTree 中的列

参考内容

PostgreSQL 源码解读(20)- 查询语句#5(查询树Query详解)

pg_analyze_and_rewrite 对select做的事

  1. 把涉及到的表与子查询都放在 rtable 中,结构为 RangeTblEntry,from、join、qual 等地方用到表时都变成对 rtable 的引用 RangeTblRef,有 fromlist,targetlist,joinlist,操作等。

  2. target 转为 TargetList 中的 TargetEntry

  3. jointree 中有 FromExpr 与 qual 条件,FromExpr 是一个列表,每个表项可以是 RangeTblRef 指向RangeTblEntry,也可以是 joinExpr

  4. RangeTblEntry 中表的别名为 alias,列的别名在 eref 的 List 里

  5. qual 是 OpExpr 节点,其中对于有常量的表达式有 args(为 RelabelType->Var 和 Const 节点),对于双变量表达式有 args (双 RelabelType->Var 节点)和 funcid,这个函数可以是简单的“=”函数

在这里插入图片描述

参考内容

PostgreSQL 源码解读(21)- 查询语句#6(PlannedStmt详解-跟踪分析)

知识点

如何开启跟踪日志?postgresql.conf配置文件设置参数:

log_destination = 'csvlog'
log_directory = 'pg_log' #与postgresql.conf文件在同一级目录
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_age = 2d
log_rotation_size = 100MB
#
debug_print_parse = on  #打印parse树
debug_print_rewritten = on #打印parse rewrite树
debug_print_plan = on #打印plan树
debug_pretty_print = on #以pretty方式显示

pg_plan_queries 中做了什么?

  1. 把所有涉及到的 RangeTblEntry 放在 root(PlannedStmt )的 rtable 上,涉及的表在 relationOids 上,生成的计划放在 PlannedStmt 的 planTree 节点
  2. 对于 NestLoop 节点,有 MergeJoin 、 SeqScan、HashJoin,三种节点均有RangeTblEntry 在 rtable 中的序号
    在这里插入图片描述
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值