循序渐进丨MogDB 中 gs_dump 数据库导出工具源码概览

背景

gs_dump 是 MogDB 中一个功能丰富灵活的数据库导出工具,在数据库的维护、迁移和开发中经常使用。该工具允许用户根据需要导出整个数据库或者数据库中的特定对象,如模式(schema)、表(tables)、视图(views)等。

近期有客户咨询gs_dump如何保证数据一致性?备份期间对数据库和表如何加锁?为了解答这个问题,顺便了解一下gs_dump的实现过程,本文对该工具的源码进行了一番解读。

由于无法查看到 MogDB 的gs_dump工具的源码,所以源码使用了gitee上用openGauss 5.0版本的gs_dump代码作为参考。

脚本路径:src/bin/pg_dump/

gs_dump的功能主要在pg_dump.cpp脚本中,同目录下还有pg_backup、pg_dumpall、pg_restore等功能脚本。从脚本名字上来看,这个脚本是继承自postgres。

下面通过分析pg_dump.cpp的main函数,从源码层面解读gs_dump 导出操作时的工作流程。

初始化和设置

  • 包含必要的头文件并设置环境相关的配置。

  • 定义各种宏和常量以进行操作。

  • 声明全局变量和结构体用于处理用户输入、数据库连接和转储选项。

命令行参数解析

在getopt_dump函数中使用 getopt_long 来解析命令行参数,并根据解析结果设置相应的全局变量,存储各种选项,如压缩级别、输出格式、用户名等

图片

在getopt_dump 函数中,解析命令行的入参:

