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 (图片来源于网络) 目录 (一)快速排序 类比递归谋划非递归 快速排序的非递归实现: (二)归并排序 归…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...
