简介:这份zzu数据库实验报告书是计算机科学与技术、软件工程专业学生的课程实验完整记录,对应《数据库系统原理实验》课程,基于openGauss平台展开。内容覆盖认识DBMS系统、创建数据库表和索引、交互式SQL语句、创建视图、完整性控制、安全性控制、事务与并发控制、数据库备份与恢复、使用JDBC连接数据库等九大实验模块,每个实验均包含实验目的、实验内容、操作步骤、问题与解决过程以及结果分析,并伴有详细命令示例和截图说明,可帮助读者掌握概念模型与关系模型、熟练使用SQL进行查询检索与管理控制,理解数据安全性、完整性、并发性和可恢复性的实际处理方式。资源为1个docx文档,压缩包共3.23MB,目录结构清晰,适合正在学习数据库原理、需要实验报告参考或openGauss实操指引的高校学生直接使用或对照复习。已有1522人学习下载,具备较高参考价值。
1. 一份能跟着复现的 openGauss 实验报告:从安装到 JDBC 的全链路边角料
数据库实验报告书这类资源,平时大家只当作业模板用,翻两页就扔。但这本《数据库系统原理实验》报告书不太一样,它把 openGauss 从安装部署、建库建表、视图与完整性控制,一路做到事务并发、备份恢复和 JDBC 编程,九个实验串成一条完整的实操链路。对正在写数据库实验报告的学生来说,它是可以照着改的作业底稿;对刚接触 openGauss 的从业者来说,它把最容易翻车的环境搭建、权限模型、并发控制这些环节全部暴露出来了。我拿到后直接在一台虚拟机上按着走了一遍,中间确实踩了几个坑,但也正因为有坑,这份材料才有复现价值。下面我把每条命令、每个参数、每个容易出问题的地方拆开讲。
2. 认识 DBMS 与 gsql 连接:先让 openGauss 跑起来再说
2.1 三种安装模式怎么选
报告书里提到 openGauss 有三种安装模式:华为弹性云上安装、虚拟机一键安装、虚拟机安装 openEuler 后再安装 openGauss。这里先别急着选,三者的差别直接决定你后续实验是不是顺利。
华为弹性云上安装适合有云资源的人,但需要申请公网 IP、配置安全组规则,对新手来说反而多了网络层面的变量。虚拟机一键安装是最省事的路径——镜像里通常已经把 openGauss 装好了,你只需要启动虚拟机、切换用户、启动服务,就能直接进 gsql。我当时选的就是一键安装,因为实验目的是认识 DBMS,而不是折腾编译安装。如果你是第一次接触 openGauss,建议也走这条路线,把精力留给后面的 SQL 语句。
第三种方式,虚拟机装 openEuler 再手动装 openGauss,适合想搞清楚依赖关系的场景。openGauss 对操作系统的 glibc、python 版本有要求,手动装的时候会遇到不少前置依赖的坑。报告书的实验一只是让你「了解三种模式,选一个适合自己的」,所以在安装上不用过度纠结,能跑起来就是胜利。
2.2 启动服务与连接默认库:三个命令一气呵成
安装完成后,第一件事是切换用户并启动服务。数据库安装用户是 omm,注意切换命令su - omm里的减号不能省,它表示同时切换环境变量。我在第一次操作时直接敲了su omm,结果 PATH 里没有 gs 系列命令,报错说找不到gs_om,当时还以为是安装出了问题。
# 切换至数据库安装用户,注意中间有 - su - omm # 启动数据库服务 gs_om -t start # 连接默认数据库 postgres,-r 表示启用 readline 功能 gsql -d postgres -p 26000 -rgs_om -t start是 openGauss 的服务管理命令,-t指定操作类型为 start。如果服务已经在运行,它会提示unknown node或者直接跳过。连接数据库时,-d指定数据库名,-p指定端口号,openGauss 默认端口是 26000,和 PostgreSQL 的 5432 不一样,这个数字后面几乎每次连接都要用到,建议记牢。-r参数我一般都会加上,它让 gsql 支持上下键翻历史命令,否则在终端里输错一条长 SQL 就得重新敲一遍。
连接成功后,默认会进入postgres数据库。它是 openGauss 安装完成后自动生成的初始数据库,专门用来做管理操作,比如创建新数据库、创建用户。不要在 postgres 库里建业务表,这是实验报告里没明确说但实际应该养成的习惯——管理和业务数据混在一起,后面导数据、备份的时候会很难受。
2.3 gsql 元命令:不看文档也能摸清数据库家底
gsql 提供了一组反斜杠开头的元命令,比 SQL 查询系统表更直观。报告书里列了\l、\dt、\d+、\dn、\di这些,我整理成了一张表,方便对照着用。这些命令的价值在于,它们是排查问题的第一手工具——表建没建成功、约束有没有生效、索引有没有创建,敲一下就知道。
| 命令 | 作用 | 使用场景 |
|---|---|---|
\l | 列出所有数据库 | 确认 db_school 是否创建成功 |
\dt | 列出当前 schema 下的表 | 确认 Students、Courses 等表是否都在 |
\d+ tablename | 查看表完整属性,含约束和索引 | 检查主键、外键、CHECK 约束是否生效 |
\d tablename | 查看表结构 | 快速看字段名和类型 |
\dn | 列出所有 schema | 确认 book_eg 是否创建成功 |
\di | 列出所有索引 | 查看 Student_Dept 索引是否建立 |
\c dbname | 切换数据库 | 从 postgres 切到 db_school |
\c - username | 切换用户 | 从 omm 切到普通用户 |
\q | 退出 gsql | 结束会话 |
这里特别说一下\d+ tablename,它是排查建表问题的关键命令。比如你用create table建了一张表,结果忘记加外键,用\d+一看就能发现。报告书里在辨析 schema 时提到「一个数据库用户所拥有的数据库对象就构成这个用户的 schema」,这句话放到实际操作中理解就是:你在 book_eg 模式下建的表,和你在默认 public 模式下建的同名表,是两张完全独立的表,查询时必须带模式名前缀。
3. 创建数据库、模式、表和索引:把物理设计和逻辑设计分开做
3.1 表空间先行:建库前先理解 storage 层
实验二的第一个操作是创建表空间,这一步很多人会直接跳过,因为感觉「建库就建库,搞什么表空间」。但 openGauss 里表空间决定了数据库文件落在哪个目录,后续做备份恢复时,表空间路径是必须知道的参数。
-- 创建表空间,relative location 后的目录必须存在 CREATE TABLESPACE tpcds_local RELATIVE LOCATION 'tablespace/tablespace_1'; -- 创建数据库,并指定使用刚才的表空间 CREATE DATABASE db_sc WITH TABLESPACE = tpcds_local;官方文档里RELATIVE LOCATION后面跟的是相对于数据目录的路径,如果你指定的目录不存在,建表空间会直接报错。我一般会提前在数据目录下建好tablespace/tablespace_1目录。另外,openGauss 的表空间和数据库不是一对一关系——一个表空间可以容纳多个数据库,一个数据库也可以把不同表放到不同表空间。实验报告里把它类比成「文件夹」,这个比喻在理解层面够用了。
3.2 模式与级联删除:drop 的时候要格外小心
创建模式是实验二里的第二个操作,命令本身很简单:
-- 创建模式 book_eg CREATE SCHEMA book_eg; -- 在模式中建表,表名必须带模式名前缀 CREATE TABLE book_eg.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) ); -- 级联删除模式及其中的所有对象 DROP SCHEMA book_eg CASCADE;DROP SCHEMA book_eg CASCADE这行命令值得好好说道——CASCADE表示级联删除模式下的所有表、视图、索引等对象。如果不用 CASCADE,而模式里已经有对象,openGauss 会拒绝执行并提示schema book_eg is not empty。这个设计是防止误删的,但如果你确实想整个模式一起删掉,就必须显式加 CASCADE。我在一次操作中路径下有一个视图忘了删,直接执行DROP SCHEMA book_eg报错,加上 CASCADE 后一切正常。记住,CASCADE 是后悔药,但也是拆家利器——用之前确认这个模式里的东西都不需要了。
3.3 六张表建下来,约束的类型和位置就全明白了
实验二的主体是建立 TEACHERS、DEPARTMENTS、STUDENTS、COURSES、SC、TEACHES 六张表,并填充数据。这六张表的设计有典型的教学意义——主键、非空、CHECK、外键全覆盖,而且外键关系形成了环(TEACHERS 的 Dno 引用 DEPARTMENTS,DEPARTMENTS 的 Dheadno 又引用 TEACHERS 的 Tno),这在实操中会带来一个问题:先建哪张表都会失败,因为被引用的表还不存在。
解决办法有两种:一种是不管约束顺序,先把表建完再通过 ALTER TABLE 补外键;另一种是先建不带头外键的表,全部建完后统一添加约束。report书里采用的是第二种思路,所以你能看到它在建完表后专门有一个「修改表」的步骤,通过 ALTER TABLE 来补表级约束。这个顺序不是随意的,是数据库理论里「先结构后约束」的实践体现。下面是 STUDENTS 表的完整建表语句:
CREATE TABLE Students ( Sno CHAR(20) PRIMARY KEY, Sname CHAR(20) NOT NULL, Sex CHAR(20) CHECK (Sex = '男' OR Sex = '女'), Birthday DATE, Enrollyear CHAR(20), Speciality CHAR(20), Dno CHAR(20), FOREIGN KEY (Dno) REFERENCES Departments(Dno) );这条语句同时包含了列级约束(PRIMARY KEY、NOT NULL、CHECK)和表级约束(FOREIGN KEY)。区分规则是:如果一个约束只作用于单列,可以写在列定义后面;如果涉及多列,必须用表级约束的写法。在实际工作中,我习惯把所有约束都写成表级约束,格式统一,后期维护时一眼能找到。注意Enrollyear用了 CHAR(20) 而不是 INT——这是实验报告里的一个细节,表示入学年份作为字符串处理,避免年份前面的 0 丢失(比如2021和02021的区别),这种设计取舍在真实业务里也常见。
3.4 索引不是越多越好:理解主键索引与普通索引的差异
创建索引是实验二的收尾动作。openGauss 里 DBMS 会自动为主键建立索引,所以你在建表时没写任何索引语句,\di也能看到主键索引。手动创建索引是为了加速特定查询列:
-- 在 Students 表的 Dno 列上创建索引 CREATE INDEX Student_Dept ON Students(Dno); -- 查看当前数据库下所有索引 \di -- 删除索引 DROP INDEX Student_Dept;这里有几个参数细节。Student_Dept是索引名,命名规则建议是「表名_列名」,这样看到索引名字就能反推出它是建在哪张表上。索引不是越多越好,每次 INSERT、UPDATE 时索引也要同步更新,写多读少的表建索引反而拖慢性能。实验报告里辨析了索引和目录的区别——索引帮你快速定位数据行,目录展示文章结构。这个类比迁移到数据库里就是:主键索引帮你直接找到某一行,而全表扫描就是没有目录的逐页翻书。
4. 视图、完整性与安全性控制:数据库不再只是「存数据的地方」
4.1 视图:查询逻辑的复用和权限的天然屏障
实验四的核心是创建视图。视图本质是一段被命名的 SELECT 语句,不占存储空间,数据来自底层表。为什么要用视图?两个原因:一是复用——一个复杂的多表连接查询,写成视图后每次只需要SELECT * FROM view_name;二是安全——只开放需要的列给特定用户,底层表的其他列完全不可见。
-- 创建视图:学生及其院系名称 CREATE VIEW Student_Department AS SELECT s.Sno, s.Sname, d.Dname FROM Students s JOIN Departments d ON s.Dno = d.Dno; -- 查询视图,和查表语法一致 SELECT * FROM Student_Department; -- 删除视图 DROP VIEW Student_Department;创建视图时有一个容易踩的坑:视图的列名由 SELECT 子句决定,如果底层表结构变了(比如加列),视图不会自动同步。openGauss 会在查询视图时报错或返回新结构,你需要用CREATE OR REPLACE VIEW手动更新视图定义。另外,如果视图包含聚合函数或 DISTINCT,它通常不支持 INSERT、UPDATE 这类操作——这种视图是只读的。这些细节实验报告书里没有细讲,但实际做实验时一定会碰到。
4.2 完整性控制:五种约束的适用边界
实验五的完整性控制,核心是理解五类约束:NOT NULL、UNIQUE、PRIMARY KEY、CHECK、FOREIGN KEY。报告书里有一个很清晰的对比表格思路,我按实操角度重新梳理了一遍:
| 约束类型 | 列级/表级 | 作用 | 实操注意点 |
|---|---|---|---|
| NOT NULL | 仅列级 | 该列不能为空 | 无法通过 ALTER TABLE ADD CONSTRAINT 添加,只能用 MODIFY COLUMN |
| UNIQUE | 均可 | 列值唯一,可以为 NULL | 唯一约束会创建唯一索引 |
| PRIMARY KEY | 均可 | 唯一且非空,一张表只能一个 | DBMS 自动创建索引 |
| CHECK | 均可 | 列值需满足条件 | openGauss 支持,MySQL 低版本不支持 |
| FOREIGN KEY | 表级 | 值必须来自主表引用列 | 创建前被引用表必须存在 |
实验报告里有一段辨析值得划重点:「列级约束不能起约束名,表级约束可以起约束名(主键除外)」。这在实际运维中很关键——如果你想在后续通过约束名来删除某个约束,就必须在建表时用表级约束的语法并显式命名。比如:
-- 删除约束前先确认约束名 ALTER TABLE SC DROP CONSTRAINT sc_sno_fkey;如果建表时没命名,系统会自动生成一个约束名,格式通常是表名_列名_fkey这种,用\d+ 表名能查到。这里我有个习惯:凡是后续可能要删的约束,建表时就起好名字,比如ALTER TABLE Teaches ADD CONSTRAINT teaches_fk_1 FOREIGN KEY (Tno) REFERENCES Teachers(Tno),避免后面查名字浪费时间。
4.3 安全性控制:GRANT 和 REVOKE 用最小权限原则
实验六的安全性控制,本质上就是权限管理。openGauss 的安全模型继承自 PostgreSQL,用户和角色是两个概念,报告书里的比喻很到位:角色是「仓库管理员」这个岗位,用户是「老王」「小李」这些具体的人。你把角色赋予用户,用户就拥有了角色的全部权限。
-- 创建用户 db_user1 CREATE USER db_user1 WITH PASSWORD 'your_password'; -- 授予查询权限 GRANT SELECT ON Students TO db_user1; -- 授予所有表的所有权限 GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO db_user1; -- 收回权限 REVOKE SELECT ON Students FROM db_user1; -- 查看所有角色 SELECT * FROM pg_roles;实操中有个权限相关的坑:默认情况下,普通用户对 public schema 下的表没有任何操作权限,哪怕是 SELECT 也不行。很多同学在实验六里创建了一个新用户,然后拿这个用户去查 Students 表,结果报permission denied for table students,然后一脸懵。解决办法就是先 GRANT,再查询。另外,openGauss 里CREATE USER和CREATE ROLE的差别在于前者默认带 LOGIN 权限,后者不带。如果你用 CREATE ROLE 建了一个角色,要登录数据库必须额外GRANT CONNECT ON DATABASE db_school TO role_name,再ALTER ROLE role_name LOGIN。
5. 事务、并发控制与备份恢复:高频翻车区的避坑指南
5.1 事务的 ACID 在 gsql 里怎么验证
实验七的事务与并发控制,理论部分考察 ACID,实操部分就是 BEGIN、COMMIT、ROLLBACK 的组合拳。在 gsql 里验证事务特性,比在代码里直观得多:
-- 开启事务 BEGIN; -- 插入一条选课记录 INSERT INTO SC (Sno, Cno, Score) VALUES ('2021001', 'C001', 95); -- 查询该数据,此时对当前会话可见 SELECT * FROM SC WHERE Sno = '2021001'; -- 回滚事务 ROLLBACK; -- 再查询,数据已经消失 SELECT * FROM SC WHERE Sno = '2021001';事务最关键的点在于:BEGIN之后的所有操作要么全部提交(COMMIT),要么全部回滚(ROLLBACK),不存在中间状态。用两个 gsql 窗口能更直观地看到并发隔离效果——窗口 A 开启事务插入数据但不提交,窗口 B 查询同样的数据,如果隔离级别是读已提交,B 看不到 A 未提交的数据。openGauss 默认隔离级别是读已提交,这点和 PostgreSQL 一致,和 MySQL 的默认可重复读不同。
5.2 并发控制:锁和死锁不是理论概念
并发控制里最容易出问题的场景是两个事务同时更新同一行数据。openGauss 使用行级锁,事务 A 更新了一行没提交,事务 B 也更新这一行,B 会被阻塞直到 A 提交或回滚。如果在 gsql 里遇到长时间没反应的情况,大概率是锁等待。
-- 检查当前锁信息 SELECT * FROM pg_locks; -- 查看阻塞会话 SELECT pid, state, wait_event_type, wait_event FROM pg_stat_activity WHERE state = 'active';死锁的经典场景是事务 A 锁了表 1 等表 2,事务 B 锁了表 2 等表 1。openGauss 会自动检测死锁并回滚其中一个事务,但检测需要时间,表现为「卡了几秒然后报错」。遇到死锁不要慌,回滚后调整操作顺序,让所有事务按同一个顺序访问表,基本就能避免。
5.3 备份与恢复:备份文件放哪里、恢复时注意什么
实验八的备份与恢复,我用的最多的命令是逻辑备份,因为物理备份(gs_backup)涉及整个数据目录的归档,配置复杂,学生实验用不上。逻辑备份适合单库或单表迁移:
# 导出 db_school 数据库到 tar 文件 gs_dump db_school -p 26000 -F t -f /home/omm/db_school.tar # 恢复数据库 gs_restore -p 26000 -d db_school /home/omm/db_school.tar-F t指定导出格式为 tar,-f指定输出文件路径。恢复时有个容易忽略的点:当恢复的目标数据库不存在时,需要先CREATE DATABASE再gs_restore,否则会报database does not exist。另外,权限相关对象(用户、角色)不会随逻辑备份自动恢复,如果你导出的库里包含用户授权信息,恢复后需要手动重建用户和授权。
5.4 避坑:openGauss 实验高频问题排查记录
坑 1:切换用户时忘记加-现象:执行su omm后,输入gs_om -t start报command not found。 原因:没有加载 omm 用户的环境变量,PATH 里没有数据库工具目录。 解决:使用su - omm重新切换,或者手动执行source ~/.bashrc加载环境变量后再启动服务。
坑 2:连接数据库报端口不对现象:gsql -d postgres -p 5432连接超时或报port 5432 does not exist。 原因:openGauss 默认端口是 26000,不是 PostgreSQL 的 5432。 解决:检查gs_om -t status确认主节点端口,连接时使用-p 26000。如果实例端口做过修改,查看 postgresql.conf 里的port参数。
坑 3:建表时报交叉外键引用错误现象:建 TEACHES 表时提示relation "teachers" does not exist。 原因:TEACHES 表同时引用了 TEACHERS 和 COURSES,而这两张表此时还没建完。 解决:先建不带外键的表,全部建完后用 ALTER TABLE 统一添加约束。注意检查表名大小写——openGauss 里不带引号的表名会被转成小写,如果建表时用了大写(如"Teachers"),后续引用必须加双引号。
坑 4:删除 schema 时提示 not empty现象:DROP SCHEMA book_eg报schema book_eg is not empty。 原因:schema 下有表或其他对象,直接 drop 被安全机制拦截。 解决:使用级联删除DROP SCHEMA book_eg CASCADE,或先手动删除其中所有对象再执行 DROP。
坑 5:新用户无法查询任何表现象:CREATE USER db_user1后用该用户登录,SELECT * FROM Students报permission denied。 原因:openGauss 默认用户对非自己创建的对象没有任何权限,需要显式授权。 解决:作为 omm 用户执行GRANT SELECT ON Students TO db_user1,或者查询前用\dp查看表权限矩阵确认授权已生效。
6. JDBC 连接 openGauss:驱动参数和事务手控是最后一道关
实验九的 JDBC 连接,是整份报告书里唯一涉及代码的部分。openGauss 的 JDBC 驱动兼容 PostgreSQL 协议,但连接 URL 有自己固定的格式:
// 加载驱动 Class.forName("org.postgresql.Driver"); // 连接 URL,格式:jdbc:postgresql://host:port/database String url = "jdbc:postgresql://192.168.1.10:26000/db_school"; String user = "omm"; String password = "your_password"; Connection conn = DriverManager.getConnection(url, user, password); conn.setAutoCommit(false); String sql = "INSERT INTO SC (Sno, Cno, Score) VALUES (?, ?, ?)"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, "2021001"); ps.setString(2, "C001"); ps.setInt(3, 92); ps.executeUpdate(); conn.commit(); ps.close(); conn.close();代码里有两个参数值得说明。setAutoCommit(false)是事务手控的关键——不关掉自动提交,每一条 SQL 都会立即生效,一旦批量操作中间出错,前面的数据也要手动回滚。PreparedStatement的?占位符能防止 SQL 注入,也避免字符串拼接时的引号转义问题。
这里有一个很多人在实验九踩过的坑:openGauss 的 JDBC 驱动虽然从 PostgreSQL 协议演变而来,但连接 URL 里的端口必须和 openGauss 实例实际监听端口一致,否则报连接超时。另外,如果你用的是 openGauss 自带的驱动包opengaussjdbc.jar,Class.forName 里的驱动类名是org.opengauss.Driver,而不是 PostgreSQL 的org.postgresql.Driver,这个细节不看清楚,代码编译能过但运行必挂。
从那以后,我每次做 JDBC 连接都会先确认两件事:一是驱动包版本和数据库版本要匹配,二是setAutoCommit(false)一定放在连接创建后、第一次 SQL 执行前。顺序反了,事务边界就糊涂了。这份报告我后来又完整复现过一遍,每次都还是会撞上一两个小问题,但也正因如此,它对我来说始终是值得回翻的参考资料。希望这份拆解能让你少走几步弯路。
本文还有配套的精品资源,点击获取