static void getopt_dump(int argc, char** argv, struct option options[], int* result)
{int opt;while ((opt = getopt_long(argc, argv, "abcCE:f:F:g:h:n:N:oOp:q:RsS:t:T:U:vwW:xZ:", options, NULL)) != -1){switch (opt){case 'h':// 显示帮助信息help(argv[0]);exit(0);case 'F':// 设置格式format = optarg;break;case 'p':// 设置端口port = optarg;break;// 其他选项处理}}
}

创建输出文件,注册清理机制

图片

createArchive 函数接受了从命令行参数中解析出的输出路径,压缩等级等参数,创建输出文件。

连接数据库

ConnectDatabase函数使用给定的参数建立数据库连接,如果连接报错就清理输出文件;如果正常,则在fout对象中设置仅当前会话生效的参数。

errorMessages = ConnectDatabase(fout, dbname, pghost, pgport, username, prompt_password, false);if (errorMessages != NULL) {(void)remove(filename);GS_FREE(filename);exit_horribly(NULL, "connection to database \"%s\" failed: %s ",((dbname != NULL) ? dbname : ""), errorMessages);}if (CheckIfStandby(fout)) {(void)remove(filename);exit_horribly(NULL, "%s is not supported on standby or cascade standby\n", progname);}if (schemaOnly) {ExecuteSqlStatement(fout, "set enable_hashjoin=off");ExecuteSqlStatement(fout, "set enable_mergejoin=off");ExecuteSqlStatement(fout, "set enable_indexscan=true");ExecuteSqlStatement(fout, "set enable_nestloop=true");}/* Turn off log collection parameters to improve execution performance */ExecuteSqlStatement(fout, "set resource_track_level='none'");find_current_connection_node_type(fout);// Get the database which will be dumpedif (NULL == dbname) {ArchiveHandle* AH = (ArchiveHandle*)fout;dbname = gs_strdup(PQdb(AH->connection));}if (NULL == instport) {ArchiveHandle* AH = (ArchiveHandle*)fout;instport = gs_strdup(PQport(AH->connection));}if ((use_role != NULL) && (rolepasswd == NULL)) {get_role_password();}setup_connection(fout);

再使用setup_connection 函数建立与数据库的连接,后面将会在此连接中执行导出。

static void setup_connection(Archive* AH)
{// 建立数据库连接PGconn *conn = PQsetdbLogin(pghost, pgport, NULL, NULL, dbname, username, password);if (PQstatus(conn) != CONNECTION_OK){fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn));exit(1);}AH->connection = conn;
}

开启事务

图片

fout->remoteVersion >= 90100表示pg的版本大于9.1。

gs_dump 参数 --serializable-deferrable 可以控制导出事务的隔离级别。默认gs_dump事务的隔离级别为REPEATABLE READ,如果设置了serializable-deferrable参数,事务的隔离级别为SERIALIZABLE,但在Mogdb中,SERIALIZABLE等价于REPEATABLE READ,所以gs_dump事务的隔离级别始终REPEATABLE READ。DEFERRABLE允许一个只读串行事务延迟执行,即如果在导出之前,有读写事务处于活动状态,转储的开始时间可能会延迟等待读写事务结束,这样能保证使用的快照与之后的数据库状态一致。

图片

添加导出schema和表

这部分代码使用expand_schema_name_patterns和expand_table_name_patterns 将要导出的schema和table 的oid保存在列表中。

图片

添加schema时,schema_include_patterns参数来自于命令行参数-n,在expand_schema_name_patterns函数中,通过简单的查询pg_namespace视图,将schema的oid 添加到列表中。

图片

static void expand_schema_name_patterns(...if (patterns->head == NULL) {return; /* nothing to do */}query = createPQExpBuffer();for (cell = patterns->head; cell != NULL; cell = cell->next) {appendPQExpBuffer(query, "SELECT oid FROM pg_catalog.pg_namespace n\n");...}destroyPQExpBuffer(query);}

而添加表时table_include_patterns和table_exclude_patterns分别来自命令行参数-t 和-T,通过查询pg_class 和 pg_namespace,将oid 添加分别添加到导出类别和排除列表中。

图片

static void expand_table_name_patterns(Archive* fout, SimpleStringList* patterns, SimpleOidList* oidlists, bool isinclude){...query = createPQExpBuffer();for (cell = patterns->head; cell != NULL; cell = cell->next) {appendPQExpBuffer(query,"SELECT c.oid""\nFROM pg_catalog.pg_class c""\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace""\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c','%c', '%c', '%c')\n",RELKIND_RELATION,RELKIND_SEQUENCE,RELKIND_LARGE_SEQUENCE,RELKIND_VIEW,RELKIND_MATVIEW,RELKIND_CONTQUERY,RELKIND_FOREIGN_TABLE,RELKIND_STREAM);...}destroyPQExpBuffer(query);}

获取导出数据对象

扫描数据库并为要导出的所有对象创建DumpableObject结构。

 tblinfo = getSchemaData(fout, &numTables);if (fout->remoteVersion < 80400)guessConstraintInheritance(tblinfo, numTables);if (!schemaOnly) {getTableData(tblinfo, numTables);if (dataOnly)getTableDataFKConstraints();}if (outputBlobs)getBlobs(fout);

获取依赖和排序

根据每个对象的依赖关系,调用getDependencies函数,整理对象间的依赖关系,调用sortDumpableObjects来决定各个数据库对象导出的顺序。

//获取数据库对象的依赖关系。依赖关系用于确保在导出数据库对象时,它们之间的顺序是正确的。
getDependencies(fout);boundaryObjs = createBoundaryObjects();//获取所有已知的数据库对象。
getDumpableObjects(&dobjs, &numObjs);addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
...sortDumpableObjects(dobjs, numObjs, boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);

执行导出

  for (i = 0; i < numObjs; i++) {if (!dataOnly && dobjs[i]->dump && dobjs[i]->objType != DO_DUMMY_TYPE &&dobjs[i]->objType != DO_PRE_DATA_BOUNDARY && dobjs[i]->objType != DO_POST_DATA_BOUNDARY) {dumpObjNums++;if (dumpObjNums % OUTPUT_OBJECT_NUM == 0)write_msg(NULL,"[%6.2lf%%] %d objects have been dumped.\n",float((float(dumpObjNums * 100.0)) / totalObjNums),dumpObjNums);}dumpDumpableObject(fout, dobjs[i]);}

这里主要调用了dumpDumpableObject函数,本函数根据要导出的对象类型,调用不同的导出函数进行处理。主要逻辑如下

static void dumpDumpableObject(Archive *fout, DumpableObject *dobj)
{// 根据对象的类型进行处理switch (dobj->objType){case DO_TABLE:dumpTable(fout, (TableInfo *) dobj);break;case DO_INDEX:dumpIndex(fout, (IndxInfo *) dobj);break;case DO_VIEW:dumpView(fout, (ViewInfo *) dobj);break;case DO_TRIGGER:dumpTrigger(fout, (TriggerInfo *) dobj);break;// 其他对象类型// ...    }}

以导出表为例,

dumpTable 函数只导出表定义,调用了dumpTableSchema生成表或视图的声明

dumpTableData函数导出表数据,根据命令行参数--inserts,选择使用copy命令导出还是导出成insert 语句

static void dumpTableData(Archive* fout, TableDataInfo* tdinfo)
{
...if (!dump_inserts) {/* Dump/restore using COPY */dumpFn = dumpTableData_copy;} else {/* Restore using INSERT */dumpFn = dumpTableData_insert;}
...

在执行COPY 命令时,会对表上加AssessShareLock锁,此锁能和除了ExclusiveLock以外的锁共存,也就是在执行copy命令不影响对表的增删改操作,只和DDL操作冲突。

testdb=# SELECT pl.pid, pl.virtualtransaction AS vxid, pl.locktype AS lock_type,pl.mode AS lock_mode, pl.granted,
CASE
WHEN pl.virtualxid IS NOT NULL AND pl.transactionid IS NOT NULL THEN pl.virtualxid || ' ' || pl.transactionid
WHEN pl.virtualxid::text IS NOT NULL
THEN pl.virtualxid
ELSE
pl.transactionid::text
END AS xid_lock, pc.relname,
pl.page, pl.tuple, pl.classid, pl.objid, pl.objsubid,ps.usename,ps.application_name,ps.query
FROM pg_locks pl LEFT OUTER JOIN pg_class pc ON (pl.relation = pc.oid) left join pg_stat_activity ps on ps.pid = pl.pid
WHERE  pl.pid != pg_backend_pid();pid       |   vxid   | lock_type  |    lock_mode    | granted | xid_lock | relname | page | tuple | classid | objid | objsubid | usename | application_name|                 query
----------------+----------+------------+-----------------+---------+----------+---------+------+-------+---------+-------+----------+---------+------------------+----------------------------------------47858921375488 | 17/15218 | relation   | AccessShareLock | t       |          | cct     |      |       |         |       |          | test    | gsql| copy cct  to '/home/test/city.bat';

导出资源

图片

调用RestoreArchive函数,真正的导出数据。RestoreArchive函数在pg_backup_archiver.cpp文件中。

释放资源

导出完成后,释放资源,退出导出任务:

图片

以上通过对opengauss 5.0源码中pg_dump.cpp mian函数的解读,可以了解到在执行gs_dump 操作导出数据库对象时具体做了哪些工作。

关于作者

陈坤,云和恩墨技术顾问,具有银行、保险、交易所等金融行业数据库运维经验,擅长数据库自动化运维工具开发。

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

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

相关文章

grafana version 11.1.0 设置Y轴刻度为1

grafana 版本 # /usr/share/grafana/bin/grafana --version grafana version 11.1.0设置轴 Axis 搜索 Standard options 在"Decimals"中输入0&#xff0c;确保只显示整数

Java基础12-特殊文件和日志技术

十二、特殊文件和日志技术 1、特殊文件 properties&#xff1a;用来存储键值对数据。 xml&#xff1a;用来存储有关系的数据。 1.1 properties文件 特点&#xff1a;存储键值对&#xff0c;键不能重复&#xff0c;文件后缀一般是.properties结尾的。 properties&#xff1a;是…

自定义注解和组件扫描在Spring Boot中动态注册Bean(一)

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 在Spring Boot中&#xff0c;自定义注解和组件扫描是两种强大的机制&#xff0c;它们允许开发者以声明性的方式动态注册Bean。这种方式不仅提高了代码的可读性和可维护性&#xff0c;还使得Spring Boot应用的…

UPDATE 更新数据

1.更新某一列字段的值 以 teacher 表为例&#xff0c;需要把前 3 条数据的 age 更新为 33&#xff0c;命令如下: UPDATE teacher SET age18,id_number44444444440604099X WHERE id 30;执行结果如下图 : 这里是对 age 字段列的前三条值进行数据的更新&#xff0c;注意更新…

网络安全有关法律法规

1. 前言 在当今数字化高速发展的时代&#xff0c;网络安全已成为关乎国家、企业和个人的重要议题。为了应对日益复杂的网络安全挑战&#xff0c;一系列网络安全法律法规应运而生&#xff0c;它们如同坚实的盾牌&#xff0c;守护着我们的数字世界。现在是2024年10月&#xff0c…

EM算法学习

1.EM算法的介绍 可以发现&#xff1a;计算出θA和θB的值的前提是知道A、B币种的抛掷情况。 所以我们需要使用EM算法&#xff1a;求出每轮选择硬币种类的概率 2.EM算法执行过程&#xff1a; 第一步&#xff1a;首先初始化设置一组PA和PB证明的值。然后通过最大似然估计得到每…

MOE论文详解(3)-Switch Transformers

Switch Transformers也是google在2022年发表的一篇论文, 该论文简化了MoE的路由算法, 减少了计算量和通信量; 第一次支持bfloat16精度进行训练. 基于T5-Base和T5-Large设计的模型在相同的算力下训练速度提升了7x倍; 同时发布了1.6万亿(1.6 trillion)参数的MoE模型&#xff0c;相…

Linux系统:Ubuntu上安装Chrome浏览器

Ubuntu系统版本&#xff1a;23.04 在Ubuntu系统上安装Google Chrome浏览器&#xff0c;可以通过以下步骤进行&#xff1a; 终端输入以下命令&#xff0c;先更新软件源&#xff1a; sudo apt update 或 sudo apt upgrade终端输入以下命令&#xff0c;下载最新的Google Chrome .…

多机编队—(3)Fast_planner无人机模型替换为Turtlebot3模型实现无地图的轨迹规划

文章目录 前言一、模型替换二、Riz可视化三、坐标变换四、轨迹规划最后 前言 前段时间已经成功将Fast_planner配置到ubuntu机器人中&#xff0c;这段时间将Fast_planner中的无人机模型替换为了Turtlebot3_waffle模型&#xff0c;机器人识别到环境中的三维障碍物信息&#xff0…

5 首页框架及路由配置

1 添加首页LayoutVue.vue组件&#xff0c;登录成功之后跳转到该组件 <script setup> import {Management,Promotion,UserFilled,User,Crop,EditPen,SwitchButton,CaretBottom } from element-plus/icons-vue import avatar from /assets/default.png// 导入ref import {…

GitLab 老旧版本如何升级?

极狐GitLab 正式对外推出 GitLab 专业升级服务 https://dl.gitlab.cn/cm33bsfv&#xff01; 专业的技术人员为您的 GitLab 老旧版本实例进行专业升级&#xff01;服务详情可以在官网查看详细解读&#xff01; 那些因为老旧版本而被攻击的例子 话不多说&#xff0c;直接上图&a…

RTMP、FFmpeg安装测试

RTMP、FFmpeg安装测试 1.使用 Docker 部署 RTMP 服务1.拉取带有 RTMP 模块的 NGINX 镜像&#xff1a;2.运行容器 2. 防火墙放行3.windows安装ffmpeg1. [下载链接](https://ffmpeg.org//download.html)2.解压3.环境变量配置 4.常用的 FFmpeg 推流和拉流命令1.推流命令1. 推流到 …

SQL实现给表添加数据及其触发器操作

新建一个表实现添加数据&#xff0c;数据不重复&#xff0c;。判断两个字段是否存在&#xff0c;如果存在&#xff0c;就修改对应字段&#xff0c;如果不存在就新增数据。 测试表格Test如下&#xff1a; 新建触发器如图&#xff1a; 触发程式如下&#xff1a; USE [Test] GO/*…

C语言_字符串+内存函数的介绍

字符函数和字符串函数 本章重点 重点介绍处理字符和字符串的库函数的使用和注意事项 求字符串长度 strlen 长度不受限制的字符串函数 strcpy strcat strcmp 字符串查找 strstr strtok 错误信息报告 strerror 字符操作内存操作函数 memcpy memmove memset memcmp 1. 字…

【含文档】基于Springboot+Vue的白云山景点门票销售管理系统(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定…

Mysql(七) --- 索引

文章目录 前言1.简介1.1.索引是什么&#xff1f;1.2.为什么使用索引? 2.索引应该使用什么数据结构&#xff1f;2.1.Hash2.2.二叉搜索树2.3.N叉树2.4.B树2.4.1. 简介2.4.2. B树的特点2.4.3. B树和B树的对比 3.Mysql中的页3.1.为什么要使用页3.2.页文件头和页文件尾3.3.页主体3.…

小新学习Docker之Docker--harbor私有仓库部署与管理

目录 一、Harbor简介 1.1、Harbor概述 1.2、Harbor的特性 1.3、Harbor的构成 二、Harbor构建Docker私有仓库 2.1、部署Harbor服务 2.2、启动 Harbor 2.3、查看 Harbor 启动镜像&#xff0c;检查harbor是否安装成功 2.4、创建一个新项目 2.5、非本地主机进行下载镜像 …

STM32学习--4-1 OLED显示屏

接线图 OLED.c #include "stm32f10x.h" #include "OLED_Font.h"/*引脚配置*/ #define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x)) #define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))/*引脚初始化*/ void …

软件安全漏洞挖掘: 基础知识和概念

1. 软件漏洞原理和漏洞检测方法 文章目录 1. 软件漏洞原理和漏洞检测方法1. 漏洞披露2. 漏洞定义和分类1. 漏洞的定义2. 漏洞的分类3. 漏洞检测方法常见方法1. 程序切片2. 形式化方法1. 符号执行3. 污点分析污点分析步骤/流程*污点分析流程的详细介绍1. 识别source和sink点2. 污…

Pycharm通过ssh远程docker容器搭建开发环境

本地pycharm通过ssh方式远程连接服务器&#xff08;Ubuntu&#xff09;上的docker容器&#xff0c;搭建开发环境。实现功能&#xff1a;将环境依赖打包成docker镜像&#xff0c;并在远程服务器中启动容器&#xff08;启动时做好端口映射和文件夹挂载&#xff09;&#xff0c;通…