参考内容
PostgreSQL 源码解读(81)- 查询语句#66(Review - exec_simp…
exec_simple_query:
参考内容
PostgreSQL 源码解读(82)- 查询语句#67(PortalXXX系列函数)
几种Portal:
PORTAL_ONE_SELECT:
- 包含一个SELECT查询。按需要的结果重复(递增)地运行执行器,支持可持有游标
PORTAL_ONE_RETURNING:
- 带有RETURNING子句的INSERT/UPDATE/DELETE查询
PORTAL_ONE_MOD_WITH:
- 只包含一个SELECT查询,但它具有数据修改的CTEs。
PORTAL_UTIL_SELECT:
- EXPLAIN或SHOW
PORTAL_MULTI_QUERY:
- 其它情况 如 create table
portal相关:create,start,run,drop
参考内容
PostgreSQL 源码解读(83)- 查询语句#68(PortalStart函数)
PostgreSQL 源码解读(84)- 查询语句#69(PortalStart->InitP…
PostgreSQL 源码解读(85)- 查询语句#70(PortalRun->InitPla…
PortalStart -> ExecutorStart -> InitPlan
参考内容
PostgreSQL 源码解读(86)- 查询语句#71(PortalRun->PortalR…
之前有中间保存的结果:创建slot,取元组,向远端发送,关闭远端,清除slot
第一次运行:切换上下文,开始计时,打开接收端,一条条调用plan的执行函数,一条条发送元组,结束计时,切回上下文
参考内容
PostgreSQL 源码解读(87)- 查询语句#72(PortalRunSelect->E…
seqscan节点的执行:红色为实际执行函数
表达式的计算用统一的入口:ExecEvalExprSwitchContext()
参考内容
PostgreSQL 源码解读(88)- 查询语句#73(SeqNext函数#1)
PostgreSQL 源码解读(89)- 查询语句#74(SeqNext函数#2)
访问元组的函数对 SeqScan 节点来说是 SeqNext 函数:
准备工作包括:pin表、计算页数、标记启始页、确定扫描条件、更新统计
扫描过程包括:获取页,判断元组可见性,判断串行冲突,判断符合扫描条件
参考内容
PostgreSQL 源码解读(90)- 查询语句#75(ExecHashJoin函数#1)
PostgreSQL 源码解读(91)- 查询语句#76(ExecHashJoin函数#2)
hashjoin的步骤:
- 为内循环创建hash表,利用hashvalue低位作为桶号,高位作为batch号,当表大小不够时扩大一倍的hash表,当扩大到超过了内存阈值,就要扩大总batch数(每次也是扩大一倍),将当前batch加入桶中,其它批次的输入到临时文件里。(不同batch相当于有不同的hashvalue)
- 外部获取一个batch,对每一条连接内部的元组
- 对没连接的外部元组连null内部
- 处理完成后,对右连接和全连接还要连null外部
在扩batch数的时候,内部元组马上重新hash,但已经到文件里的元组不会马上hash,而是保留在原来batch中,在join到这一个批次时会重新建这一batch的hash表,这时会把属于后面批次的值hash到后面批次。(内部重hash:**ExecHashJoinNewBatch **,外部重hash:**ExecHashJoin **-> HJ_NEED_NEW_OUTER)
参考内容
PostgreSQL 源码解读(93)- 查询语句#77(ExecHashJoin函数#3)
PostgreSQL 源码解读(95)- 查询语句#78(ExecHashJoin函数#4-H…
PostgreSQL 源码解读(97)- 查询语句#79(ExecHashJoin函数#5-H…
- 从文件获取元组时不是每次都读文件,而是一次读满一个buffer的大小,再一条条取
- 获取列值不是数组或指针方式获取,而是用函数来获取
ExecScanHashBucket 获取桶元组也是两步,从桶数组中获取对应最小元组,并判断是否满足条件,满足就返回
参考内容
PostgreSQL 源码解读(92)- 分区表#1(数据插入路由#1)
PostgreSQL 源码解读(94)- 分区表#2(数据插入路由#2)
PostgreSQL 源码解读(96)- 分区表#3(数据插入路由#3-获取分区键值)
PostgreSQL 源码解读(98)- 分区表#4(数据查询路由#1-“扩展”分区表)
PostgreSQL 源码解读(99)- 分区表#5(数据查询路由#2-RelOptInfo数…
PostgreSQL 源码解读(100)- 分区表#6(数据查询路由#3-prune part…
PostgreSQL 源码解读(101)- 分区表#7(数据查询路由#4-prune part…
PostgreSQL 源码解读(102)- 分区表#8(数据查询路由#5-构建APPEND访问路径)
PostgreSQL 源码解读(103)- 分区表#9(数据查询路由#6-APPEND初始化和实现)
append 表有两种:继承表(分区表)或 UNION ALL 子查询。
hash 值合并函数: a ^= b + UINT64CONST(0x49a0f4dd15e5a8e3) + (a << 54) + (a >> 7);
执行插入时涉及 ExecModifyTable 需要对分区路由
PortalRunMulti
-> ProcessQuery
-> ExecutorRun
-> ExecutePlan
-> ExecProcNode
-> ExecModifyTable
-> ExecPrepareTupleRouting 分区的路由
修剪分区发生在限制条件都准备好之后:
subquery_planner
-> grouping_planner
-> query_planner 查询优化
-> add_other_rels_to_query 修剪分区表入口
-> expand_inherited_rtentry 修剪分区表,创建RTE
-> expand_partitioned_rtentry 递归展开所有分区表到 root->append_rel_list 里
生成修剪分区步骤的大概过程:
- 归约限制条件至最简
- 找到限制条件中涉及的列,它们属于那种分区条件
- 对每种分区条件生成修剪的步骤
- is null 条件:
- 对列表分区和范围分区可直接生成步骤把非null的分区裁剪掉
- 对于hash分区,必须所有分区键都涉及is null 条件才裁剪
- op 条件:
- 生成步骤
- is not null 条件:
- 生成步骤,删除 null 分区
- is null 条件:
执行裁剪:
裁剪对 hash、列表、范围 用不同策略
分区表路径生成过程
subquery_planner
-> grouping_planner
-> query_planner 查询优化
-> make_one_rel 生成路径
-> set_base_rel_pathlists 生成表访问路径
-> set_append_rel_pathlist 生成分区表的路径
生成了四种路径
参考内容
PostgreSQL 源码解读(103)- 分区表#9(数据查询路由#6-APPEND初始化和实现)
append 操作的初始化
append 操作执行
参考内容
参见PostgreSQL 源码解读(104)- WAL#1(Insert & WAL-heap_insert函数#1)
参见PostgreSQL 源码解读(105)- WAL#2(Insert & WAL-heap_insert函数#2)
知识点:
WAL:Write after Logging 在数据之前写日志
虽然是先写数据到页缓冲区再写日志,但刷新到磁盘时要先刷新日志,再刷新数据。LSN是日志的最后一条的结束位置,只有日志都写好了才设置LSN。
bootstrap 模式:引导模式,启动模式
插入数据时 heap_insert
先检查串行冲突,再加锁一块表的页缓冲区,插入元组到缓冲区,标记脏块,并记录这个插入日志。最后把这个临时元组标记为无效,并更新页面统计信息
日志内容包括:页头,元组头,元组内容。(最后要在日志页的头部设置LSN)
参考内容
参见PostgreSQL 源码解读(106)- WAL#3(Insert & WAL-heap_insert函数#3)
参见PostgreSQL 源码解读(107)- WAL#4(Insert & WAL-heap_insert函数#4)
知识点:
临界区:CritSectionCount
- 在处理中断(ProcessClientWriteInterrupt、ProcessInterrupts)时,必须没有数据处于临界区(CritSectionCount != 0),否则直接返回
- 创建上下文(MemoryContextCreate)时要求没有数据在临界区(避免内存冲突)
- 所有log相关操作(log_newpage_buffer、XLogEnsureRecordSpace、XLogWrite、XactLogAbortRecord、XactLogCommitRecord)要在临界区做(log不可以被中断)
- 初始化错误报告(errstart)时,如果在临界区内,则要写到server log里
插入日志底层实现 XLogInsert:
由于需不需要备份页数据与Redo的位置有关,所以当Redo位置改变时,要重新判断,重新assemble(红色箭头)
数据包含了四块:记录头部、页上数据(需要备份的话)、原缓冲区数据、实际 logRecord 的数据
获取与释放锁的过程(下图黄色部分)
获取过程:
- 关闭一个信息中断
- 获取一次,不成功的话就把自己放在队列,等待信号
释放过程:
- 把自己的锁从锁队列里移出来并释放
- 收集多个共享锁或一个排他锁
- 为它们发送唤醒信号