Postgresql源码(132)分布式行锁的原理分析

news2024/11/15 11:35:18

相关
《Postgresql源码(131)行锁的原理分析》

1 分布式行锁

PG中的行锁在上一片中做了分析《Postgresql源码(131)行锁的原理分析》,本篇对分布式PG(PGXL)中的行锁做一些分析。(版本:Postgres-XL 10alpha2)

2 计划生成pgxc_planner

分布式PG中的计划生成有两个入口:

pgxc_planner
	result = pgxc_FQS_planner(query, cursorOptions, boundParams);
	if (result) return result;
	result = standard_planner(query, cursorOptions, boundParams);
	return result;
  • pgxc_FQS_planner(Fast Query Shipping planner)尝试确定一个查询是否可以完全在DN上执行,不需要CN节点参与计算。FQS计划比较简单,直接把SQL发到某几个DN上跑。
  • standard_planner是标准查询规划器。

查询首先通过pgxc_FQS_planner看是否适合快速分发。如果不适合,会继续走standard_planner。

2.1 pgxc_FQS_planner生成FQS计划

XL默认对行锁的SQL不能走FQS,这里为了简单介绍下FQS用了一个点查的例子。

用例

drop table TBL_33;
create table TBL_33(c3 int);
insert into TBL_33 values(0);
SELECT c3 FROM TBL_33 WHERE c3=0;;

分布式执行计划

explain SELECT c3 FROM TBL_33 WHERE c3=0;
                          QUERY PLAN
---------------------------------------------------------------
 Remote Fast Query Execution  (cost=0.00..0.00 rows=0 width=0)
   Node/s: datanode_2
   ->  Seq Scan on tbl_33  (cost=0.00..41.88 rows=13 width=4)
         Filter: (c3 = 0)

pgxc_FQS_planner

static PlannedStmt *
pgxc_FQS_planner(Query *query, int cursorOptions, ParamListInfo boundParams)
{
  • 用规则过滤一些不能FQS的情况:
	if (!enable_fast_query_shipping)
		return NULL;
	if (cursorOptions & CURSOR_OPT_SCROLL)
		return NULL;
	if (query->utilityStmt && IsA(query->utilityStmt, RemoteQuery))
	{
		RemoteQuery *stmt = (RemoteQuery *) query->utilityStmt;
		if (stmt->exec_direct_type != EXEC_DIRECT_NONE)
			return NULL;
	}

  • 遍历查询树,用一些规则排除不能FQS的情况。
  • pgxc_shippability_walker函数在遍历的同时,会维护一个bitmap(sc_context.sc_shippability),里面记录了不能ship的各种原因,最后在pgxc_is_query_shippable函数中检测bitmap确认是否能ship。
  • exec_nodes中记录的最重要的信息就是需要在哪个节点上执行,由pgxc_FQS_find_datanodes函数计算出来。
  • 计算逻辑:
    在这里插入图片描述
	exec_nodes = pgxc_is_query_shippable(query, 0);
	if (exec_nodes == NULL)
		return NULL;

	glob = makeNode(PlannerGlobal);
	glob->boundParams = boundParams;
	root = makeNode(PlannerInfo);
	root->parse = query;
	root->glob = glob;
	root->query_level = 1;
	root->planner_cxt = CurrentMemoryContext;

	top_plan = (Plan *)pgxc_FQS_create_remote_plan(query, exec_nodes, false);

	top_plan = set_plan_references(root, top_plan);

	result = makeNode(PlannedStmt);
	result->commandType = query->commandType;
	result->canSetTag = query->canSetTag;
	result->utilityStmt = query->utilityStmt;

	if (query->commandType != CMD_SELECT)
		result->resultRelations = list_make1_int(query->resultRelation);
	result->planTree = top_plan;
	result->rtable = query->rtable;
	result->queryId = query->queryId;
	result->relationOids = glob->relationOids;
	result->invalItems = glob->invalItems;

	return result;
}

