简介:面向广东工业大学数据库课程的一套实验与课设资料,以openGauss实验平台为主线,并配有一个JDBC课设项目,适合正在做实验报告或需要参考数据库课设代码的学生。实验部分覆盖基本表建立、查询、索引与视图、存储过程、触发器五个环节,每项都给出了可运行的代码和相关说明,能帮助对照openGauss的语法特点完成实验。课设部分是一个Java项目,包含Java源码、class编译产物、mysql-connector-java驱动和运行截图,演示了通过JDBC连接数据库并执行操作的完整流程,日志文件也便于排查问题。压缩包共64个文件,主要类型为Java源码和class文件,另外有xml配置、图片、jar驱动、log日志等,整体大小6.93MB,目录按源码、编译、配置和图片分开,查找方便。已有2930人学习下载,对同校学生来说,既能借鉴实验思路,也能直接利用课设代码,是完成数据库课程任务的高效参考。
1. openGauss成为广工数据库实验平台:不只是换一个安装包那么简单
数据库实验在广工的课程体系里是一项需要写报告、答辩并留档的硬任务,近年以openGauss作为实验平台之后,学生首先要面对的是:安装不再靠“下一步下一步”,而是一整套用户、目录、权限、初始化参数的操作链;存储过程、触发器、事务隔离这些原本只出现在试卷里的考点,也变成了必须在命令行里亲手跑出来的结果。这篇笔记不按实验题号逐条讲,只围绕“2022年广工数据库实验+openGauss+课设”这条主线,把从零搭建实验环境、完成常见实验、写完课程设计并顺利答辩的路径走一遍。适合正在被实验报告和课设两头夹击的同学,也适合带实验的助教参考。
2. 本地搭建openGauss实验环境:从依赖、用户、初始化到gsql能跑通
2.1 为什么不要用root装,实验前先建一个专属用户
openGauss的安装脚本一直坚持:不能用root直接跑。这不是玄学,而是数据库实例一旦以root启动,数据目录和日志文件的属主就难以约束,后续任何一处权限放得过宽都会成为隐患。实验机通常是自己一个人用,很多同学图省事直接在主目录解压,结果预安装脚本第一步就退出。常见做法是先新建一个专用系统用户,例如omm,同时建好同名组dbgrp:
# 建组和用户,后面所有安装操作都用 omm 执行 groupadd dbgrp useradd -m -g dbgrp omm echo 'Lab_2022_pass' | passwd --stdin omm参数说明:-m表示创建用户主目录,-g指定初始用户组。这里把窗口期需要的实验密码写成了“Lab_2022_pass”,你可以换成自己的,但建议只用字母数字加下划线,避免特殊符号在后续命令行里总是要转义。第二步是把openGauss的环境变量写进omm的bash配置:
echo "export GAUSSHOME=/opt/software/openGauss/app" >> /home/omm/.bashrc echo "export PATH=\$GAUSSHOME/bin:\$PATH" >> /home/omm/.bashrc echo "export LD_LIBRARY_PATH=\$GAUSSHOME/lib:\$LD_LIBRARY_PATH" >> /home/omm/.bashrc source /home/omm/.bashrc这一段的意义在于:不配置PATH,gsql、gs_ctl这些命令根本找不到,安装完以后连“gsql不是内部或外部命令”这种低级报错都排查半天。实验环境刚搭时,我一般会把这三行当成固定动作,先配好再继续。
2.2 下载解压与配置文件:cluster_config.xml里最容易错的两个参数
到openGauss官方网站,按操作系统和架构下载对应版本的企业版或轻量版安装包,实验电脑建议用CentOS 7.6及以上或openEuler虚拟机,资源不低于2核4GB,内存不够的话gs_install阶段容易被OOM杀掉。下载后解压:
# 切换到 omm 用户,目录统一放在 /opt/software 下 su - omm mkdir -p /opt/software/openGauss cd /opt/software/openGauss tar -zxvf /your_download_path/openGauss-x.x.x-CentOS-64bit.tar.gz解压后根目录下会有gs_preinstall、gs_install两个脚本和一个cluster_config.xml模板。单机实验只需要把cluster_config.xml里的节点名改对。最容易翻车的两个参数是nodeNames和DEVICE里的name:这两个值必须和系统的hostname一致,而不是随便取一个“db1”。检查hostname用命令hostname,不要让它返回localhost.localdomain。配置文件修改后大致是这样:
<?xml version="1.0" encoding="UTF-8"?> <ROOT> <CLUSTER> <PARAM name="clusterName" value="dbCluster"/> <PARAM name="nodeNames" value="opengauss01"/> <PARAM name="gaussdbAppHome" value="/opt/software/openGauss/app"/> <PARAM name="gaussdbLogHome" value="/opt/software/openGauss/log"/> <PARAM name="tmpMoc" value="/tmp"/> <PARAM name="gaussdbToolPath" value="/opt/software/openGauss/tool"/> <PARAM name="corePath" value="/opt/software/openGauss/corefile"/> </CLUSTER> <DEVICELIST> <DEVICE sn="1000001"> <PARAM name="name" value="opengauss01"/> <PARAM name="azName" value="AZ1"/> <PARAM name="azPriority" value="1"/> <PARAM name="backIp1" value="192.168.1.11"/> </DEVICE> </DEVICELIST> </ROOT>参数说明:nodeNames与DEVICE的name必须等于hostname输出;backIp1填本机内网或虚拟机NAT的IP。如果虚拟机用NAT模式,IP没固定,建议先在虚拟机设置里改成静态IP或DHCP保留,否则重启后IP漂移,找半天原因。
2.3 预安装与初始化:一次跑完最少踩两个坑
预安装脚本的作用是检查依赖、初始化运行环境、做目录规划。常见做法是全自动模式:
cd /opt/software/openGauss ./gs_preinstall -U omm -G dbgrp -X /opt/software/openGauss/cluster_config.xml --non-interactive参数说明:-U指定运行用户,-G指定用户组,-X指定配置文件,--non-interactive跳过交互问答。预安装阶段如果报缺libaio-devel、flex、bison,先补齐再重跑:yum -y install libaio-devel flex bison python3。缺依赖硬跑,后面gs_install会以“初始化数据库失败”这种模糊话术崩掉,特别浪费时间。
预安装成功后执行初始化:
./gs_install -X /opt/software/openGauss/cluster_config.xml这一步会初始化数据目录、生成postgresql.conf和pg_hba.conf、设置初始用户omm的密码。初始化完成后,启动单机实例并检查状态:
gs_ctl start -Z single_node -D /gaussdb/data gs_ctl status -Z single_node -D /gaussdb/data-Z single_node表示部署形态为单节点,-D指向gs_install输出中显示的数据目录,通常为/gaussdb/data。看到“online”状态就说明实例起来了。然后第一件事是用gsql登录默认数据库postgres:
gsql -d postgres -U omm -h 127.0.0.1 -p 5432 -W参数说明:-d指定数据库名,-U指定用户名,-h指定监听地址,-p指定端口号,openGauss默认端口是5432,-W表示交互输入密码。连不上时先看gs_ctl status,再看5432端口是否有监听:ss -lntp | grep 5432。这一步能过滤出八成安装问题。
2.4 建实验库和实验账号:分清系统库和你自己要用的库
默认只有一个postgres库,所有实验都堆在里面,后面做权限实验时会互相干扰。我一般会在第一周实验就固定好一套命名:
CREATE DATABASE labdb ENCODING 'UTF8' TEMPLATE template0; CREATE USER lab_user WITH PASSWORD 'Lab_123456'; GRANT ALL PRIVILEGES ON DATABASE labdb TO lab_user;参数说明:ENCODING显式写成UTF8,并且用TEMPLATE template0,是因为template1的默认编码不一定是UTF8,直接建库容易得到中文乱码。GRANT ALL PRIVILEGES只授权到数据库级别,模式级别的授权后面做权限实验时再单独练。至此,实验环境已经能跑起来,下一步进入实验本身的拆解。
3. 数据库实验的四个必考层次:建表、增删改查、视图与事务的落地写法
3.1 建表与约束:主键、外键、CHECK约束各自的实验目的
广工数据库实验的第一个层次围绕表结构设计。不是拿一个CREATE TABLE模板复制就完事,重点是每种约束要解释清楚为什么存在。历年实验任务里出现频率最高的就是学生、课程、成绩三张表,一个能直接跑的表结构:
CREATE TABLE students ( sid CHAR(8) PRIMARY KEY, name VARCHAR(32) NOT NULL, dept VARCHAR(64) DEFAULT '计算机学院', grade INT CHECK (grade BETWEEN 2000 AND 2050) ); CREATE TABLE courses ( cid CHAR(8) PRIMARY KEY, cname VARCHAR(64) NOT NULL, credit NUMERIC(2,1) CHECK (credit > 0 AND credit <= 10) ); CREATE TABLE sc ( sid CHAR(8), cid CHAR(8), score NUMERIC(5,1) CHECK (score >= 0 AND score <= 100), PRIMARY KEY (sid, cid), FOREIGN KEY (sid) REFERENCES students(sid) ON DELETE CASCADE, FOREIGN KEY (cid) REFERENCES courses(cid) ON DELETE CASCADE );逻辑说明:PRIMARY KEY既是实体唯一标识,也是查询的入口,openGauss建主键时会自动建唯一索引;CHECK约束把非法数据挡在数据库层之外。外键的ON DELETE CASCADE是高频考点——如果不指定ON DELETE,外键默认动作会阻止删除被引用的行,实验报告里要把CASCADE和RESTRICT的差别讲清楚。
注意,主键在单机部署下生成btree索引,将来如果接触分布式部署,表还要考虑分布列,实验阶段不用管。
3.2 DML增删改查:批量插入、UPDATE子查询和分组统计的写法差异
第二个层次是DML。很多课设只做到“单表插入、单表查询”的程度,太浅。课堂实验想拿高分,至少要写批量插入:
INSERT INTO courses (cid, cname, credit) VALUES ('C001', '数据库原理', 3.0), ('C002', '操作系统', 3.5), ('C003', '计算机网络', 2.5);批量插入比逐行INSERT的优势不只是语句短,多条VALUES在一条语句内是原子性的,要么全部插入,要么全部回滚,这个特性在实验报告里值得写一笔。大规模造数据可以配合generate_series,把学生表扩到几百行:
INSERT INTO students (sid, name, dept, grade) SELECT '2022' || lpad(i::text, 4, '0'), '测试学生' || i, '计算机学院', 2022 FROM generate_series(1, 200) i;这里的lpad函数把数字i补成四位,再拼上前缀“2022”,得到“20220001”这种定长学号,避免主键不统一。
UPDATE带子查询也是实验爱考的写法,比如把所有平均分低于60的学生成绩提到60:
UPDATE sc SET score = 60 WHERE sid IN ( SELECT sid FROM ( SELECT sid, AVG(score) avg_score FROM sc GROUP BY sid HAVING AVG(score) < 60 ) t );子查询套在UPDATE的条件里,是openGauss和PostgreSQL风格的标准解法。分组统计更是必考,尤其注意GROUP BY和HAVING的分工:
SELECT s.dept, c.cid, COUNT(*) AS cnt, ROUND(AVG(sc.score),2) AS avg_score FROM students s JOIN sc ON s.sid = sc.sid JOIN courses c ON sc.cid = c.cid WHERE c.credit >= 2.5 GROUP BY s.dept, c.cid HAVING COUNT(*) >= 10 ORDER BY avg_score DESC;GROUP BY的列必须和SELECT里的非聚合列严格一致,这是数据库原理课扣分最狠的地方;HAVING里过滤的是聚合后的结果,WHERE过滤的是原始行——这两个区别每学期都有人搞反。
3.3 视图与索引:这对实验要展示的是“为什么调优不是玄学”
第三层次常见任务是创建视图和索引,再做执行计划对比。视图侧重点在于看出它不占物理存储:
CREATE VIEW v_score_overview AS SELECT s.sid, s.name, s.dept, c.cname, sc.score FROM students s LEFT JOIN sc ON s.sid = sc.sid LEFT JOIN courses c ON sc.cid = c.cid;视图本质是保存一段SQL定义,不是复制数据。实验报告里要强调“基表数据变化时视图内容实时跟着变”,这是它与物化视图的核心区别。
索引部分最好用EXPLAIN ANALYZE验证,先看没索引的情况,再建索引:
EXPLAIN ANALYZE SELECT * FROM sc WHERE cid = 'C001'; CREATE INDEX idx_sc_cid ON sc(cid); EXPLAIN ANALYZE SELECT * FROM sc WHERE cid = 'C001';第一次执行计划是Seq Scan,建索引后变成Index Scan或Bitmap Index Scan。实验报告里把两次执行计划中“actual time”的差距贴出来,比空写“索引能加速查询”有说服力得多。还需要说明一个观察结论:数据量只有几百行时,优化器可能放弃索引走全表扫描;数据量几万行时索引优势才明显。
3.4 事务与并发:两个gsql窗口能说明隔离级别的大部分问题
第四层次是事务。实验机开两个终端,一个终端begin,另一个终端查询,最能体现隔离级别:
-- 终端A BEGIN; UPDATE sc SET score = score + 1 WHERE sid = '20220001'; -- 终端B SELECT * FROM sc WHERE sid = '20220001';逻辑说明:终端A更新但不提交,终端B的SELECT会被阻塞住,这是行级锁的正常表现,不是系统卡死。接着把事务隔离级别设置为可重复读,观察快照差异:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; SELECT * FROM sc WHERE sid = '20220001';openGauss默认隔离级别是读已提交,每条语句取最新快照;可重复读下,整个事务使用同一快照,所以事务开始后其他会话提交的数据看不到。实验报告里把这两个级别在同一个业务场景下的表现对比清楚,事务这一章就稳了。注意,不要在终端A事务未提交时又在同一窗口里把事务改成其他模式,自锁会把自己卡死。
4. openGauss存储过程与触发器:从MySQL习惯迁移过来的三个关键差别
4.1 为什么openGauss的存储过程和MySQL不是一回事
很多同学先在MySQL里写了存储过程,再来openGauss里交实验,最容易踩的坑就是把MySQL的DELIMITER习惯带过来。在MySQL里必须用DELIMITER把分号冲突绕开,但openGauss的SQL解析器对CREATE FUNCTION和CREATE PROCEDURE是整体处理的,函数体内直接写BEGIN END,每条语句以分号结束不会被提前执行。所以在openGauss里写“DELIMITER $$”,反而直接报语法错误。
第二个关键差别是过程语言。openGauss与PostgreSQL同源,过程语言是PL/pgSQL,赋值用“:=”或SELECT INTO,日志输出用RAISE NOTICE,异常抛出用RAISE EXCEPTION。第三个差别是返回方式:函数必须有RETURNS类型声明,存储过程则用IN/OUT参数来交换结果。这些差别整理成一张表,实验报告可以直接参考:
| 对比点 | MySQL 存储过程 | openGauss 存储过程 |
|---|---|---|
| 定界符 | DELIMITER $$ | 不需要,直接CREATE |
| 赋值方式 | SET var = ... | var := ... / SELECT INTO |
| 日志输出 | SELECT 'text' | RAISE NOTICE 'text' |
| 抛异常 | SIGNAL SQLSTATE | RAISE EXCEPTION |
| 条件/循环 | IF...END IF、LOOP...END LOOP | 关键字基本一致 |
4.2 出题频率最高的函数:按系部统计平均分
一个能作为实验作业直接提交的函数是:输入系部名,返回该系学生的选课平均分,同时用COALESCE处理没有成绩数据的情况。可执行版本如下:
CREATE OR REPLACE FUNCTION dept_avg_score(dept_name IN VARCHAR) RETURNS NUMERIC(5,1) LANGUAGE plpgsql AS $$ DECLARE v_avg NUMERIC(5,1); BEGIN SELECT ROUND(AVG(sc.score),1) INTO v_avg FROM sc JOIN students s ON sc.sid = s.sid WHERE s.dept = dept_name; IF v_avg IS NULL THEN RAISE NOTICE '该系暂无选课成绩'; RETURN 0; END IF; RETURN v_avg; END; $$;逻辑说明:INTO子句是PL/pgSQL的关键赋值方式,把SELECT结果写进变量。如果查询返回多行,PL/pgSQL会直接报错,因此这里必须先聚合保证只有一行返回。RAISE NOTICE是openGauss的控制台输出手段,实验报告里建议把“用SELECT打印日志”的习惯改掉。
参数说明:dept_name IN VARCHAR定义了输入参数,调用时传入系名;RETURNS NUMERIC(5,1)声明返回类型;RETURN 0把NULL转成0,避免前端拿到空值还要判空。调用时直接:
SELECT dept_avg_score('计算机学院');4.3 触发器:BEFORE UPDATE日志表的完整套路
触发器实验第一步是建日志表,记录成绩表的改动,然后写触发器函数和触发器本身:
CREATE TABLE sc_log ( log_id BIGSERIAL PRIMARY KEY, sid CHAR(8), cid CHAR(8), old_score NUMERIC(5,1), new_score NUMERIC(5,1), op_type VARCHAR(8), op_time TIMESTAMP DEFAULT current_timestamp ); CREATE OR REPLACE FUNCTION trg_fn_sc_update_log() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN INSERT INTO sc_log(sid, cid, old_score, new_score, op_type) VALUES (NEW.sid, NEW.cid, OLD.score, NEW.score, 'UPDATE'); RETURN NEW; END; $$; CREATE TRIGGER trg_sc_update_log BEFORE UPDATE ON sc FOR EACH ROW EXECUTE FUNCTION trg_fn_sc_update_log();逻辑说明:BEFORE UPDATE使触发器在数据修改前执行;INSERT操作时只有NEW记录,UPDATE操作时NEW和OLD同时存在;RETURN NEW表示让原修改继续执行,如果把RETURN NEW改成RETURN NULL,原UPDATE会被取消。FOR EACH ROW是行级触发器——实验里如果误写成FOR EACH STATEMENT,一条UPDATE只触发一次,日志表里只会出现一条记录,很多人检查日志发现数量不对,问题就出在这里。
验证触发器是否生效:
UPDATE sc SET score = score + 1 WHERE sid = '20220001'; SELECT * FROM sc_log;注意,即使UPDATE把score加0,行数据没有实际变化,只要行被更新,行级触发器也会激活,日志表会多一行冗余记录。想避免这个现象,可以在函数里加一个IF NEW.score IS DISTINCT FROM OLD.score的判断,这属于加分项。老版本里触发器的EXECUTE FUNCTION写法需要改成EXECUTE PROCEDURE,按实际安装版本的兼容提示调整即可。
5. 实验与课设路上绕不过去:openGauss安装与使用中的五类报错排查
5.1 预安装脚本在check OS version时退出:主机名解析的锅
现象:执行gs_preinstall时,还没进入依赖检查就报“Checking OS version... failed”,或直接报权限类错误。
原因:脚本会解析cluster_config.xml里的nodeNames,再用hostname命令得到的机器名去/etc/hosts里做解析。很多实验机的机器名是localhost.localdomain,或者hosts里没有把IP和机器名关联在一起,解析失败就退出了。
解决:先把hostname改成一个唯一标识,再编辑hosts文件:
hostnamectl set-hostname opengauss01 vi /etc/hosts在hosts里追加一行,把本机IP和主机名对应起来,例如“192.168.1.11 opengauss01”。改完重新登录确认hostname已经生效,再重跑预安装。这是安装阶段最常见的翻车点,先查这一条能救回半小时。
5.2 gsql用127.0.0.1连不上:pg_hba.conf默认没有放行TCP
现象:gs_ctl status显示实例已online,但gsql -h 127.0.0.1连接时报“FATAL: no pg_hba.conf entry for host '127.0.0.1'”。
原因:openGauss初始化后的pg_hba.conf默认只允许本机Unix socket访问,TCP连接需要显式授权。很多同学习惯用localhost,表面看都是本机,但TCP走的是独立认证条目,没有放行就拒绝连接。
解决:编辑数据目录下的pg_hba.conf,加上两行:
host all all 127.0.0.1/32 sha256 host all all 192.168.1.0/24 sha256保存后用gs_ctl reload让配置生效。注意,不要为图方便改成trust认证,哪怕是实验环境也别交这种报告,数据库安全章节里认证方式对比就彻底没法写了。
5.3 gsql里中文变成乱码:client_encoding和server不一致
现象:向表里插入‘计算机学院’,查出来变成“¤Æ”或类似的乱码。
原因:服务端数据库字符集是UTF8,客户端工具默认使用操作系统locale,比如LANG=zh_CN.GB18030,两边编码不一致,写入和读取都错位。
解决:在gsql里执行SET client_encoding TO 'UTF8';,或者在启动gsql之前先设置环境变量:
export PGCLIENTENCODING=UTF8这个坑很容易被误写成“openGauss对中文支持不好”,实际是客户端编码没对齐。做实验时把客户端固定成UTF8,中文相关的实验就不会再出幺蛾子。
5.4 普通用户建表报permission denied for schema public:授权只给到数据库不够
现象:用lab_user连接labdb后执行CREATE TABLE,报“permission denied for schema public”。
原因:在openGauss里,public schema的可写权限默认不向普通用户开放。很多实验手册只写了CREATE USER和GRANT ALL PRIVILEGES ON DATABASE,但这个授权并不包含schema级的写权限。
解决:用omm管理员在labdb里执行:
GRANT CREATE, USAGE ON SCHEMA public TO lab_user;这行授权要和建库时的GRANT区分开:一个是数据库级权限,一个是模式级权限。实验报告里把这两个层级分别写清楚,权限章节的分不会低。如果不想开放public,也可以让lab_user建自己的schema再设置search_path,但课设时间紧张时别折腾这个。
5.5 DROP DATABASE总是失败:还有活动连接占着库
现象:执行DROP DATABASE labdb,报“database 'labdb' is being accessed by other users”。
原因:自己或助教在另一个窗口开着连到labdb的会话,openGauss不会在DROP时自动断开所有连接,必须手动终止。
解决:先查占用连接并终止,再删库:
SELECT pid, usename, application_name, state FROM pg_stat_activity WHERE datname = 'labdb'; SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'labdb' AND pid <> pg_backend_pid(); DROP DATABASE labdb;注意,第二条一定要带pid <> pg_backend_pid(),否则连自己执行命令的会话也kill掉了,DROP会跟着断开。这是比较典型但很尴尬的低级错误,排查顺序记牢:先查连接,再终止,最后删。
6. 课程设计怎么从“能跑”到“能答辩”:一个可复用的学生选课项目样板
课设题目五花八门,但评审最看重的就三件事:表结构有没有冗余、事务和存储过程有没有真实场景、性能分析是不是自己跑出来的。我一般建议用“学生选课+成绩分析”作为载体,体量不大,但能把数据库课程的考点全部带出来。
选题后按四个步骤搭:第一步,关系模式落地,学生表、课程表、选课表之外,加一个院系表消掉students表里的冗余字段,所有外键按逻辑加上,答辩时被问实体完整性和参照完整性就不慌;第二步,存储过程用真实业务,比如按系部统计平均成绩,把第4章的dept_avg_score原样扩展成“补考名单统计”;第三步,触发器放在成绩表上,做成绩修改自动写日志,演示时直接UPDATE一行再查sc_log;第四步,性能验证用数据说话,把选课表扩到几万行,在有无索引下各跑一次EXPLAIN ANALYZE,打印两张执行计划,把“actual time”的差值写进PPT。
答辩验证建议准备一张自检表:
| 维度 | 演示内容 | 必说出口的术语 |
|---|---|---|
| 表设计 | ER图到关系模式转换 | 范式、主外键、引用完整性 |
| 事务 | 两个gsql窗口同时改数据 | 原子性、读已提交、锁 |
| 存储过程 | CALL一次带参数的过程 | IN参数、RAISE NOTICE |
| 触发器 | UPDATE后查日志表 | ROW级触发、NEW/OLD |
| 性能 | 索引前后EXPLAIN对比 | Seq Scan、Index Scan、执行计划 |
最后给自己留一句“技术边界”的话:单机部署只能演示锁等待和隔离级别观察,做不了真正的分布式扩展。这不算扣分项,反而能显示你清楚实验平台边界。
做了几年实验指导后,我把一个习惯带进了所有数据库实践:每次在openGauss里操作前先想清楚要不要包事务,出问题回滚得比删数据快;每次写存储过程都顺手加一条RAISE NOTICE输出,排错时少很多黑盒猜测;每次建索引都要跑EXPLAIN验证效果再收工。这三个习惯帮我少踩了很多坑,希望帮到你。
本文还有配套的精品资源,点击获取