简介这份数据库实验报告书来自郑州大学zzu适用于计算机科学与技术、软件工程等专业的学生是《数据库系统原理实验》课程的完整实践材料。资源以docx文档形式提供共1个文件压缩包大小为3.23MB内容覆盖基于openGauss的九个核心实验认识DBMS系统、创建数据库表与索引、交互式SQL语句、创建视图、完整性控制、安全性控制、事务与并发控制、数据库备份与恢复以及使用JDBC连接数据库。每个实验都包含实验目的、操作步骤、问题修正过程与结果分析并配有截图和规范的表注能直观反映从环境搭建到编程访问数据库的完整学习路径。报告还详细记录了常见错误排查方法对初次使用openGauss的学生尤其有参考价值。已有1522人浏览学习适合正在修读数据库课程、需要参考实验报告结构或准备数据库相关考核的高校学生可作为巩固理论知识和提升实践能力的重要参考。1. 数据库实验报告书把 openGauss 九个实验串成一条实践路线第一次拿到这份《数据库系统原理实验》报告书时我下意识觉得它不过是一份交作业用的文档。直到把九个实验从头到尾过了一遍才发现这其实是一条非常完整的数据库实践路线从认识 DBMS 系统开始到创建数据库、表和索引再到交互式 SQL、创建视图、完整性控制、安全性控制、事务与并发最后落到备份恢复和 JDBC 连接数据库。整套实验基于 openGauss命令行侧用 gsql 操作编程侧用 JDBC 打通应用与数据库几乎覆盖了关系数据库从 DDL 到 DCL、从单机操作到并发编程的所有基础面。适合正在做数据库课程实验的同学照着复现也适合想用国产数据库快速过一遍核心操作的开发者——按这条路线走踩坑能少一半。2. 环境搭建与命令行基础安装 openGauss 到 gsql 连接2.1 三种安装模式怎么选报告在实验一里列了三种安装模式云服务器上直接安装、虚拟机一键安装、先装 openEuler 再装 openGauss。三种方式最终都会落到同一个动作上——启动数据库服务、用 gsql 连上默认的 postgres 库但它们的前置条件和适用场景差别挺大我整理了一张对比表安装方式前置条件优点适合场景云服务器安装云资源与公网 IP开箱即用可远程连接多人协作实验、没有本地虚拟化环境虚拟机一键安装已提供的虚拟机镜像安装最快失败率低课程实验首选openEuler 再安装已装 openEuler 的虚拟机环境贴近生产安装过程透明想理解依赖关系和安装细节的人我的建议是课程实验直接选第二种别纠结。一键安装把 openGauss 的依赖、用户、目录一次性配好省下大量排查时间顺序反过来你会发现很多报错都集中在系统依赖缺失上而这些和数据库本身没什么关系。选云服务器则要注意安全组放通 26000 端口否则客户端永远连不上这不是玄学就是网络层把端口挡了。2.2 启动服务、连接默认库与基础校验安装完成后openGauss 默认只有一个 postgres 数据库它是实例初始化时自动生成的。后续创建数据库、建表都建议先连到这个库里操作。启动和连接的完整命令链是下面这样# 在主节点服务器上切换至数据库安装用户 omm su - omm # 启动数据库服务 gs_om -t start # 连接默认数据库端口 26000-r 表示只读模式 gsql -d postgres -p 26000 -r三条命令各干一件事su - omm切换用户因为 openGauss 的服务进程和文件归属都是安装用户普通用户没有权限操作gs_om -t start是集群管理工具启动服务的通用写法-t指定操作为 startgsql是 openGauss 自带的命令行客户端-d指定数据库名-p指定端口号-r打开只读模式防止误操作。连接之后我一般先看服务状态到底正不正常而不是急着敲 SQL。gs_om -t status能查主节点状态gs_om -t start如果已经启动过会提示节点已启动这时直接连就行。26000 是 openGauss 默认端口和 MySQL 的 3306、PostgreSQL 的 5432 都不一样记错端口是新手第一个翻车点。2.3 用元命令和系统表摸清数据库实例gsql 里有一批以反斜杠开头的元命令它们是了解实例最快的方式运行时不走 SQL 解析由客户端直接处理\l # 查看数据库列表 \c db_school # 切换数据库 \dt # 列举当前 schema 下的表 \d Teachers # 查询 Teachers 表的完整属性 \dn # 列举 schema \di # 查看索引 \db # 查看表空间 \q # 退出 gsql除了元命令系统表是更有信息量的来源。用 SQL 查pg_database能拿到实例下所有数据库的清单查pg_tablespace能看到表空间名称查pg_user、pg_authid、pg_roles能分别看到用户、用户属性和角色列表。这里很容易混淆的是用户和角色报告里的辨析写得很清楚角色是一类权限的集合用户是具体账户个体。比如仓库管理员是一个角色公司里好几个仓库管理员是不同的用户分配权限时把角色授给用户用户就拥有了这组权限换岗时换角色即可。数据库里同理一个用户只能查某些表、另一个用户只能执行存储过程这种诉求用角色来管最省事。3. 核心模块拆解建库建表、索引视图的完整 SQL 链路3.1 表空间、数据库与模式先分清楚这三层实验二从表空间开始这个概念的类比最直观表空间像文件夹数据库像在文件系统里划出的一块存储区域。实操先建表空间再建数据库最后切目标库-- 创建表空间relative location 指向空目录 create tablespace tpcds_local relative location tablespace/tablespace_1; -- 创建数据库并指定表空间 create database db_sc with tablespace tpcds_local; -- 数据库改名 alter database db_sc rename to db_school; -- 切换至新库 \c db_schoolrelative location的路径是相对于数据目录的所以只要给一个空目录名就行不需要写绝对路径with tablespace把新库的表空间指向前一步创建的 tpcds_local这样数据库的物理文件会落在独立位置便于后续备份和空间管理。有一个常见拼写错误要小心ALTER DATABASE中间的 DATABASE 不要拼成 DATABAE一旦拼错openGauss 直接报语法错误而且报错信息不会提示你少了字母。模式Schema是这一层容易绕晕的概念。openGauss 里的 schema 是一个名字空间里面可以放表、视图、存储过程等对象一个数据库可以有多个 schema默认情况下对象建在 public 这个 schema 里。用独立 schema 隔离业务模块是常见做法-- 创建 book_eg 模式 create schema book_eg; -- 在模式下建表 create table book_eg.Teachers(); -- 级联删除模式连同里面的对象一起删 drop schema book_eg cascade;删除 schema 时不加cascade会报错因为 schema 里已经有对象了。我一般会给每个实验模块单独开一个 schema做完直接级联删除比一张张表 drop 干净利落。3.2 建表与约束列级约束和表级约束的边界建表是实验二的重头戏报告里的六张表教师表、院系表、学生表、课程表、选课表、教学表构成了一个完整的参照关系网络。先看带约束的教师表create table Teachers( Tno CHAR(20) PRIMARY KEY, Tname CHAR(20) NOT NULL, Sex CHAR(20) CHECK(Sex 男 OR Sex 女), Birthday DATE, Title CHAR(20), Dno CHAR(20) );这里PRIMARY KEY、NOT NULL、CHECK都是列级约束直接跟在字段定义后面不需要指定列名因为它们就作用在该列上。约束分为列级和表级列级约束只能作用于所在列表级约束在字段定义之后、用逗号分隔且必须显式写出约束的列名。NOT NULL和DEFAULT只能写为列级约束CHECK、UNIQUE、FOREIGN KEY则两种写法都可以。院系表和教师表之间存在外键参照这是表级约束的典型场景create table Departments( Dno CHAR(20) PRIMARY KEY, Dname CHAR(20), Dheadno CHAR(20), FOREIGN KEY (Dheadno) REFERENCES Teachers(Tno) );外键约束的意义是保证引用完整性往 Departments 插入数据时openGauss 会检查Dheadno在 Teachers 表的Tno中是否存在不存在就拒绝插入。这里最容易被忽略的是 CHAR(20) 的补空格行为——如果你在 Teachers 里插入了T01实际存储的是带尾部空格的长度 20 的字符串关联查询时一不小心就会因为空格匹配不上而查不到数据。处理这种问题要么统一用 VARCHAR要么在查询时用TRIM这点在第五部分还会提到。学生表、课程表、选课表的建表逻辑同理选课表 SC 是最典型的多对多关联表create table SC( Sno CHAR(20), Cno CHAR(20), Grade SMALLINT, PRIMARY KEY (Sno, Cno), FOREIGN KEY (Sno) REFERENCES Students(Sno), FOREIGN KEY (Cno) REFERENCES Courses(Cno) );这张表的主键是复合主键Sno Cno同时有两个外键分别指向学生表和课程表是标准的关系模式拆分。建完所有表后用\dt能看到完整表清单用\d 表名可以查看字段属性和约束定义我习惯每一张表创建后都立即查看确认避免问题积累到最后。3.3 索引创建、查看与删除索引的作用是加速检索但日常使用中最容易忽略的一点是DBMS 会自动为主键建索引所以主键列不需要手动再建一个。实验要求手动建的是外键列和查询频繁列上的索引-- 在 Students 表的 Dno 列创建索引 create index Student_Dept on Students(Dno); -- 查看当前 schema 下所有索引 \di -- 删除索引 drop index Student_Dept;为什么要在外键列上建索引因为外键关联查询经常以WHERE Dno ?作为过滤条件没有索引就是全表扫描数据量大了之后查询性能会明显下降。但也不要所有列都建索引索引占用存储空间插入和更新时要同步维护属于典型的空间换时间建多了反而拖慢写操作。3.4 视图把复杂查询封装成虚表实验四创建视图核心思路是把一个常用的查询语句保存成一个虚拟表之后对视图的查询和对表的查询完全一致。常见的做法是这么写的-- 创建视图只看女学生的基本信息 create view v_student_girls as select Sno, Sname, Speciality from Students where Sex 女; -- 像查表一样查视图 select * from v_student_girls;视图不额外占用存储空间每次查询视图时都会重新执行基表的 SELECT但对应用程序来说视图隐藏了表结构和过滤条件接口稳定即使底层表结构调整只要视图定义不变应用层就不用改。要注意的是视图的可更新性有限带DISTINCT、聚合函数、GROUP BY的视图通常不能直接做 INSERT 或 UPDATE这一点很容易在后续实验中翻车。4. 数据治理实战完整性、安全性、事务与并发控制4.1 完整性控制约束之外的底线实验五讲完整性控制这是数据库区别于文件系统的核心能力。完整性约束的作用是保证数据的正确性和一致性openGauss 支持五类约束NOT NULL保证字段非空PRIMARY KEY保证唯一且非空UNIQUE只保证唯一但允许为空CHECK做范围校验FOREIGN KEY做参照完整性。约束可以在建表时定义也可以事后用ALTER TABLE补充。比如 Teachers 表建表时没有给 Dno 加外键后面发现需要参照 Departments 表就可以这样补上-- 为教师表添加外键约束引用院系表 alter table Teachers add constraint fk_teacher_dept foreign key (Dno) references Departments(Dno);这里的constraint fk_teacher_dept是给约束起名字方便后续用alter table drop constraint fk_teacher_dept定向删除。违约处理是完整性的关键行为主键冲突会直接拒绝插入外键违约同样拒绝操作所以完整性控制与其说是功能不如说是数据库自我保护的机制。有些业务规则靠约束表达不了比如「选课成绩只能在 0 到 100 之间且课程学分必须在 1 到 10 之间」。CHECK可以覆盖一部分更复杂的逻辑 openGauss 可以用触发器实现但触发器的维护成本高实际项目中我一般优先把规则写进约束只有约束确实搞不定时才上触发器。4.2 安全性控制用户、角色与权限分配实验六的内容是安全性控制核心就两件事谁能连数据库谁能对哪些对象做什么操作。openGauss 用 PostgreSQL 那套权限模型先建用户再授权-- 创建数据库用户 create user app_user identified by App123456; -- 授予查询权限 grant select on Students to app_user; -- 撤销权限 revoke select on Students from app_user; -- 创建只读角色再把角色授予用户 create role read_only; grant select on Teachers, Courses to read_only; grant read_only to app_user;grant select on Students只授予 SELECT 权限用户能查但不能改如果要让用户同时能插入和更新写成grant select, insert, update on Students to app_user。角色在这里的价值是批量管理比如一个应用团队有十个人权限都一样直接给十个人逐个授权既啰嗦又容易漏正确做法是先建 read_only 角色把权限授给角色再把角色授给十个用户有人离职时只需要从角色里移除这个用户不用逐个对象去回收权限。实际授权时还有一个常见误区只授了表的权限没授序列sequence的权限导致应用执行 INSERT 时明明表权限正常却报权限不足。openGauss 里自增列依赖序列需要额外grant usage on sequence。这类小坑排查起来最费时间权限问题报错又不会直说缺什么拿到permission denied时先默念三遍「看序列」。4.3 事务与并发控制从 COMMIT 到锁等待实验七是事务与并发控制也是数据库理论课和实验课差距最大的一章。事务的 ACID 特性在 SQL 里落地就是三句话BEGIN开启事务COMMIT提交ROLLBACK回滚。看一个实际例子-- 开启事务 begin; -- 给选课成绩统一加 5 分 update SC set Grade Grade 5 where Sno 2021001; -- 确认无误后提交 commit; -- 如果发现加错了回滚 rollback;BEGIN之后的所有操作都在事务内没有COMMIT之前其他会话看不到这些改动执行了ROLLBACK本次事务内所有修改都会被撤销。这个逻辑在单用户下体会不到价值一旦多用户同时操作同一张表事务的隔离性就关键了。openGauss 默认隔离级别是 READ COMMITTED在这个级别下两个事务同时更新同一行数据会触发锁等待后提交的一方必须等先提交否则会被阻塞直到超时。并发带来的问题有脏读、不可重复读、幻读和更新丢失。脏读指读到未提交的数据READ COMMITTED 已经解决不可重复读指同一事务内两次查询结果不一致把隔离级别提升到 REPEATABLE READ 可以解决幻读是范围查询时其他事务插入了新行需要 SERIALIZABLE 级别才能彻底避免。实际项目中调高隔离级别意味着并发行下降绝大多数场景 READ COMMITTED 够用不要一刀切全上 SERIALIZABLE。锁等待和死锁是并发实验里最容易遇到的惊悚场面。两个会话互相持有对方需要的资源时openGauss 会自动检测并回滚其中一个事务报错信息会提示deadlock detected。遇到这种情况要做的不是改 SQL而是检查事务里操作表的顺序是不是一致——只要所有人都按同一个顺序加锁死锁基本就不可能发生。5. 备份恢复与 JDBC 连接常见问题排查与避坑记录5.1 备份与恢复gs_dump 与 gs_restore 的完整流程实验八的备份与恢复是后面所有实验的后悔药。openGauss 支持物理备份和逻辑备份两种方式课程层面用逻辑备份就够命令是 gs_dump恢复用 gs_restore# 逻辑备份 db_school 数据库到 backup.sql gs_dump -U omm -f /home/omm/backup.sql -p 26000 db_school # 先创建空库再恢复 createdb -p 26000 db_school_restore gs_restore -U omm -d db_school_restore -p 26000 /home/omm/backup.sql-U指定执行备份的用户-f指定备份文件输出位置-p是端口最后跟的是要备份的库名。恢复流程里最容易踩坑的动作是「先建空库」——直接对已有数据的库执行恢复大概率互相冲突标准动作永远是建一个新空库再恢复。恢复完成后我一般会立刻验证select count(*) from Teachers;对比备份前的行数行数对得上才算真成功。5.2 JDBC 连接 openGauss 的工程写法实验九把数据库操作从前台搬到了代码里用 JDBC 连接 openGauss。openGauss 兼容 PostgreSQL 协议所以驱动直接用 PostgreSQL 的 JDBC 驱动就可以连接串长这样import java.sql.*; public class DbConnDemo { public static void main(String[] args) throws Exception { // 1. 加载驱动类 Class.forName(org.postgresql.Driver); // 2. 建立连接URL 格式固定 String url jdbc:postgresql://127.0.0.1:26000/db_school; Connection conn DriverManager.getConnection(url, app_user, App123456); // 3. 创建语句对象并执行查询 Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(select Sno, Sname from Students where Sex 女); // 4. 遍历结果集 while (rs.next()) { System.out.println(rs.getString(Sno) , rs.getString(Sname)); } // 5. 逆序释放资源 rs.close(); stmt.close(); conn.close(); } }URL 的格式是jdbc:postgresql://主机IP:端口/数据库名连接的时候数据库名要换成真实存在的库。DriverManager.getConnection的三个参数依次是 URL、用户名、密码getString(Sno)按列名取值也可以用getString(1)按下标取。资源释放顺序必须是 ResultSet → Statement → Connection顺序反了会报连接泄漏。工程实际中不会用 Statement而是用 PreparedStatement 先占位再传参既防 SQL 注入也让数据库能复用执行计划String sql select Sno, Sname from Students where Sno ?; PreparedStatement pstmt conn.prepareStatement(sql); pstmt.setString(1, 2021001); ResultSet rs pstmt.executeQuery();5.3 高频踩坑排查清单这部分是整套实验跑下来最值得记录的踩坑集每条都是实际发生过的按现象、原因、解决的顺序写现象一gsql连接报could not connect to server。绝大多数原因是服务没有启动或者端口写错。先执行gs_om -t status查看节点状态服务没起来就用gs_om -t start确认端口是 26000 而不是 5432openGauss 的连接端口在安装时确认过就不要改两边不一致永远连不上。现象二切换用户时报su: cannot open session或者权限拒绝。一般是把系统用户和数据库用户搞混了su - omm切的是操作系统用户必须先保证 omm 用户存在并且在主节点服务器上执行这一步。在备节点上切 omm 也会遇到类似问题它没有主节点的服务权切换前先确认当前是哪台机器。现象三建表或插数据报relation xxx already exists。同一个 schema 下表名冲突了用\dt看一下实际有哪些表或者干脆用drop table 表名 cascade清掉重建。如果之前建过同名视图也会占用这个名字表和视图在同一个命名空间里共享名称。现象四插入子表数据时外键报错总是violates foreign key constraint。先查父表里是否真的有对应的主键值特别注意 CHAR 类型补空格的问题CHAR(20)存储时会自动补足到 20 个字符你肉眼看到的D01实际是D01 匹配不上是常态。解决办法是把外键字段统一改为 VARCHAR(20)或者在插入前用TRIM清理。现象五JDBC 报No suitable driver found。原因是驱动 jar 没有加入 classpath或者Class.forName里的驱动类名写错。把postgresql驱动 jar 放到项目 lib 目录并加入构建路径确认类名是org.postgresql.Driver用 Maven 的话检查依赖有没有真正下载下来。6. 二次开发思路把实验报告改造成自动化 SQL 练习环境这份报告最好的改造方式是把九个实验的 SQL 全部脚本化做成一个能一键重建的练习环境。我一般这样组织文件00_reset.sql负责删库重建、清 schema01_schema.sql放建表语句02_seed.sql放插入的初始数据03_query.sql放交互式 SQL 练习04_acl.sql放授权语句。然后用一个 shell 脚本把它们串起来#!/bin/bash # 一键重置数据库练习环境 su - omm -c gsql -d postgres -p 26000 -f /data/sql/00_reset.sql su - omm -c gsql -d db_school -p 26000 -f /data/sql/01_schema.sql su - omm -c gsql -d db_school -p 26000 -f /data/sql/02_seed.sql每次练习前跑一遍这个脚本就能回到干净的初始状态怎么折腾都不怕。更进一步的验证是给脚本加上断言逻辑——每个脚本执行完后用select count(*)检查关键表的行数行数不对就退出并报错。这样不仅能当实验环境用还能当回归测试跑改过表结构后重新执行一遍就知道有没有改坏。对动手能力强的同学还可以把 JDBC 示例扩展成一个最简工具类封装连接获取、查询、更新三个方法用连接池替代DriverManager把数据库密码放到配置文件而不是写死在代码里。这基本上就是一个小型数据访问层DAO的雏形完成之后再去接触 MyBatis 这类框架会发现一切都是熟悉的套路只是框架帮你把重复代码吃掉了。我从那以后每次搭数据库实验环境都强制先把 reset 脚本放在第一位任何时刻都能一键回到干净基线这个习惯帮我省掉了大量翻车后的人工修复时间也让我在做完第九个实验后回看第一个实验时不再觉得它是孤立的——九个实验本质上就是一条从命令行到应用程序的完整数据库工程路线。希望这份拆解能帮到你少走弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?