在这里插入图片描述

  • FQS的计划会比较简单,基本就是把SQL用deparse_query出来,然后拼到计划节点中,找到发到哪些节点执行即可。

2.2 standard_planner生成remote计划

回到行锁用例上:

drop table TBL_33;
create table TBL_33(c33 int);
insert into TBL_33 values(0);

SELECT c33 FROM TBL_33 WHERE c33=0 for update;

分布式执行计划

explain SELECT c33 FROM TBL_33 WHERE c33=0 for update;
                                  QUERY PLAN
-------------------------------------------------------------------------------
 Remote Subquery Scan on all (datanode_2)  (cost=0.00..42.01 rows=13 width=10)
   ->  LockRows  (cost=0.00..42.01 rows=13 width=10)
         ->  Seq Scan on tbl_33  (cost=0.00..41.88 rows=13 width=10)
               Filter: (c33 = 0)

2.2.1 subquery_planner→grouping_planner生成local计划

subquery_planner生成计划:
在这里插入图片描述

2.2.2 make_remotesubplan为计划添加remote算子

standard_planner → make_remotesubplan

standard_planner
	...
	best_path = get_cheapest_fractional_path(final_rel, tuple_fraction);
	if (!root->distribution)
		root->distribution = best_path->distribution;
	top_plan = create_plan(root, best_path);
	if (root->distribution)
		top_plan = (Plan *) make_remotesubplan(root, top_plan, NULL, root->distribution, root->sort_pathkeys);

在这里插入图片描述

2.2.3 path的distribution信息从哪来?

explain SELECT c33 FROM TBL_33 WHERE c33=0 for update;
                                  QUERY PLAN
-------------------------------------------------------------------------------
 Remote Subquery Scan on all (datanode_2)  (cost=0.00..42.01 rows=13 width=10)
   ->  LockRows  (cost=0.00..42.01 rows=13 width=10)
         ->  Seq Scan on tbl_33  (cost=0.00..41.88 rows=13 width=10)
               Filter: (c33 = 0)

SELECT c33 FROM TBL_33 WHERE c33=0 for update;执行时会生成两个算子:

  • create_seqscan_path
  • create_lockrows_path
create_seqscan_path
Path *
create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
					Relids required_outer, int parallel_workers)
{
	Path	   *pathnode = makeNode(Path);

	pathnode->pathtype = T_SeqScan;
	pathnode->parent = rel;
	pathnode->pathtarget = rel->reltarget;
	pathnode->param_info = get_baserel_parampathinfo(root, rel,
													 required_outer);
	pathnode->parallel_aware = parallel_workers > 0 ? true : false;
	pathnode->parallel_safe = rel->consider_parallel;
	pathnode->parallel_workers = parallel_workers;
	pathnode->pathkeys = NIL;	/* seqscan has unordered result */

#ifdef XCP
  • set_scanpath_distribution会配置pathnode->distribution信息,标记计划需要发到哪个节点执行。
  • restrict_distribution会更严格的检查计划发到哪个节点。
	set_scanpath_distribution(root, rel, pathnode);
	if (rel->baserestrictinfo)
	{
		ListCell *lc;
		foreach (lc, rel->baserestrictinfo)
		{
			RestrictInfo *ri = (RestrictInfo *) lfirst(lc);
			restrict_distribution(root, ri, pathnode);
		}
	}
#endif
	cost_seqscan(pathnode, root, rel, pathnode->param_info);
	return pathnode;
}

  • 经过set_scanpath_distribution后
    • pathnode->distribution->nodes标记了dn0、dn1。
    • p/t pathnode->distribution->nodes->words[0] = 11
  • 经过restrict_distribution后
    • pathnode->distribution->nodesrestrictNodes只标记了datanode1。
    • p/t pathnode->distribution->restrictNodes->words[0] = 10
