PG在还没有pg_class的时候怎么访问基础系统表?
在没有pg_class的时候,数据库怎么访问系统表?这个问题可以分成两个阶段来看:
- 数据库簇初始化,此时一个database都没有,所以怎么构造和访问pg_class等系统表是一个问题
- 私有内存初始化系统表。PG的系统表信息是放在backend本地进程上的,backend在初始化的时候又怎么load pg_class?
初始化数据字典
在数据库还没有初始化的时候,明显是不能通过访问数据字典来初始化database、pg_class等等对象的,因为一个库都没有就不能create database,也没有pg_class去查元数据信息。
PG通过bki文件的特殊语言初始化一些数据结构,然后在bootstrap模式初始化一个原始database1。
编译阶段:genbki.h & genbki.pl
src/include/catalog/genbki.h:
* genbki.h defines CATALOG(), BKI_BOOTSTRAP and related macros* so that the catalog header files can be read by the C compiler.* (These same words are recognized by genbki.pl to build the BKI* bootstrap file from these header files.)
genbki.h内容很少,主要是为了catalog相关操作的宏定义,以及给KBI bootstrap文件的宏定义。数据字典的头文件基本都包含genbki.h
genbki.pl会在编译过程读取/src/include/catalog目录下的.h表定义文件(不含pg_*_d.h),并创建postgres.bki文件和pg_*_d.h头文件。
以pg_class为例:
[postgres@catalog]$ ll |grep pg_class
-rw-r----- 1 postgres postgres 3682 Aug 6 2019 pg_class.dat
lrwxrwxrwx 1 postgres postgres 86 Apr 8 20:31 pg_class_d.h -> /lzl/soft/postgresql-11.5/src/backend/catalog/pg_class_d.h
-rw-r----- 1 postgres postgres 5219 Aug 6 2019 pg_class.h
pg_*_d.h头文件就是genbki.pl生成的。pg_*_d.h文件中都包含下面的一段话:
It has been GENERATED by src/backend/catalog/genbki.pl
每个数据字典都有一个结构体typedef struct FormData_*catalogname*用以存储数据字典的行数据2,例如pg_class的FormData_pg_class:
CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,RelationRelation_Rowtype_Id) BKI_SCHEMA_MACRO
{/* oid */Oid oid;/* class name */NameData relname;/* OID of namespace containing this class */Oid relnamespace BKI_DEFAULT(pg_catalog) BKI_LOOKUP(pg_namespace);/* OID of entry in pg_type for relation's implicit row type, if any */Oid reltype BKI_LOOKUP_OPT(pg_type);/* OID of entry in pg_type for underlying composite type, if any */Oid reloftype BKI_DEFAULT(0) BKI_LOOKUP_OPT(pg_type);/* class owner */Oid relowner BKI_DEFAULT(POSTGRES) BKI_LOOKUP(pg_authid);.../* access-method-specific options */text reloptions[1] BKI_DEFAULT(_null_);/* partition bound node tree */pg_node_tree relpartbound BKI_DEFAULT(_null_);
#endif
} FormData_pg_class;
pg_class的OID写死了1259,所有字段都在FormData_pg_class结构体中。
用户存储数据的结构体初始化后,会使用对应的.dat文件插入基础数据。pg_class中会插入4条数据,可以理解为bootstrap item(pg15中的数据字典表有49个):
{ oid => '1247',relname => 'pg_type', reltype => 'pg_type' },
{ oid => '1249',relname => 'pg_attribute', reltype => 'pg_attribute' },
{ oid => '1255',relname => 'pg_proc', reltype => 'pg_proc' },
{ oid => '1259',relname => 'pg_class', reltype => 'pg_class' },
postgres=# select oid,relname from pg_class where oid::int >=1247 and oid::int<=1259;oid | relname
------+--------------1247 | pg_type1249 | pg_attribute1255 | pg_proc1259 | pg_class
把基础数据字典写入后,其他的都可以依赖这些数据生成。
初始化database阶段:initdb&postgres.bki
initdb.c中的注释:
* To create template1, we run the postgres (backend) program in bootstrap* mode and feed it data from the postgres.bki library file. After this* initial bootstrap phase, some additional stuff is created by normal* SQL commands fed to a standalone backend.
以bootstrap模式启动backend并运行postgres.bki脚本,postgres.bki可以在没有任何系统表的情况下,执行相关函数。此后才可以使用正常的SQL文件和启动标准的backend进程。
template1可以称之为bootstrap database了,postgres、template0两个库是在template1建立以后才创建:
void
initialize_data_directory(void)
{
.../* Bootstrap template1 */bootstrap_template1();
...make_template0(cmdfd);make_postgres(cmdfd);PG_CMD_CLOSE;check_ok();
}
有了template1后,make_template0和make_postgres创建对应的template0 database和postgres database,直接用一般的SQL语句CREATE DATABASE命令创建:
/** copy template1 to postgres*/
static void
make_postgres(FILE *cmdfd)
{const char *const *line;/** Just as we did for template0, and for the same reasons, assign a fixed* OID to postgres and select the file_copy strategy.*/static const char *const postgres_setup[] = {"CREATE DATABASE postgres OID = " CppAsString2(PostgresDbOid)" STRATEGY = file_copy;\n\n","COMMENT ON DATABASE postgres IS 'default administrative connection database';\n\n",NULL};for (line = postgres_setup; *line; line++)PG_CMD_PUTS(*line);
}
backend本地缓存数据字典
PG私有内存的基础知识可参考PostgreSQL内存浅析3。
PG的数据字典信息存放在本地backend进程中,非共享。数据字典缓存主要关注的是syscache/catcache和relcache,他们分别缓存系统表和表模式信息。
其中syscache/catcache是用于缓存系统表的,syscache相当于catcache的上层结构。syscache是一个数组,数字中的每个元素对应一个catcache,每个catcache对应一个系统表1。
//PG15.3 SysCacheSize=35
static CatCache *SysCache[SysCacheSize];
pg在fork backend的时候调用的是InitPostgres,其中会调用syscache/catcache和relcache的初始化函数。下面来看看backend的初始化。
syscache/catcache初始化
struct cachedesc
{Oid reloid; /* OID of the relation being cached */Oid indoid; /* OID of index relation for this cache */int nkeys; /* # of keys needed for cache lookup */int key[4]; /* attribute numbers of key attrs */int nbuckets; /* number of hash buckets for this cache */
};static const struct cachedesc cacheinfo[] = {{
... {RelationRelationId, /* RELNAMENSP */ClassNameNspIndexId,2,{Anum_pg_class_relname,Anum_pg_class_relnamespace,0,0},128},{RelationRelationId, /* RELOID */ClassOidIndexId,1,{Anum_pg_class_oid,0,0,0},128
...
};
例如pg_class,由genbki.pl生成的pg_class_d.h中定义Anum_pg_class_oid
#define Anum_pg_class_oid 1
reloid就是oid
select oid,relname from pg_class where oid::int >=1247 and oid::int<=1259;oid | relname
------+--------------1259 | pg_class
InitCatalogCache其实是初始化syscache数组,也就是初始化所有的catcache。InitCatalogCache最终通过InitCatCache全量初始化CatCache(这里其中一个就有pg_class的):
void
InitCatalogCache(void)
{
...for (cacheId = 0; cacheId < SysCacheSize; cacheId++){SysCache[cacheId] = InitCatCache(cacheId,cacheinfo[cacheId].reloid,cacheinfo[cacheId].indoid,cacheinfo[cacheId].nkeys,cacheinfo[cacheId].key,cacheinfo[cacheId].nbuckets);if (!PointerIsValid(SysCache[cacheId]))elog(ERROR, "could not initialize cache %u (%d)",cacheinfo[cacheId].reloid, cacheId);/* Accumulate data for OID lists, too */SysCacheRelationOid[SysCacheRelationOidSize++] =cacheinfo[cacheId].reloid;SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =cacheinfo[cacheId].reloid;SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =cacheinfo[cacheId].indoid;/* see comments for RelationInvalidatesSnapshotsOnly */Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));}
...CacheInitialized = true;
}
然后来到catcache.c。
InitCatCache会开辟内存,并且放到CacheMemoryContext中管理。它也只是把宏定义的一些oid赋值给对应的catcache,此时还没有open表:
/** InitCatCache** This allocates and initializes a cache for a system catalog relation.* Actually, the cache is only partially initialized to avoid opening the* relation. The relation will be opened and the rest of the cache* structure initialized on the first access.*/
CatCache *
InitCatCache(int id,Oid reloid,Oid indexoid,int nkeys,const int *key,int nbuckets)
{
...oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
...sz = sizeof(CatCache) + PG_CACHE_LINE_SIZE;cp = (CatCache *) CACHELINEALIGN(palloc0(sz));cp->cc_bucket = palloc0(nbuckets * sizeof(dlist_head));/** initialize the cache's relation information for the relation* corresponding to this cache, and initialize some of the new cache's* other internal fields. But don't open the relation yet.*/cp->id = id;cp->cc_relname = "(not known yet)";cp->cc_reloid = reloid;cp->cc_indexoid = indexoid;cp->cc_relisshared = false; /* temporary */cp->cc_tupdesc = (TupleDesc) NULL;cp->cc_ntup = 0;cp->cc_nbuckets = nbuckets;cp->cc_nkeys = nkeys;for (i = 0; i < nkeys; ++i)cp->cc_keyno[i] = key[i];
...MemoryContextSwitchTo(oldcxt);return cp;
}
id是catcache数组元素的编号,赋值的reloid是已知的cacheinfo中的oid,也赋值了cacheinfo中的key[4],其他信息基本都还不知道,例如relname、tupdesc,因为到这里系统表还没有open。
catcache只有在search的时候才有open的操作,虽然函数名字类似*init*,不过已经不在初始化的过程中了,相关函数不再这里展示。
syscache/catcache初始化完成后,实际上是没有任何元组信息的。
relcache初始化
relcache初始化这篇PostgreSQL内存浅析已经讲的比较好了。
relcache初始化由5个阶段:
- RelationCacheInitialize - 初始化relcache,初始化为空的
- RelationCacheInitializePhase2 - 初始化共享的catalog,并加载5个global系统表
- RelationCacheInitializePhase3 - 完成初始化relcache,并加载4个基础系统表
- RelationIdGetRelation - 通过relation id获得rel描述
- RelationClose - 关闭一个relation
其中RelationCacheInitializePhase2 RelationCacheInitializePhase3 都有load系统表,他们有先后顺序的必要。
RelationCacheInitializePhase2有兴趣的可以自行查看函数,也load几个系统表;RelationCacheInitializePhase3 是与我们的问题相关的,我们看这个:
/** RelationCacheInitializePhase3** This is called as soon as the catcache and transaction system* are functional and we have determined MyDatabaseId. At this point* we can actually read data from the database's system catalogs.* We first try to read pre-computed relcache entries from the local* relcache init file. If that's missing or broken, make phony entries* for the minimum set of nailed-in-cache relations. Then (unless* bootstrapping) make sure we have entries for the critical system* indexes. Once we've done all this, we have enough infrastructure to* open any system catalog or use any catcache. The last step is to* rewrite the cache files if needed.*/
void
RelationCacheInitializePhase3(void)
{
...if (IsBootstrapProcessingMode() ||!load_relcache_init_file(false)){needNewCacheFile = true;formrdesc("pg_class", RelationRelation_Rowtype_Id, false,Natts_pg_class, Desc_pg_class);formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false,Natts_pg_attribute, Desc_pg_attribute);formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false,Natts_pg_proc, Desc_pg_proc);formrdesc("pg_type", TypeRelation_Rowtype_Id, false,Natts_pg_type, Desc_pg_type);#define NUM_CRITICAL_LOCAL_RELS 4 /* fix if you change list above */}MemoryContextSwitchTo(oldcxt);/* In bootstrap mode, the faked-up formrdesc info is all we'll have */if (IsBootstrapProcessingMode())return;.../* now write the files */write_relcache_init_file(true);write_relcache_init_file(false);}
}
IsBootstrapProcessingMode其实是专门为bootstrap模式定制的判断,一般的backend是不满足这个条件的。
load_relcache_init_file(false)尝试从initfile中加载系统表信息,load_relcache_init_file(false)传入的是false表示是私有initfile,不是共享initfile:
[postgres@16384]$ pwd
/pgdata/lzl/data15_6879/base/16384
--粗糙一点看。strings会忽略一部分信息,但是表和列名可以看到
[postgres@16384]$ strings pg_internal.init |grep pg_class
pg_class_oid_index
pg_class
pg_class_relname_nsp_index
[postgres@16384]$ strings pg_internal.init |grep -E "pg_class|relname"
pg_class_oid_index
pg_class
relname
relnamespace
pg_class_relname_nsp_index
relname
relnamespace
如果initfile损坏或者没有,那么加载initfile失败进入判断,去load 4个基础系统表:
//跟2阶段差不多,加载更多的系统表描述if (IsBootstrapProcessingMode() ||!load_relcache_init_file(false)){needNewCacheFile = true;formrdesc("pg_class", RelationRelation_Rowtype_Id, false,Natts_pg_class, Desc_pg_class);formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false,Natts_pg_attribute, Desc_pg_attribute);formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false,Natts_pg_proc, Desc_pg_proc);formrdesc("pg_type", TypeRelation_Rowtype_Id, false,Natts_pg_type, Desc_pg_type);
有了pg_class 4个基础表,后面加载系统表信息一切都很简单了
References
《PostgreSQL内核分析》第2,3章 ↩︎ ↩︎
https://www.postgresql.org/docs/current/system-catalog-declarations.html ↩︎
PostgreSQL内存浅析 ↩︎
相关文章:
PG在还没有pg_class的时候怎么访问基础系统表?
在没有pg_class的时候,数据库怎么访问系统表?这个问题可以分成两个阶段来看: 数据库簇初始化,此时一个database都没有,所以怎么构造和访问pg_class等系统表是一个问题私有内存初始化系统表。PG的系统表信息是放在back…...
UnityHub 无法添加模块问题
文章目录 1.问题描述2.问题解决 1.问题描述 在Hub中无法添加模块 2.问题解决 1、点击设置 2、设置版本安装位置 可以发现installs的安装位置路径设置不是unity安装位置,这里我们更改成自己电脑unity安装位置的上一级路径 添加模块正常:...
python04——类(基础new)
类其实也是一种封装的思想,类就是把变量、方法等封装在一起,然后可以通过不同的实例化对其进行调用操作。 1.类的定义 class 类名: 变量a def __init__ (self,参数2,参数2...):初始化函数!!&…...
【Python百日进阶-Web开发-Peewee】Day296 - 查询示例(五)聚合2、递归
文章目录 14.6.13 列出每个指定设施的预订总小时数 List the total hours booked per named facility14.6.14 列出每位会员在 2012 年 9 月 1 日之后的首次预订 List each member’s first booking after September 1st 201214.6.15 生成成员名称列表,每行包含成员总数 Produc…...
闲话银行家舍入法,以及在程序中如何实现
前言 相信对于四舍五入的舍入法,大家都耳熟能详,但对于银行家舍入法,可能就会比较少接触了! 可是在金融界,银行家舍入法可是大名鼎鼎的主角之一,主要应用于金融领域和涉及货币计算的场合。 那么…...
最短路径算法(算法篇)
算法之最短路径算法 最短路径算法 概念: 考查最短路径问题,可能会输入一个赋权图(也就是边带有权的图),则一条路径的v1v2…vN的值就是对路径的边的权求和,这叫做赋权路径长,如果是无权路径长就是单纯的路径上的边数。…...
昇思25天学习打卡营第11天 | LLM原理和实践:基于MindSpore实现BERT对话情绪识别
1. 基于MindSpore实现BERT对话情绪识别 1.1 环境配置 # 实验环境已经预装了mindspore2.2.14,如需更换mindspore版本,可更改下面mindspore的版本号 !pip uninstall mindspore -y !pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore2.2…...
反向散射技术(backscatter communication)
智能反射表面辅助的反向散射通信系统研究综述(知网) 1 反向散射通信技术优势和应用场景 反向散射通信技术通过被动射频技术发送信号,不需要一定配有主动射频单元,被认为是构建绿色节能、低成本、可灵活部署的未来物联网规模化应用关键技术之一,是实现“…...
致远CopyFile文件复制漏洞
复现版本 V8.0SP2 漏洞范围 V5&G6_V6.1至V8.0SP2全系列版本、V5&G6&N_V8.1至V8.1SP2全系列版本。 漏洞复现 上传文件 POST /seeyon/ajax.do?methodajaxAction&managerNameportalCssManager&rnd57507 HTTP/1.1 Accept: */* Content-Type: applicatio…...
MySQL 创建数据库
MySQL 创建数据库 在当今的数据驱动世界中,数据库是任何应用程序的核心组成部分。MySQL,作为一个流行的开源关系数据库管理系统,因其可靠性、易用性和强大的功能而广受欢迎。本文将详细介绍如何在MySQL中创建数据库,包括基础知识和最佳实践。 什么是MySQL数据库? MySQL…...
AbyssFish单连通周期边界多孔结构2D软件
软件介绍 AbyssFish单连通周期边界多孔结构2D软件(以下简称软件)可用于生成具备周期性边界条件的单连通域多孔结构PNG图片,软件可设置生成模型的尺寸、孔隙率、孔隙尺寸、孔喉尺寸等参数,并且具备孔隙形态控制功能。 软件生成的…...
Linux驱动开发-03字符设备驱动框架搭建
一、字符设备驱动开发步骤 驱动模块的加载和卸载(将驱动编译模块,insmod加载驱动运行)字符设备注册与注销(我们的驱动实际上是去操作底层的硬件,所以需要向系统注册一个设备,告诉Linux系统,我有…...
Zynq系列FPGA实现SDI视频编解码+图像缩放+多路视频拼接,基于GTX高速接口,提供8套工程源码和技术支持
目录 1、前言工程概述免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本博已有的FPGA图像缩放方案本方案的无缩放应用本方案在Xilinx--Kintex系列FPGA上的应用 3、详细设计方案设计原理框图SDI 输入设备Gv8601a 均衡器GTX 解串与串化SMPTE SD/HD/3G SDI IP核BT1120转RGB自研…...
VS2019使用C#写窗体程序技巧(1)
1、打开串口 private void button1_Click(object sender, EventArgs e){myPort cmb1.Text;mybaud Convert.ToInt32(cmb2.Text, 10);databit 8;parity Parity.None;stopBit StopBits.One;textBox9.Text "2";try{sp new SerialPort(myPort, mybaud, parity, dat…...
Python爬虫-requests模块
前戏: 1.你是否在夜深人静的时候,想看一些会让你更睡不着的图片却苦于没有资源... 2.你是否在节假日出行高峰的时候,想快速抢购火车票成功..。 3.你是否在网上购物的时候,想快速且精准的定位到口碑质量最好的商品. …...
适用于PyTorch 2.0.0的Ubuntu 22.04上CUDA v11.8和cuDNN 8.7安装指南
将下面内容保存为install.bash,直接用bash执行一把梭解决 #!/bin/bash### steps #### # verify the system has a cuda-capable gpu # download and install the nvidia cuda toolkit and cudnn # setup environmental variables # verify the installation ######…...
使用conda安装openturns
目录 1. 有效方法2. 整体分析使用pip安装使用conda安装验证安装安装过程中可能遇到的问题 1. 有效方法 conda install -c conda-forge openturns2. 整体分析 OpenTURNS是一个用于概率和统计分析的软件库,主要用于不确定性量化。你可以通过以下步骤在Python环境中安…...
Chameleon:动态UI框架使用详解
文章目录 引言Chameleon框架原理核心概念工作流程 基础使用安装与配置创建基础界面 高级使用自定义组件响应式布局数据流与状态管理 结论 引言 Chameleon,作为一种动态UI框架,旨在通过灵活、高效的方式帮助开发者构建跨平台、响应用户交互的图形用户界面…...
7.10飞书一面面经
问题描述 Redis为什么快? 这个问题我遇到过,但是没有好好总结,导致答得很乱。 答:Redis基于内存操作: 传统的磁盘文件操作相比减少了IO,提高了操作的速度。 Redis高效的数据结构:Redis专门设计…...
[数据结构] 归并排序快速排序 及非递归实现
()标题:[数据结构] 归并排序&&快速排序 及非递归实现 水墨不写bug (图片来源于网络) 目录 (一)快速排序 类比递归谋划非递归 快速排序的非递归实现: (二)归并排序 归…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
