简介:面向计算机科学与技术、软件工程专业学生的《数据库系统原理实验》课程报告文档,围绕 openGauss 平台组织九个实验,适合正在修读数据库课程、需要完成实验报告或复习数据库操作的学习者参考。内容从认识 DBMS 系统、安装与连接数据库起步,依次覆盖创建数据库表与索引、交互式 SQL 查询、视图创建、完整性控制、安全性控制、事务与并发控制、备份与恢复,以及使用 JDBC 连接数据库,并记录了实验目的、实验内容、遇到的问题与结果分析。压缩包共 1 个 docx 文件,约 3.23MB,排版完整,含目录、实验截图与表格说明,可直接对照课程要求梳理实验流程。已有 1525 人学习,适合用作实验预习、报告撰写模板和操作步骤复盘。
1. 从一份实验报告翻车说起:openGauss 装不起来,九个实验全成空壳
数据库实验课最容易翻车的地方,从来不是 SQL 写不出来,而是环境根本没起来。我见过不止一个同学,报告书写得工工整整,实验一到实验九的表格全填满,结果老师随手一句「你把gsql连上来我看看」,登录主节点、切omm用户、gs_om -t start一路下去,服务起不来,端口 26000 没有任何监听,前面那些截图全成了摆设。这份《数据库系统原理实验》报告书覆盖九个实验,从认识 DBMS、建库建表建索引,到交互式 SQL、视图、完整性控制、安全性控制、事务与并发、备份恢复,最后落到 JDBC 连接数据库——九个实验其实共用同一套 openGauss 环境,环境这一步塌了,后面全是空壳。这篇文章不讲理论,只讲这套环境怎么从零装到能跑通九个实验,适合第一次在 openEuler 虚拟机上装 openGauss、被gs_preinstall和cluster_config.xml卡住的人。
2. 装 openGauss 前先把底子打好:openEuler 虚拟机与三种安装模式的取舍
动手之前得先想清楚一件事:你到底是「要一个能连的数据库」,还是「要一个能反复折腾、装坏了能自己救回来的实验环境」。这两件事的装法差别很大,选错了后面每次实验都要重来一遍。
2.1 三种安装模式:云主机、一键脚本、openEuler 自装怎么选
课程材料里给了三条路:在云主机上装、用虚拟机一键安装、以及在 openEuler 上装好系统后再装 openGauss。三条路都能跑通实验,但适合的人不一样。
云主机安装最省事,开一台实例,按控制台向导点几下就有一个可连的库,适合只想验证 SQL 语法、不关心底层的人。缺点是它是个黑匣子,安装路径、端口、用户都是别人定好的,gs_preinstall到底改了什么你完全看不见,一旦实验七要调事务参数、实验八要做备份恢复,你会发现没有权限改,也没有权限重启服务。
虚拟机一键安装介于两者之间,镜像里把 openGauss 和依赖都打进去了,开机即用。听着很美,实际上是最容易让人产生错觉的一种:能用,但你不清楚它是怎么装上的,等哪天服务起不来,你连日志在哪都不知道,只能整机重装。我一般把它当成「临时救急」用,不当成正式实验环境。
真正推荐的是第三种:先装一个干净的 openEuler,再按官方脚本一步步装 openGauss。慢,但每一步都在你眼皮底下,omm用户、数据目录、端口、配置文件全是自己写的,出了问题知道去哪儿翻日志。这份报告书里实验一到实验九用的是同一个实例,实验八的备份恢复、实验九的 JDBC 连接都要求你能控制服务启停和端口,所以这条路的收益在后期会越来越明显。
| 安装模式 | 耗时 | 可控性 | 适合场景 | 主要风险 |
|---|---|---|---|---|
| 云主机安装 | 10 分钟 | 低 | 只想验证 SQL | 无 root、无法调参、无法备份 |
| 虚拟机一键安装 | 5 分钟 | 中 | 临时演示 | 不清楚安装细节,坏了只能重装 |
| openEuler 自装 | 1~2 小时 | 高 | 完整做完九个实验 | 参数和依赖要自己一项项对 |
提示:三种模式不要混用。有人先用一键镜像起了一套,后面又想按文档手动装一遍,结果两套服务抢同一个 26000 端口,
gs_om -t start一直报错,排查半天才发现是端口被上一套环境占着。
2.2 虚拟机规格与系统参数:内存、磁盘、字符集、防火墙
规格这件事上,网上很多说法偏乐观。我的血泪经验是:2 核 4G 是能跑起来的底线,但只要你想同时开 gsql 会话、开一个 Java 程序连 JDBC、再留点内存给编译器,4G 会非常紧张,gs_om -t start卡在 starting 半天不动的场景我遇到太多次了。建议直接给 2 核 8G、磁盘 40G 以上,数据目录单独规划,别和系统盘挤在一起。
系统层面有四件事必须先做掉,做完再谈安装,否则预安装脚本会直接把你拦下来:
# 1. 关闭 SELinux,enforcing 状态下 gs_preinstall 会拒绝执行 sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config setenforce 0 # 2. 放通数据库端口 26000,实验九的 JDBC 也要走这个端口 firewall-cmd --permanent --add-port=26000/tcp firewall-cmd --reload # 3. 内核参数,openGauss 启动时会做检查,信号量和共享内存不够会直接失败 cat >> /etc/sysctl.conf <<'EOF' kernel.sem = 250 32000 100 999 kernel.shmall = 1073741824 kernel.shmmax = 4398046511104 kernel.shmmni = 4096 net.ipv4.ip_local_port_range = 9000 65500 net.core.rmem_default = 212992 net.core.rmem_max = 212992 net.core.wmem_default = 212992 net.core.wmem_max = 212992 EOF sysctl -p # 4. 主机名和 hosts 必须能互相解析,主机名不要带下划线 hostnamectl set-hostname node1 echo "192.168.x.x node1" >> /etc/hosts这几行里最容易被跳过的是kernel.sem和kernel.shmmax。前者控制信号量数组,数据库并发连接一多就要用;后者是共享内存上限,给得太小,实例初始化阶段就会报共享内存申请失败,而且报错信息不直观,只会在日志里留一行could not create shared memory segment。net.ipv4.ip_local_port_range之所以要从默认值往 9000 以上挪,是给数据库自己分配本地端口留出空间,避免被系统随机端口抢占。
字符集建议统一 UTF8。如果安装时用了默认的 C locale,后面建库再想改就麻烦了,中文字段排序、长度计算都会出问题。这一点在实验二建Students、Teachers这些带中文姓名的表时会立刻体现出来。
注意:
hostnamectl改完主机名一定要重启或者重新登录,/etc/hosts里写的 IP 也要和虚拟机实际 IP 对上。我见过配置文件里backIp1写的是示例 IP,装完连不上,翻了两小时日志才发现是复制文档时没改。
2.3 安装用户与目录规划:omm 用户、软件目录、数据目录
openGauss 不允许用 root 直接跑,官方脚本会创建一个专用安装用户,习惯上叫omm。这一步看着简单,实际上密码复杂度和目录属主是两个高频坑。
# 创建安装用户和属组,-m 会自动建家目录 groupadd dbgrp useradd -g dbgrp -m -d /home/omm omm passwd omm # 密码必须 8 位以上,含大小写字母、数字、特殊字符,否则后面初始化会失败 # 规划三个目录:安装包、软件安装目录、数据目录 mkdir -p /opt/software/openGauss mkdir -p /opt/openGauss/data/single_node chown -R omm:dbgrp /opt/software/openGauss /opt/openGauss chmod 755 /opt/software/openGauss目录规划的逻辑是:/opt/software/openGauss只放解压出来的安装包,属于临时目录;/opt/openGauss是GAUSSHOME的父目录,安装后应用、日志、工具、core 文件都在这下面;/opt/openGauss/data/single_node是真正的数据目录($PGDATA),后面cluster_config.xml里的dataNode1参数就要指到这里。
chown那行不能省。预安装脚本会以 root 身份改目录属主,但如果你提前手建了data/single_node又忘了给它omm权限,安装到初始化数据目录那一步会失败,报的是权限错误,但日志不会明确告诉你是哪个子目录,很容易看漏。
密码那一关也别抱侥幸心理。复杂度不够的情况下,gs_install会在最后阶段才报错,前面几十分钟的预安装全白做,这是最让人想砸键盘的一种失败。装之前就把密码按规则定好,写在一个地方记着。
3. openGauss 安装与 gsql 连接:从安装包校验到 gs_om -t start
底子打好之后,安装过程其实就三件事:把包装上、把配置文件写对、把服务拉起来。
3.1 安装包解压与环境变量:GAUSSHOME、LD_LIBRARY_PATH、PATH
安装包一般是个all结尾的压缩包,里面套着一个主包和若干脚本。解压顺序别搞反,all包解开之后还要再解一次主包,script目录才会出现。
cd /opt/software/openGauss # 第一层:解开 all 包,得到主包和校验文件 tar -zxvf openGauss-x.x.x-openEuler-64bit-all.tar.gz # 第二层:解开主包,得到 simpleInstall、script 等目录 tar -zxvf openGauss-x.x.x-openEuler-64bit.tar.gz ls # 此时应该能看到 script、simpleInstall、lib 等目录文件名里的版本号以你实际下载到的为准,x.x.x只是占位。这里有个判断依据:包名里带openEuler字样,说明它是给 openEuler 系统准备的;如果你装的是别的发行版,要先确认依赖能不能对上,别硬装。
解压完成后,环境变量由预安装脚本自动写进omm用户的~/.bashrc,主要包括三个:
GAUSSHOME:指向应用安装目录,gsql、gs_om、gs_dump这些命令都在这下面。LD_LIBRARY_PATH:指向$GAUSSHOME/lib,gsql依赖的那批动态库在这里。PATH:把$GAUSSHOME/bin加进去,否则直接敲gsql会提示 command not found。
手工验证也很简单:su - omm之后echo $GAUSSHOME看有没有值,which gsql看路径对不对。如果为空,说明预安装没跑完或者 shell 配置没生效,这时候别急着装第二步。
提示:
su omm和su - omm差别很大。前者不加载目标用户的环境变量,后者才走登录 shell。实验报告里写的su - omm那个横杠不是装饰,少了它,gsql就会报找不到动态库。
3.2 集群配置文件与 gs_preinstall、gs_install
cluster_config.xml是整台机器安装行为的唯一依据,端口、数据目录、IP 全从这里读。单机实验环境写一个最简版本就够:
<?xml version="1.0" encoding="UTF-8"?> <ROOT> <CLUSTER> <PARAM name="clusterName" value="db_cluster"/> <PARAM name="nodeNames" value="node1"/> <PARAM name="gaussdbAppPath" value="/opt/openGauss/app"/> <PARAM name="gaussdbLogPath" value="/opt/openGauss/log"/> <PARAM name="tmpMppdbPath" value="/opt/openGauss/tmp"/> <PARAM name="gaussdbToolPath" value="/opt/openGauss/tool"/> <PARAM name="corePath" value="/opt/openGauss/corefile"/> <PARAM name="clusterType" value="single-inst"/> </CLUSTER> <DEVICELIST> <DEVICE sn="node1"> <PARAM name="name" value="node1"/> <PARAM name="azName" value="AZ1"/> <PARAM name="azPriority" value="1"/> <PARAM name="backIp1" value="192.168.x.x"/> <PARAM name="sshIp1" value="192.168.x.x"/> <PARAM name="dataNum" value="1"/> <PARAM name="dataPortBase" value="26000"/> <PARAM name="dataNode1" value="/opt/openGauss/data/single_node"/> </DEVICE> </DEVICELIST> </ROOT>重点看三个参数:nodeNames和<DEVICE>里的name必须和hostname输出完全一致;backIp1、sshIp1要填虚拟机真实 IP,不是示例 IP;dataPortBase就是26000,后面gsql -p 26000连的就是它。clusterType写single-inst,表示单机单实例,九成实验场景用这个就够了。
配置文件就位后,分两步执行:
# 第一步:root 执行预安装,-U 指定安装用户,-G 指定属组,-X 指定配置文件 /opt/software/openGauss/script/gs_preinstall -U omm -G dbgrp -X /opt/software/openGauss/cluster_config.xml # 第二步:切到 omm 用户执行真正的安装,安装过程会交互式要求输入数据库初始密码 su - omm gs_install -X /opt/software/openGauss/cluster_config.xml \ --gsinit-parameter="--locale=C" \ --gsinit-parameter="--encoding=UTF8"预安装干的事是:创建目录、改权限、配环境变量、检查内核参数和依赖包、给omm用户配互信。这些都是 root 权限才能做的,所以必须在 root 下跑。安装阶段才真正拉起实例、初始化数据目录,所以必须切到omm下跑。
--gsinit-parameter可以传多个,这里指定 locale 和编码。编码一旦定下来,后面建库建表都跟它走,中途改不了,所以第一次就写 UTF8。预安装如果中途失败,不要直接重跑,先看它留下的日志,通常是缺libaio这类依赖或者内核参数不达标,把原因解决了再执行一次,必要时先手工清理半成品目录。
3.3 启动、连接与建用户建库:gs_om -t start 到 gsql -d postgres
装完之后第一件事是看状态,第二件事是启动,第三步才是连。
su - omm gs_om -t status --detail # 查看实例状态,正常应为 Normal gs_om -t start # 启动服务,成功会打印 Successfully started gs_om -t status --detail # 再确认一次gs_om是 openGauss 的运维命令,-t后面跟动作,常用的有start、stop、status、restart。它管的是实例级别的启停,和单个会话无关。实验七做事务并发的时候要反复启停,这个命令会用到很多次。
服务起来之后连数据库:
# -d 指定数据库名,-p 指定端口,-r 开启行编辑(支持上下键翻历史命令) gsql -d postgres -p 26000 -rpostgres是 openGauss 安装完成后默认生成的数据库,初始只能用它做入口。26000是主节点端口,和配置文件里的dataPortBase必须一致。-r这个参数强烈建议加上,没有它,命令行里按上箭头不会翻历史命令,长 SQL 写错一个字符就要重敲一遍。
进去之后第一件事是建一个自己的用户和库,别一直用初始管理员账号做实验:
-- 创建实验用普通用户,密码同样要满足复杂度要求 CREATE USER dbuser WITH PASSWORD 'Abc@123456'; -- 创建业务库,属主指定为刚建的用户 CREATE DATABASE db_sc OWNER dbuser; -- 切换到新库 \c db_sc这里有个细节:CREATE DATABASE不能放在事务块里,也不能在连着的会话里对当前库操作,所以要先\c到postgres再执行,执行完再切过去。建用户和建库的顺序也不能反,库的属主必须是个已经存在的角色。
常用元命令记几个就够用了:\l列数据库、\c dbname切库、\dt列表、\d+ tablename看表详细结构、\di看索引、\dn看 schema、\db看表空间、\du看用户、\q退出。这套命令是后面所有实验的基础,实验一要求熟悉的「查看数据库对象」就是这些。
4. 库表骨架落地:表空间、schema、六张表与索引的创建顺序
实验二到实验四是整套报告里最实在的部分,也是最容易在顺序上翻车的地方。表建错顺序,外键就加不上;schema 没切对,表建到了别的命名空间里,查的时候一直报「关系不存在」。
4.1 表空间与数据库:relative location 的路径规则
表空间可以理解为给数据单独划一个存放位置,和数据库不是一回事:一个数据库可以指定一个默认表空间,一个表空间可以服务多个数据库。实验里先建表空间再建库,是为了让数据文件落到独立目录。
-- 相对路径写法,路径相对于实例的数据目录 $PGDATA,末尾目录必须是空目录 CREATE TABLESPACE tpcds_local RELATIVE LOCATION 'tablespace/tablespace_1'; -- 建库时指定默认表空间 CREATE DATABASE db_sc WITH TABLESPACE = tpcds_local; -- 改名,后面所有实验统一用 db_school ALTER DATABASE db_sc RENAME TO db_school; -- 切过去 \c db_schoolRELATIVE LOCATION后面写的是相对路径,不用写绝对路径,也不用自己mkdir。实例会在$PGDATA下面自动创建对应目录。如果你手建了目录再指过去,且目录非空,创建会失败。这一点和某些数据库的习惯不同,别想当然。
数据库改名之后,\l里能看到db_school,但已经连着的会话还挂在旧库上,要重新\c db_school才生效。这个小细节在实验二卡过不少人,改完名直接建表,表建到了postgres里,后面怎么查都查不到。
4.2 schema 与用户:book_eg 的创建、归属与级联删除
schema 是数据库内部的命名空间,一个库里可以有多个 schema,每个 schema 下有自己的表、视图、索引。它和用户不是同一个概念,但默认情况下,用户建的对象会落在同名 schema 下。
-- 创建一个独立的 schema,用于放教学示例的两张表 CREATE SCHEMA book_eg; -- 建表时带上 schema 前缀,表就会落在 book_eg 下 CREATE TABLE book_eg.teachers ( tno CHAR(20) PRIMARY KEY, tname CHAR(20) NOT NULL ); -- 删除 schema 时用 cascade,会连同里面的对象一起删掉 DROP SCHEMA book_eg CASCADE;cascade是个双刃剑。它省事,但也意味着你一条命令就能把一整套表端掉,而且没有二次确认。实验环境里无所谓,生产环境里我一般会先\dt book_eg.*看一眼里面有什么,再决定加不加cascade。不加的话,schema 非空会直接报错,这反而是个保护。
另一个高频问题是search_path。默认值是"$user", public,意思是先找和当前用户同名的 schema,再找public。你把表建在book_eg里,又没加前缀,查询就会报「关系不存在」。解决办法有两个:写全book_eg.teachers,或者临时SET search_path TO book_eg, public;。做实验时我倾向于写全前缀,因为报告要截图,写全了别人一眼能看出对象在哪个 schema。
4.3 六张表的外键顺序:Teachers → Departments → Students → Courses → SC → Teaches
这里有个互相引用的死结:Teachers里有院系号Dno,Departments里有系主任工号Dheadno,两边互相指。正确做法是先建Teachers但不加Dno的外键,再建Departments(它引用Teachers),最后用ALTER TABLE回头给Teachers补外键。这就是报告里说的「由于关系中存在相互参照,需要通过修改表添加表级约束」。
-- 1. 教师表先建,Dno 暂不加外键 CREATE TABLE Teachers ( Tno CHAR(20) PRIMARY KEY, Tname CHAR(20) NOT NULL, Sex CHAR(20) CHECK (Sex IN ('男','女')), Birthday DATE, Title CHAR(20), Dno CHAR(20) ); -- 2. 院系表引用教师表的主键 CREATE TABLE Departments ( Dno CHAR(20) PRIMARY KEY, Dname CHAR(20), Dheadno CHAR(20), CONSTRAINT fk_dept_head FOREIGN KEY (Dheadno) REFERENCES Teachers(Tno) ); -- 3. 回头补齐教师表的外键,形成互相引用 ALTER TABLE Teachers ADD CONSTRAINT fk_teacher_dept FOREIGN KEY (Dno) REFERENCES Departments(Dno); -- 4. 学生表引用院系表 CREATE TABLE Students ( Sno CHAR(20) PRIMARY KEY, Sname CHAR(20) NOT NULL, Sex CHAR(20) CHECK (Sex IN ('男','女')), Birthday DATE, Enrollyear CHAR(20), Speciality CHAR(20), Dno CHAR(20), CONSTRAINT fk_student_dept FOREIGN KEY (Dno) REFERENCES Departments(Dno) ); -- 5. 课程表,学时和学分用 SMALLINT 就够,别用 INT 浪费空间 CREATE TABLE Courses ( Cno CHAR(20) PRIMARY KEY, Cname CHAR(20) NOT NULL, Period SMALLINT, Credit SMALLINT ); -- 6. 选课表,主键是学号+课程号的联合主键 CREATE TABLE SC ( Sno CHAR(20), Cno CHAR(20), Grade SMALLINT, CONSTRAINT pk_sc PRIMARY KEY (Sno, Cno), CONSTRAINT fk_sc_student FOREIGN KEY (Sno) REFERENCES Students(Sno), CONSTRAINT fk_sc_course FOREIGN KEY (Cno) REFERENCES Courses(Cno) ); -- 7. 授课表,教师和课程的对应关系 CREATE TABLE Teaches ( Tno CHAR(20), Cno CHAR(20), CONSTRAINT pk_teaches PRIMARY KEY (Tno, Cno), CONSTRAINT fk_teaches_teacher FOREIGN KEY (Tno) REFERENCES Teachers(Tno), CONSTRAINT fk_teaches_course FOREIGN KEY (Cno) REFERENCES Courses(Cno) );这段顺序的核心原则只有一条:被引用的表必须先存在,被引用的列必须有主键或唯一约束。SC和Teaches放最后,因为它们同时引用两张表。
约束写法上有两种:列级约束直接跟在字段后面,比如Sno CHAR(20) PRIMARY KEY;表级约束写在所有字段之后,用CONSTRAINT 名字 FOREIGN KEY ...。涉及多列的时候只能用表级,比如SC的联合主键。给约束起名字是个好习惯,报错信息里会带上名字,出问题一眼能定位到是哪条。
SMALLINT和INT的选择也值得说一句。学时、学分、成绩这类数值范围很小的字段用SMALLINT,取值范围 0 到 65535(无符号)足够;INT是 4 字节,范围大但占空间。实验报告要求写「属性类型的选取理由」,这就是现成的答案。
建完六张表,用\dt确认全部存在,再逐张\d+ 表名看结构,报告里那些「图 x 表结构」的截图就是这么来的。
4.4 索引与结构核查:CREATE INDEX、\di、\d+ 三个观察命令
索引的目的是加快检索,代价是写入变慢、占空间。实验二只要求建一个普通索引,重点是搞清楚「主键自动建索引」这件事。
-- 在学生表的院系号上建普通索引,加速按院系查学生 CREATE INDEX Student_Dept ON Students(Dno); -- 查看当前库所有索引 \di -- 查看某张表的详细结构,包括索引和约束 \d+ Students -- 不需要时删除 DROP INDEX Student_Dept;CREATE INDEX的语法里,索引名后面跟表名,括号里是列名,多列索引用逗号隔开。索引名在同一 schema 内要唯一,命名习惯上建议「表名_列名」或者语义化一点的名字,别用idx1、idx2。
\di列出来的结果里,你会看到主键自动生成的索引,名字通常带_pkey后缀。这就是「DBMS 自动为主码建立索引」的直接证据,报告里可以直接拿这张截图说明。
\d+比\d多输出几列,包括字段的存储方式、约束定义、索引信息。查表结构优先用它,信息最全。
提示:建索引之前先想清楚查询条件。学生表上最常查的是按姓名、按院系,按姓名建索引有意义;如果整张表就几十行数据,建不建索引执行计划都走全表扫描,这时候建了也白建,反而让报告显得没思考。
5. 装完之后最容易翻车的五处:openGauss 常见问题排查
前面按流程走完,大概率是能连上的。但实验环境的特点就是折腾,装一次不够,装三次四次很正常。下面这五条是我在实际环境里反复遇到的,按「现象 → 原因 → 解决」记下来。
5.1 现象:gs_om -t start 一直停在 starting,gsql 连不上
原因:最常见的是内存不够。实例启动要预分配共享内存,2G 内存的虚拟机基本起不来,日志里会出现共享内存申请失败的记录。其次可能是 26000 端口被别的进程占了,或者上一次没停干净,残留进程还挂着。
解决:先gs_om -t status --detail看实例状态,再ss -lntp | grep 26000看端口占用。端口被占就gs_om -t stop彻底停掉再起,还不行就ps -ef | grep gaussdb找到残留进程手工清掉。内存不足只能加内存,别指望调参数绕过去,绕过去后面做并发实验一样卡。日志位置在$GAUSSHOME/../log或者数据目录下的pg_log,报错关键字一般是shared memory、bind、permission。
5.2 现象:gsql 报 error while loading shared libraries: libreadline.so
原因:gsql依赖$GAUSSHOME/lib下的一批动态库,LD_LIBRARY_PATH没生效就会这样。九成情况是用su omm而不是su - omm切过去的,环境变量没加载。
解决:退出来重新su - omm,echo $LD_LIBRARY_PATH确认有值。如果还是不行,用ldd $(which gsql)看具体缺哪个库,缺的库一般在$GAUSSHOME/lib里能找到,实在找不到就是安装包不完整,重新解压安装。这条坑很玄学,因为同一条命令在 root 下能跑,切用户就报错,很容易被误判成安装失败。
5.3 现象:ERROR: relation "teachers" does not exist
原因:三种可能。一是表建在book_egschema 下,查询时没加前缀,search_path里也没有它;二是表名大小写问题,openGauss 会把不加引号的标识符折叠成小写,你建表时写Teachers,实际存的是teachers;三是压根没建成功,前面的建表语句报错了但被忽略过去。
解决:先\dn看有哪些 schema,再\dt *.*或者\dt book_eg.*找表到底在哪。确认存在后用SET search_path TO book_eg, public;或者写全book_eg.teachers。养成习惯:建表语句执行完立刻\dt确认一次,别一口气贴十行 SQL 然后不管结果。
5.4 现象:加外键时报 there is no unique constraint matching given keys
原因:外键引用的那一列在被引用表里不是主键,也没有唯一约束。另一种情况是建表顺序反了,被引用的表还不存在,报错信息会变成 relation does not exist。
解决:回到被引用的表上\d+ 表名看约束,确认那一列是 PRIMARY KEY 或者有 UNIQUE。顺序问题就按 4.3 节的顺序重排:先Teachers,再Departments,再ALTER TABLE补Teachers的外键,然后Students、Courses、SC、Teaches。真建错了就DROP TABLE重来,实验环境里数据不重要,别舍不得。
5.5 现象:普通用户查表报 permission denied for relation
原因:实验六安全性控制那一节才会正式讲授权,但很多人提前用普通用户连库,发现连SELECT都不行,以为环境坏了。实际上是GRANT没做,或者GRANT做在了错误的库、错误的 schema 上。
解决:用属主账号或者管理员账号执行授权,注意库和 schema 都要对:
-- 授表级查询权限,必须在表所在的库里执行 GRANT SELECT ON Students TO dbuser; -- 授 schema 使用权限,没有这个连表名都解析不了 GRANT USAGE ON SCHEMA public TO dbuser; -- 收回权限做对比验证 REVOKE SELECT ON Students FROM dbuser;验证的时候用\dp 表名看权限列表,比翻系统表快。授权语句最容易犯的错是在postgres库里执行,然后连到db_school里测试,当然不生效。做实验六的时候,先\c到目标库再操作,这是基本纪律。
6. 验证跑通:用九个实验的最小闭环检查这套环境
装完不代表能用,我一般的做法是跑一个最小闭环,把九个实验各自最关键的一步串起来验证一遍。全部通过,这套环境才算真的交付。
| 检查项 | 命令或操作 | 期望结果 |
|---|---|---|
| 实例状态 | gs_om -t status --detail | Normal |
| 连接与库列表 | gsql -d postgres -p 26000 -r后\l | 能看到db_school |
| 表与结构 | \c db_school后\dt、\d+ Students | 六张表齐全,约束和外键都在 |
| 视图 | CREATE VIEW v_stu AS SELECT Sno,Sname,Dno FROM Students; | 创建成功,SELECT * FROM v_stu;有结果 |
| 完整性 | INSERT INTO Students(Sno,Sname,Sex) VALUES('S999','测试','未知'); | 被 CHECK 约束拒绝 |
| 安全性 | 用dbuser登录查表 | 未授权时报 permission denied |
| 事务 | BEGIN; UPDATE Courses SET Credit=Credit+1; ROLLBACK; | 回滚后数据不变 |
| 备份恢复 | gs_dump -U omm -p 26000 -f /home/omm/db_school.sql db_school | 生成 SQL 文件,非空 |
| JDBC | 用驱动连jdbc:postgresql://IP:26000/db_school | 打印查询结果 |
备份那一步要多说一句:gs_dump导出的是逻辑备份,纯文本 SQL,可以直接用gsql -f灌回去。实验八要求写恢复过程,我建议先gs_dump一份,再DROP TABLE一张表,然后从备份里恢复,最后核对行数。这个流程走一遍,比看十页文档都管用。
JDBC 那一步是最容易在答辩时被问住的。驱动包要和数据库版本对得上,连接串里的端口就是 26000,不是默认的 5432。下面这段是最小可运行版本:
// 端口必须是 26000,和 dataPortBase 一致;库名换成你自己的 String url = "jdbc:postgresql://192.168.x.x:26000/db_school"; String user = "dbuser"; String pwd = "Abc@123456"; try (Connection conn = DriverManager.getConnection(url, user, pwd); Statement st = conn.createStatement(); ResultSet rs = st.executeQuery("SELECT Sno, Sname FROM Students")) { while (rs.next()) { System.out.println(rs.getString("Sno") + " " + rs.getString("Sname")); } }跑通这段,实验九的截图就有了;跑不通,先看是连接被拒还是认证失败,前者查端口和防火墙,后者查用户密码和pg_hba.conf里的认证方式。
从那以后我每次装完 openGauss,都会强制走一遍这个九项检查表,一项不落地打勾,才敢开始写实验报告。因为我知道,漏掉「完整性约束」那一项的人,最后总会在实验五被一个莫名其妙的插入失败卡住半天;漏掉备份那一项的人,实验八只能重装一遍环境。希望帮到你。
本文还有配套的精品资源,点击获取