p	*pathnode->distribution
$27 = {type = T_Distribution, distributionType = 72 'H', distributionExpr = 0x135fea8, nodes = 0x1360650, restrictNodes = 0x1360898}
(gdb) p/t pathnode->distribution->nodes->words[0]
$31 = 11
(gdb) p/t pathnode->distribution->restrictNodes->words[0]
$30 = 10
create_lockrows_path
  • lockrows节点比较特殊,不需要做什么事情,执行器会在执行阶段特殊处理。
  • pathnode→distribution信息集成subplan的即可。
LockRowsPath *
create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
					 Path *subpath, List *rowMarks, int epqParam)
{
	LockRowsPath *pathnode = makeNode(LockRowsPath);

	pathnode->path.pathtype = T_LockRows;
	...
	...	
	pathnode->path.distribution = copyObject(subpath->distribution);
	
	...
	...
	return pathnode;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1705458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CDGA|像治理空气和水一样来治理数据:构建数字时代的新秩序

在数字时代,数据已经渗透到我们生活的方方面面,成为推动社会发展的重要引擎。然而,随着数据的爆炸式增长,如何有效治理数据,确保其安全、可靠和合规,成为摆在我们面前的一大挑战。为了应对这一挑战&#xf…

期望薪资26K,北京疯狂游戏golang一面

北京疯狂游戏一面 1、自我介绍 2、财务业务中,你做了哪些设计来保证金额数据的准确性?(例如,业务涉及多步骤,某一步出了问题怎么解决) 3、如何解决单个业务直接报错的数据准确性问题 4、分布式场景下&a…

Springboot事务控制中A方法调用B方法@Transactional生效与不生效情况实战总结

介绍 本篇对Springboot事务控制中A方法调用B方法Transactional生效与不生效情况进行实战总结,让容易忘记或者困扰初学者甚至老鸟的开发者,只需要看这一篇文章即可立马找到解决方案,这就是干货的价值。喜欢的朋友别忘记来个一键三连哈&#x…

如何简化不同网间文件摆渡的操作流程,降低IT人员工作量?

为了保护内部核心数据不被泄露,同时有效屏蔽外部网络攻击的风险,企业大多会选择实施网络隔离。将“自己人”与“外人”隔离,具有较强的安全敏感性。有些企业还会在内部网络中进一步划分,比如划分为研发网、办公网、生产网等&#…

【基于 PyTorch 的 Python 深度学习】9 目标检测与语义分割(2)

前言 文章性质:学习笔记 📖 学习资料:吴茂贵《 Python 深度学习基于 PyTorch ( 第 2 版 ) 》【ISBN】978-7-111-71880-2 主要内容:根据学习资料撰写的学习笔记,该篇主要介绍了优化候选框的几种方法。 一、优化候选框的…

Java 面向对象编程(OOP)

面向对象编程(Object-Oriented Programming,OOP)是Java编程语言的核心思想之一。通过OOP,Java提供了一种结构化的编程方式,使代码更易于维护和扩展。 一、类和对象 1. 类的定义 类是对象的蓝图或模板,定…

回收站删掉的照片还能找回来吗?掌握这5个方法,很简单!

“我一不小心在回收站中误删了一些照片,这些照片对我来说是比较重要的,不知道这些还有机会将它们找回来吗?” 当我们不小心将珍贵的照片从计算机的回收站中误删时,那种瞬间的心痛和焦虑难以言表。这些照片可能记录了我们生活中的重…

大数据开发面试题【Kafka篇】

83、介绍下Kafka,Kafka的作用?Kafka的组件?适用场景? kafka是一个高吞吐量、可扩展的分布式消息传递系统,在处理实时流式数据,并能够保证持久性和容错性 可用于数据管道、流分析和数据继承和关键任务应用(发布/订阅模式&#…

雷达基数据绘制成雷达图

x波段雷达基数据绘制成雷达图 1.雷达基数据格式Z_RADR_I_ZR001_20240521020002_O_DOR_YLD2-D_CAP_FMT.bin.bz2 2.基数据读取 python f StandardData(i) # 新版本标准数据radarTime f.scantime # 获取雷达时次date_str radarTime.strftime(%Y-%m-%d %H:%M:%S)date_str d…

盘点10大灵动惊艳小演员❗谁是你的心头好?

盘点国内影视那些惊艳观众的小演员们无疑为影视作品注入了新的活力。以下是10个备受赞誉的小演员: 1.韩昊霖:凭借在《我和我的祖国》和《庆余年》中的出色表现 韩昊霖的演技赢得了观众和业界的广泛认可 他能够准确地把握角色的情感和细节,展…

STM32_USART

1、USART简介 USART,即Universal Synchronous/Asynchronous Receiver/Transmitter,通用同步/异步收发器。USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自…

claude3国内API接口对接

众所周知,由于地理位置原因,Claude3不对国内开放,而国内的镜像网站使用又贵的离谱! 因此,团队萌生了一个想法:为什么不创建一个一站式的平台,让用户能够通过单一的接口与多个模型交流呢&#x…

点云工具CloudCompare下载、安装与汉化

一、下载 软件免费开源,所以可以直接在官网下载,官网地址: CloudCompare - Open Source project 进入官网后,点击菜单栏【Download】 选择合适系统进行下载 二、安装 常规软件安装流程即可 三、汉化 打开软件后,…

社交媒体数据恢复:微信电话本

首先,请确保您的微信已登录,并且您具有管理员权限。接下来,按照以下步骤进行操作: 第一步:备份微信数据 在进行数据恢复之前,建议您先备份微信数据。这可以帮助您在数据丢失的情况下更好地保护您的微信数据…

成都百洲文化传媒有限公司引领电商服务新风尚

在当今数字化时代,电商行业正以前所未有的速度蓬勃发展,而在这个充满机遇与挑战的领域中,成都百洲文化传媒有限公司以其专业的电商服务,成为了行业的佼佼者。作为一家专注于电商服务的传媒公司,百洲文化不仅为商家提供…

NDK下载与配置以及遇到的问题

通过 Android Studio进行下载或者官网下载,下面是在 androidStudio中下载在项目中配置ndk 菜单栏点开 File-》Project Structure,可以看到配置好的ndk配置ndk的系统环境变量,系统变量 -> 新建一个变量名为 NDK_HOME -> 变量值为文件路…

新旅程:类与对象的魔法课堂

🎉🎉🎉欢迎莅临我的博客空间,我是池央,一个对C和数据结构怀有无限热忱的探索者。🙌 🌸🌸🌸这里是我分享C/C编程、数据结构应用的乐园✨ 🎈🎈&…

AI大模型在测试中的深度应用与实践案例

文章目录 1. 示例项目背景2. 环境准备3. 代码实现3.1. 自动生成测试用例3.2. 自动化测试脚本3.3. 性能测试3.4. 结果分析 4. 进一步深入4.1. 集成CI/CD管道4.1.1 Jenkins示例 4.2. 详细的负载测试和性能监控4.2.1 Locust示例 4.3. 测试结果分析与报告 5. 进一步集成和优化5.1. …

RK3588 camera驱动总结二之图像格式

camera驱动中很重要的一个设置就是格式,此文来看看这块。 在驱动中有个重要的参数mbus-code,即Media Bus Pixel Codes,它描述的是用于在物理总线上传输的格式,比如 sensor 通过mipi dphy 向 isp 传输的图像格式,或者在…

AI预测体彩排3采取888=3策略+和值012路一缩定乾坤测试5月28日预测第4弹

今天继续基于8883的大底进行测试,今天继续测试,好了,直接上结果吧~ 首先,888定位如下: 百位:3,4,2,5,0,7,8,9 十位:3,2,4,1,6,7,8,9 个位:0,1,2,3,4,5,6,7 …