绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
Citus 前身pg shard代码的执行阶段分析
2022-04-13 13:53:07

Citus是PostgreSQL世界观下,少数成功突围的商业化的开源产品(相比较pgxc、xl等官方几乎没有维护来说)。
1 前身 pg_shard

citus实现不能说完美,但的确相当精巧,现在citus项目的代码迭代已经非常多了,想要从当前代码进行一些逻辑分析,是相对比较困难的,但citus的前身,pg_shard项目却有一个简单直观的执行模型,可以相对简单地进行观察,之后,也可以看到citus在此基础上,进行的进一步的优化和改造动作。
1.1 pg_shard主体代码结构

这里分析的主入口,就是插件初始化时候的_PG_init。
1.1.1 插件init初始化阶段

主要牵涉到五个不同的入口

分别是:

执行计划hook:PgShardPlanner
执行启动时候的hook:PgShardExecutorStart
实际执行的hook:PgShardExecutorRun
执行完成的hook:PgShardExecutorFinish
执行结束的hook:PgShardExecutorEnd

这里也提供几个选项(参数均为任何时候可以设置,不需要重启数据库):

AllModificationsCommutative 对应选项pg_shard.all_modifications_commutative,如果设置为true,则所有修改操作都会加共享锁,否则update和delete会加排他锁
UseCitusDBSelectLogic 对应选项 pg_shard.use_citusdb_select_logic 是否直接查询逻辑表而非shard,这里会标记查询类型为PLANNER_TYPE_CITUSD
LogDistributedStatements 对应选项pg_shard.log_distributed_statements 是否记录分布式(对每个shard)的执行语句

1.1.2 查询计划Planner代码结构

这里实际上分了三部分处理,对于shard表,进行分布式计划处理,对于citus元数据,pg原生表则继续使用pg本身的逻辑(也就是说,对于多个sharing入口,元数据需要自己维护一致性)

接下来看pgshard的分布式处理,由于代码过多,这里进进行关键逻辑分析。

BuildDistributedPlan 创建分布式执行计划

DistributedPlan数据结构

执行列表taskList
目标列表targetList
标记是否是多源查询selectFromMultipleShards
对于需要创建临时结果表的,提供临时表建表语句createTemporaryTableStmt

DistributedQueryShardList 获取查询中需要访问的shard的列表

LookupShardIntervalList 从表id查询shard列表,并缓存LoadShardIntervalList的结果

PruneShardList 根据查询条件确定终的shard列表

对于range,append调用 BuildRestrictInfoList
对于hash 调用HashableClauseMutator

对于需要访问多个shard节点的查询,下推行列过滤语句(不包括join),然后设置临时表来缓存数据

RowAndColumnFilterQuery 设置过滤语句
BuildLocalQuery 设置本地查询
PlanSequentialScan 对于逻辑表的查询指向临时表
CreateTemporaryTableLikeStmt 创建临时表

deparse_shard_query 生成对应的查询用SQL语句(不同版本分别处理),get_shard_query_def 实际执行的函数

with子语句
union等组操作语句
get_basic_select_query获取基本的查询语句
    处理distinct
    拼接列名称
    处理from条件
    处理where条件
    处理group by条件
    处理having条件
加锁语句 (for update,share)
order by语句
limit语句

1.1.3 查询执行前PgShardExecutorStart代码结构

这里对于只访问一个shard的情况,PreventTransactionChain关闭掉分布式事务,处理事务相关的锁,因为不需要缓存结果集,因此实际执行到后面的PgShardExecutorRun部分执行

对于访问多个shard的情况,则在这一步就创建临时结果集:

执行create temp table like创建临时表
ExecuteMultipleShardSelect执行多个节点的查询
    for循环(这里不是并行的)对每个shard分别执行ExecuteTaskAndStoreResults
UnregisterSnapshot,UpdateActiveSnapshotCommandId,RegisterSnapshot 升级快照让数据可见
RangeVarGetRelid 让实际查询指针指向临时表

1.1.4 查询执行中PgShardExecutorRun代码结构

这里首先拒绝掉(对shard的查询)不支持的查询:

如果scan是forword的情况
如果使用游标而非直接结果的情况

queryDesc->totaltime 更新查询的执行时间

对于insert,update,delete执行ExecuteDistributedModify

for循环逐个执行
GetConnection获取节点连接
PQexec执行节点上查询
PQcmdTuples 处理查询的结果集
返回 affectedTupleCount(对于修改来说,只需要返回影响行数)

对于select,执行 ExecuteSingleShardSelect,其他情况直接报错,需要注意的是,对多个shard的查询之前实际上已经在start部分处理,因此这里仅处理单个shard的查询,执行task来自linitial(tasks列表个ta’s)

执行ExecuteTaskAndStoreResults来获取查询的实际结果集

MakeSingleTupleTableSlot创建结果的描述信息

while true循环来处理收取到的结果集

ExecuteTaskAndStoreResults
    for循环执行task的taskPlacementCell
    SendQueryInSingleRowMode执行查询
    StoreQueryResult保存结果集

1.1.5 查询执行完成PgShardExecutorFinish代码结构

这个的作用主要是清理掉期间的数据结构

标记es_finished=true
1.1.6 查询结束PgShardExecutorEnd代码结构

这个的作用主要是清理掉期间的数据结构

标记es_finished=true

这里两个hook完全相同,应该是为后续定制预留入口。

分享好友

分享这个小栈给你的朋友们,一起进步吧。

Citus
创建时间:2022-04-13 09:43:21
Citus
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

技术专家

查看更多
  • 飘絮絮絮丶
    专家
戳我,来吐槽~