postgresql insert ddl执行流程分析

 


目录

前言

总体流程

调用堆栈

执行接口说明

详细流程分解

ExecInsert对于普通表的insert;

插入tuple的流程,调用table_tuple_insert(heapam_tuple_insert)中;

heap_insert的流程:

索引创建过程简述

结尾


前言

本文是基于postgresql 15的代码进行分析解读,演示是在centos8系统上进行。


总体流程

(1)如果是分区表,需要找到对应子表;

(2)待插入表是否有索引,需要打开对应的索引表;

(3)处理before row insert triggers

(4)处理instead of row insert triggers

(5)如果是Fdw(foreign-data wrapper)接口的表,单独处理

(6)处理tuple检查和insert buffer

(7)如果是分区表,引起分区key变化,导致有tuple要移动到新分区时,处理数据的delete/insert;

(8)处理after row insert triggers

(9) 处理with check option;

(10) 生成returning

调用堆栈

ExecInsert(ModifyTableState * mtstate, ResultRelInfo * resultRelInfo, TupleTableSlot * slot, TupleTableSlot * planSlot, EState * estate, _Bool canSetTag)

ExecModifyTable(PlanState * pstate)

ExecProcNode(PlanState * node)

ExecutePlan(_Bool execute_once, DestReceiver * dest, ScanDirection direction, uint64 numberTuples, CmdType operation, _Bool use_parallel_mode, PlanState * planstate, EState * estate)

standard_ExecutorRun(QueryDesc * queryDesc, ScanDirection direction, uint64 count, _Bool execute_once)

ProcessQuery(PlannedStmt * plan, const char * sourceText, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc)

PortalRunMulti(Portal portal, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)

PortalRun(Portal portal, long count, _Bool isTopLevel, _Bool run_once, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)

exec_simple_query(const char * query_string)

PostgresMain(const char * dbname, const char * username)

BackendRun()

BackendStartup()

ServerLoop()

PostmasterMain(int argc, char ** argv)

main(int argc, char ** argv)

在经过SQL解析,生成执行plan后,就开始调用PortalRun进行执行阶段,最后调用ExecutePlan,按照plan中的每个node进行执行,不同的node有不同的类型,比如merge/sort等等(详细参见本专栏的执行计划分享),每种类型有对应的执行调用,对于insert的执行动作,主要由ExecInsert来完成。

执行接口说明

static TupleTableSlot *

ExecInsert(ModifyTableContext *context,

           ResultRelInfo *resultRelInfo,

           TupleTableSlot *slot,

           bool canSetTag,

           TupleTableSlot **inserted_tuple,

           ResultRelInfo **insert_destrel)

slot,中存储有需要insert的tuple;

**inserted_tuple,插入成功后的tuple;

**inserted_destrel,是出参,新tuple插入成功后的表,因为这里有可能是不同的分区表;

插入不成功函数返回值为NULL。

详细流程分解

postgresql在整个处理时,进行分层处理,这里涉及到了两层:

一是执行层,对应的调用是ExecInsert

二是heapam层,也就是针对heap类型表的操作;这里是为了支持多种类型表而留了扩展设计的空间;heapam层对应的调用是table_tuple_insert

  • ExecInsert对于普通表的insert;

(1)如果有自动生成列,进行数据生成;

(2)with check option类型判断和处理;

(3)检查约束

(4)检查分区表约束,是否符合分区条件;

(5)是否有冲突处理策略,如果有需要检查处理;

(6)如果没有冲突处理策略,则正常插入tuple和创建索引;

  • 插入tuple的流程,调用table_tuple_insert(heapam_tuple_insert)中;

(1)从slot记录的tuple数据,拼装成要插入的tuple;

(2)获取table OID

(3)调用heap_insert,查找空闲空间将tuple插入page,记录wal等;

(4)将插入的tuple的tid记录到slot;方便后面进行索引的插入;

(5)如果有申请tuple空间,在这里释放;

  • heap_insert的流程:

(1)准备tuple,在拼装好的tuple上继续填写infomask/xmin/xmax等;对于可压缩的,当前tup大于toast存储阈值时,要拼成toast存储tuple;

(2)查找有空闲空间的buffer来插入当前tuple;同时检查是否都可见,标记vm文件;(查找空闲空间的流程请查看本专栏的文章)

(3)检查串行化级别的冲突;

(4)开启关键代码区;将tuple放到buffer中;

(5)更新可见性信息;

(6)标脏buffer;

(7)写WAL日志;结束关键代码区;

(8)释放buffer,vmbuffer;

(9)如果插入的是系统表的tuple,需要invalidatecache;

(10)记录t_cid给返回者,释放申请的tuple;

  • 索引创建过程简述

(1)在tuple插入成功后,得到tuple的tid;

(2)调用ExecInsertIndexTuples,执行indextuple的插入;这是在执行层的调用;

(3)每张表可以有多个index,所以扫描数据字典,查找有多少个索引;然后每个索引进行分别插入;

(4)对于每个索引,调用对应的Indexam层接口进行处理;这里调用index_insert,它内部调用indexRelation->rd_indam->aminsert,这是在创建索引时初始化好的。索引相关详细内容查看本专栏的内容。


结尾

作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!

猜你喜欢

转载自blog.csdn.net/senllang/article/details/130472467
今日推荐