PostgreSQL源码分析——COPY

导入数据的几种方式

在进行数据导入导出时常会用到copy命令,语法使用可参考下面这篇博文
[Postgres] Bulk Insert and Export Data with csv Files with Postgres copy Command。通常导入数据的方法,可以通过insert的方式(insert into t1 values v1)向表中插入数据,当然这是最慢的方法了,比这个更快的是批量插入,就是每次通过一条insert插入多条数据(insert into t1 values v1,v2,v3,...)。比这个更快的就是copy了。我们来分析一下COPY的实现。

在进行分析copy之前,我们先理解一下为什么批量插入较快?

为什么批量插入比单条插入快

这里postgres中的代码注释写的很明白,另外一点就是在语法解析,语义分析,执行计划生成这块,批量插入相比单条插入也省了很多时间,不用每条插入都执行解析、执行计划生成,只需一次即可,而copy则连计划生成这步都省了,直接批量构造tuple插入表中。

/** Insert multiple tuples into a table.** This is like table_tuple_insert(), but inserts multiple tuples in one* operation. That's often faster than calling table_tuple_insert() in a loop,* because e.g. the AM can reduce WAL logging and page locking overhead.*/
static inline void
table_multi_insert(Relation rel, TupleTableSlot **slots, int nslots,CommandId cid, int options, struct BulkInsertStateData *bistate)
{rel->rd_tableam->multi_insert(rel, slots, nslots, cid, options, bistate);
}/**	heap_multi_insert	- insert multiple tuples into a heap** This is like heap_insert(), but inserts multiple tuples in one operation.* That's faster than calling heap_insert() in a loop, because when multiple* tuples can be inserted on a single page, we can write just a single WAL* record covering all of them, and only need to lock/unlock the page once.*/
void
heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,CommandId cid, int options, BulkInsertState bistate){// ...}

下面我们继续对copy的源码进行分析

copy源码分析

copy的核心代码在/src/backend/commands/copy.c/src/backend/commands/copyfrom.c/src/backend/commands/copyto.c

主流程如下:

exec_simple_query
--> pg_parse_query--> raw_parser
--> pg_analyze_and_rewrite--> parse_analyze--> transformStmt
--> pg_plan_queries  // 属于utility command, 无需进行查询优化,相比insert快的原因之一,insert需要完整的走解析,优化
--> PortalRun--> ProcessUtility--> standard_ProcessUtility--> DoCopy  // 执行copy语句
语法解析层

关键数据结构:

/* ----------------------*		Copy Statement** We support "COPY relation FROM file", "COPY relation TO file", and* "COPY (query) TO file".  In any given CopyStmt, exactly one of "relation"* and "query" must be non-NULL.* ---------------------- */
typedef struct CopyStmt
{NodeTag		type;RangeVar   *relation;		/* the relation to copy */  // 最关键的就是表名和文件路径名Node	   *query;			/* the query (SELECT or DML statement with RETURNING) to copy, as a raw parse tree */List	   *attlist;		/* List of column names (as Strings), or NIL for all columns */bool		is_from;		/* TO or FROM */bool		is_program;		/* is 'filename' a program to popen? */char	   *filename;		/* filename, or NULL for STDIN/STDOUT */List	   *options;		/* List of DefElem nodes */Node	   *whereClause;	/* WHERE condition (or NULL) */
} CopyStmt;

gram.y中copy命令的语法定义:

/*******************************************************************************		QUERY :*				COPY relname [(columnList)] FROM/TO file [WITH] [(options)]*				COPY ( query ) TO file	[WITH] [(options)]**				where 'query' can be one of:*				{ SELECT | UPDATE | INSERT | DELETE }**				and 'file' can be one of:*				{ PROGRAM 'command' | STDIN | STDOUT | 'filename' }**				In the preferred syntax the options are comma-separated*				and use generic identifiers instead of keywords.  The pre-9.0*				syntax had a hard-wired, space-separated set of options.**				Really old syntax, from versions 7.2 and prior:*				COPY [ BINARY ] table FROM/TO file*					[ [ USING ] DELIMITERS 'delimiter' ] ]*					[ WITH NULL AS 'null string' ]*				This option placement is not supported with COPY (query...).******************************************************************************/CopyStmt:	COPY opt_binary qualified_name opt_column_listcopy_from opt_program copy_file_name copy_delimiter opt_withcopy_options where_clause{CopyStmt *n = makeNode(CopyStmt);n->relation = $3;n->query = NULL;n->attlist = $4;n->is_from = $5;n->is_program = $6;n->filename = $7;n->whereClause = $11;if (n->is_program && n->filename == NULL)ereport(ERROR,(errcode(ERRCODE_SYNTAX_ERROR),errmsg("STDIN/STDOUT not allowed with PROGRAM"),parser_errposition(@8)));if (!n->is_from && n->whereClause != NULL)ereport(ERROR,(errcode(ERRCODE_SYNTAX_ERROR),errmsg("WHERE clause not allowed with COPY TO"),parser_errposition(@11)));n->options = NIL;/* Concatenate user-supplied flags */if ($2)n->options = lappend(n->options, $2);if ($8)n->options = lappend(n->options, $8);if ($10)n->options = list_concat(n->options, $10);$$ = (Node *)n;}| COPY '(' PreparableStmt ')' TO opt_program copy_file_name opt_with copy_options{CopyStmt *n = makeNode(CopyStmt);n->relation = NULL;n->query = $3;n->attlist = NIL;n->is_from = false;n->is_program = $6;n->filename = $7;n->options = $9;if (n->is_program && n->filename == NULL)ereport(ERROR,(errcode(ERRCODE_SYNTAX_ERROR),errmsg("STDIN/STDOUT not allowed with PROGRAM"),parser_errposition(@5)));$$ = (Node *)n;};// 其余省略,可到gram.y中查看 ...
执行器层

这里只分析copy from, copy to同理。

copy的核心实现就是DoCopy函数,我们进行分析,核心流程如下,可以看到,copy from,就是跳过解析,优化,直接进入执行器,提取文件中的数据,构造tuple,每1000 tuple为一批tuple,然后执行table_multi_insert批量插入。

DoCopy
--> BeginCopyFrom  // Setup to read tuples from a file for COPY FROM.
--> CopyFrom       /* copy from file to database */--> CreateExecutorStatefor (;;){CopyMultiInsertInfoNextFreeSlot-->table_slot_createNextCopyFrom  // Directly store the values/nulls array in the slotExecStoreVirtualTupleExecMaterializeSlotCopyMultiInsertInfoStore /* Add this tuple to the tuple buffer *//* If enough inserts have queued up, then flush all buffers out to their tables. */if (CopyMultiInsertInfoIsFull(&multiInsertInfo))  // 批量插入表中,默认值为1000,#define MAX_BUFFERED_TUPLES 1000CopyMultiInsertInfoFlush(&multiInsertInfo, resultRelInfo);--> CopyMultiInsertBufferFlush--> table_multi_insert // 前面的部分实质都是在准备数据,从文件从取数据批量构造tuple,然后批量插入表中}
--> EndCopyFrom

批量插入的代码后续再进行分析,这里只列出大致的流程。

heap_multi_insert
--> GetCurrentTransactionId
--> RelationNeedsWALfor (i = 0; i < ntuples; i++){heap_prepare_insert}while (ndone < ntuples){buffer = RelationGetBufferForTuple      // 获取表页中含有空闲空间页的buffer(空闲空间要大于插入tuple的size)page = BufferGetPage(buffer);       // buffer中获取指定的页RelationPutHeapTuple     // place tuple at specified page--> PageAddItemExtended  // Add an item to a pageMarkBufferDirty(buffer);if (needwal){XLogBeginInsert();XLogRegisterData((char *) xlrec, tupledata - scratch.data);XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);XLogRegisterBufData(0, tupledata, totaldatalen);/* filtering by origin on a row level is much more efficient */XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);recptr = XLogInsert(RM_HEAP2_ID, info);PageSetLSN(page, recptr);}}

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

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

相关文章

BC153 [NOIP2010]数字统计

数字统计 一.题目描述二.输入描述&#xff1a;三.输出描述&#xff1a;四.数字范围五.题目思路六.代码实现 一.题目描述 请统计某个给定范围[L, R]的所有整数中&#xff0c;数字2出现的次数。 比如给定范围[2, 22]&#xff0c;数字2在数2中出现了1次&#xff0c;在数12中出现1次…

如何恢复iPhone iCloud云盘资料删除?给出建议

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

【Java】已解决com.mysql.cj.jdbc.exceptions.CommunicationsException异常

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决com.mysql.cj.jdbc.exceptions.CommunicationsException异常 一、分析问题背景 com.mysql.cj.jdbc.exceptions.CommunicationsException是Java程序在使用MySQL Connector/J与…

目标检测—Fast RCNN

介绍Fast R-CNN之前先简单回顾一下R-CNN和SPP-net R-CNN&#xff08;Regions with CNN&#xff09; affine image wraping 解析 Bounding Box Regression&#xff08;边界框回归&#xff09; 如何回归&#xff1f; 问题1&#xff1a;为什么要使用相对坐标差&#xff1f; …

全面对比与选择指南:Milvus、PGVector、Zilliz及其他向量数据库

本文全面探讨了Milvus、PGVector、Zilliz等向量数据库的特性、性能、应用场景及选型建议&#xff0c;通过详细的对比分析&#xff0c;帮助开发者和架构师根据具体需求选择最合适的向量数据库解决方案。 文章目录 向量数据库概述向量数据库的关键功能向量数据库的扩展和选择向量…

立创开源学习篇(一)

1.机壳地 外面包围的一圈是机壳地&#xff0c;和金属外壳相连与电路板的GND不相连&#xff1a;&#xff08;大疆很多产品有此设计&#xff09; 屏蔽和接地&#xff1a;通过在电路板周围打孔&#xff0c;并连接到机壳地&#xff0c;可以形成有效的电磁屏蔽层&#xff08;形成金…

004、KMeans和DBSCAN的比较

KMeans 聚类 工作原理 选择K个初始中心点&#xff08;可以随机选择或使用其他方法&#xff09;。迭代过程&#xff1a; 分配每个数据点到最近的中心点&#xff1a;计算每个数据点到所有中心点的距离&#xff0c;将数据点分配到最近的中心点所属的簇。更新中心点&#xff1a;计…

【C语言】回调函数 和 部分库函数的用法以及模拟实现

一、回调函数&#xff1a; 1、定义&#xff1a; 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其所指向的函数时&#xff0c;我们就说这是回调函数。 2、qsort的模拟实现…

怎样打印微信文档文件?

在日常生活和工作中&#xff0c;我们经常需要打印微信中的文档文件&#xff0c;无论是工作资料、学习笔记还是其他重要信息。随着科技的发展&#xff0c;我们不再需要前往打印店进行繁琐的操作&#xff0c;而是可以通过一些便捷的在线打印平台轻松实现。今天&#xff0c;我们就…

微信小程序自定义组件(写一个点击查看大图有关闭按钮,复制即可使用)

今天碰到一个需求&#xff0c;本来使用wx.previewImage用的好好的&#xff0c;非要加一个关闭按钮&#xff0c;这可把不想写代码的我难住了&#xff0c;查看官方文档没有加这个按钮的属性&#xff0c;那就自己写了吧&#xff0c;自己写的途中发现胶囊隐藏不了&#xff0c;兴高采…

echarts学习:通过图例事件实现选中后控制多条折线的显隐

1.问题描述 我在工作中遇到了这样一个需求&#xff1a;我们都知道点击echarts折线图的图例&#xff0c;是可以控制折线的显隐的。我现在希望点击某一个图例可以改变多条折线的显隐。 例如在下面这张图中&#xff0c;我将“xxx水位”和“yyy水位”分为一组&#xff1b;将“xxx…

SGPT论文阅读笔记

这是篇想要用GPT来提取sentence embedding的工作&#xff0c;提出了两个框架&#xff0c;一个是SGPT-BE&#xff0c;一个是SGPT-CE&#xff0c;分别代表了Bi-Encoder setting和Cross-Encoder setting。CE的意思是在做阅读理解任务时&#xff0c;document和query是一起送进去&am…

IPv6改造是什么意思?网站IPv6改造怎么做?

随着互联网技术的飞速发展&#xff0c;IPv4地址资源短缺的问题日益凸显&#xff0c;已经难以满足物联网、人工智能等新基建的激增需求。为了解决这一困境&#xff0c;IPv6协议应运而生&#xff0c;其海量的地址资源和内置的安全机制为互联网的持续发展提供了广阔空间。目前各行…

kbadminv1版后台快速开发框架

探索高效开发的新境界&#xff01;kbadminv1 版后台快速开发框架震撼登场&#xff01; 基于强大的 thinkphp6 框架&#xff0c;kbadminv1 为开发者们带来了前所未有的便捷与高效。 它犹如一把智慧的钥匙&#xff0c;轻松开启后台开发的大门。简洁而直观的设计&#xff0c;让复…

2024-06-18 blue-VH-driver-订阅组态相关分析

摘要: 2024-06-18 blue-VH-driver-订阅组态相关分析 订阅组态相关: 1.1 创建订阅分组成功, 但是激活订阅分组失败 调用VH接口删除已经创建的订阅分组 无论否删除成功,都返回不做重试删除已经创建的订阅分组,避免无法结束调用1.2 向订阅分组添加的位号的数据量超过单个订阅分…

高并发系统中面临的问题 及 解决方案

在互联网软件系统中,高并发读写场景会带来一系列复杂的问题。以下是详细的分析和解决方案: 1. 性能瓶颈 问题: 数据库性能瓶颈:高并发请求会导致数据库负载过重,响应时间增加,甚至可能导致数据库崩溃。服务器性能瓶颈:服务器的CPU、内存、网络带宽可能无法承受高并发请…

Maven 配置学习:存在两个本地私服如何配置

Maven 配置学习&#xff1a;存在两个本地私服如何配置 目录 Maven 配置学习&#xff1a;存在两个本地私服如何配置解释&#xff1a;1.本地仓库位置&#xff1a;2.Profiles 定义&#xff1a;3.Repositories 定义顺序&#xff1a;4.Active Profiles&#xff1a; 操作步骤&#xf…

在Pycharm使用Github Copilot

文章目录 1.GitHub Copilot 是什么2.注册GitHub Copilot3.官方使用文档4.安装 GitHub Copilot插件5.在Pycharm中使用6.相关功能键7.启用或禁用 GitHub Copilot 1.GitHub Copilot 是什么 GitHub Copilot 是一款 AI 编码助手&#xff0c;可帮助你更快、更省力地编写代码&#xff…

【MySQL进阶之路 | 高级篇】SQL执行过程

1. 客户端与服务器的连接 运行中的服务器程序与客户端程序本质上都是计算机的一个进程&#xff0c;所以客户端进程向服务器端进程发送请求并得到相应过程的本质就是一个进程间通信的过程. 我们可以使用TCP/IP网络通信协议&#xff0c;命名管道和共享内存等方式&#xff0c;实…

2024/6/18(RBAC,查询用户权限,细粒度授权,选课,支付宝生成二维码支付,支付结果查询需要内网穿透)

黑马程序员【学成在线项目】,P141 测试沙箱支付宝_黑马学成在线支付宝沙箱-CSDN博客 需要内网穿透