ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

悉尼大学DBMS课程实战指南:从SQL到事务恢复的底层认知

悉尼大学DBMS课程实战指南:从SQL到事务恢复的底层认知 简介悉尼大学数据库管理系统课程资料内容覆盖数据库核心理论与应用适合正在学习数据库原理的高校学生、准备课程考试或希望系统复习数据库管理系统的技术人员。压缩包共65个文件大小约17.04MB以PDF课件和带答案的练习册为主体同时包含多个SQL脚本、PPT讲义等辅助材料。资料按周组织从概念建模、关系模型、关系代数与SQL查询到事务处理、并发控制、完整性约束、存储索引、查询处理和数据库规范化均有对应讲解与习题且练习多附参考答案便于对照练习与查漏补缺。其中还包含复习周材料和整体复习题可帮助考前系统梳理。无论是理解ACID属性、事务隔离还是完成复杂SQL查询都能借助这些材料得到有效训练。已有191人学习是一份理论与实践结合较紧密的数据库学习资料。1. 悉尼大学这门DBMS课程为什么值得你把数据库地基重打一遍对绝大多数写业务代码的人来说数据库就是“会写SQL、会建索引、挂了会重启”直到线上出现死锁、数据对不上、恢复不回来才意识到自己缺的不是工具经验而是数据库管理系统层面的底层认知。悉尼大学的这门Database Management System课程通常对应INFO2120/INFO2820编号体系恰好是把这个缺口补上的典型训练它不是让你背语法而是逼着你从关系模型、存储结构、事务并发一路走到恢复机制在一学期内把DBMS的黑匣子拆开看一遍。内容密度和作业强度都高于普通“数据库应用课”愿意照着它的脉络啃下来的人无论是去做后端、做数据平台还是做数据库内核底子都会扎实很多。这篇文章按课程主线拆出可执行的落地路径结合我在本地环境复现课程练习时的经验讲清楚每一步怎么走、参数怎么设、哪些地方容易翻车。2. 课程主线拆解关系模型、SQL 与 ER 建模到底在考什么2.1 用 PostgreSQL 跑通课程 SQL 练习最小环境与命令集课程前半段会密集覆盖关系代数、SQL DDL/DML、约束与视图。作业里最常出现的场景是给你一个业务描述让你建表、写查询再针对特定查询说明执行顺序。很多同学在 pgAdmin 或 Navicat 里点鼠标习惯了到考试和作业里反而写不利索原生的 SQL。我的建议是整个学期都用命令行 psql 纯 SQL 脚本完成练习训练强度完全不同。# 创建课程练习专用库owner 用自己的系统用户 createdb -h localhost -p 5432 -U $USER info2120_lab # 进入交互终端实际上 psql 会读取 ~/.pgpass 里的密码配置 psql -h localhost -p 5432 -U $USER -d info2120_lab-- 建一张符合课程常用场景的成绩表学生选课记录 CREATE TABLE enrollments ( student_id INTEGER NOT NULL, course_code CHAR(8) NOT NULL, semester CHAR(6) NOT NULL, grade NUMERIC(3,1), PRIMARY KEY (student_id, course_code, semester), CHECK (grade IS NULL OR (grade 0 AND grade 100)) ); -- 插入验证约束 INSERT INTO enrollments VALUES (1001, INFO2120, 2024S2, 85.5); INSERT INTO enrollments VALUES (1001, INFO2120, 2024S2, 120); -- 违反 CHECK会被拒绝这里的核心不是建表语法而是让你理解“约束是数据完整性的一部分”。PRIMARY KEY决定唯一性CHECK把非法数据挡在入库之前这些在课程后面的并发与恢复章节里会反复依赖。psql 终端里判断 SQL 是否执行成功看返回的INSERT 0 1或错误码就可以作业判题器也是这么判断的。实际练习时我习惯把每道作业题写成独立的.sql文件再用psql -f批量执行。这样做的好处是改错重跑成本低而且提交作业时只需要打包文件不需要截图证明。命令行执行还有一个容易被忽视的好处——你会更早意识到 SQL 的事务边界。默认情况下 psql 每条语句自动提交如果你在一段脚本里先删表再插入中间某条失败前面已经提交的操作回不去。课程作业里经常要你对比“自动提交”和“手动BEGIN ... COMMIT”的行为差异这恰恰是后面事务章节的前置实验。2.2 ER 图转关系模式三个必须检查的映射点课程期中前后会进入数据库设计环节作业典型形态是给你一段校园场景描述比如图书馆借阅、课程注册让你画 ER 图再转成关系模式。这里翻车最多的不是画图而是从 ER 图到关系模式的映射不完整。我常用的检查清单是三条实体必须有主键、关系两端都要有外键落入、多对多必须拆成交叉表。-- 以“学生-课程-教师”场景为例多对多关系 student_course 必须单独成表 CREATE TABLE student ( student_id INTEGER PRIMARY KEY, student_name VARCHAR(50) NOT NULL ); CREATE TABLE course ( course_id INTEGER PRIMARY KEY, course_name VARCHAR(100) NOT NULL, teacher_id INTEGER NOT NULL REFERENCES teacher(teacher_id) ); -- 交叉表学生与课程的多对多关系落在这里 CREATE TABLE student_course ( student_id INTEGER REFERENCES student(student_id), course_id INTEGER REFERENCES course(course_id), enroll_date DATE NOT NULL DEFAULT CURRENT_DATE, PRIMARY KEY (student_id, course_id) );参数说明上要注意三点一是外键列的数据类型必须与主键完全一致INTEGER和BIGINT混用会让 PostgreSQL 在运行时抛出类型不匹配错误二是复合主键的列顺序要看查询模式作业或多或少的评分规则是不看你的顺序但(student_id, course_id)比反过来的写法更适合按学生查选课三是命名要带明确语义student_course比sc更容易在后面的连接查询里减少差错。实际上这门课并不会要求你用特定工具画 ER 图draw.io 或者手画都可以提交 PDF 即可。但我的经验是每画一个关系立刻写下对应的 SQL DDL画图与建表同步推进可以有效避免“图上有关系、表里没外键”的经典翻车。2.3 规范化理论从函数依赖到 3NF 的判定步骤规范化是课程里理论性最强也最容易懵的部分。作业里经常直接甩给你一个R(A, B, C, D)加若干函数依赖让你判断属于第几范式并分解到 3NF 或 BCNF。很多同学靠背定义遇到题目变形就垮。我按课程要求整理成一套固定流程先找候选键再看非主属性对候选键的依赖类型最后判断是否存在传递依赖或部分依赖。具体步骤是第一步根据函数依赖集求出闭包确定候选键第二步列出所有非主属性第三步检查是否存在非主属性对候选键的部分依赖存在则至少是 1NF不存在再判断有没有传递依赖第四步如果所有非主属性都完全直接依赖候选键则达到 3NF。注意BCNF 要求“所有依赖左侧都是超键”这个条件比 3NF 更严课程考试里经常拿 BCNF 和 3NF 的区别做区分题。-- 规范化过程不适合用 SQL 直接验证但可以用约束来检验分解结果的关系模式 -- 比如将 R(A, B, C, D) 分解为 R1(A, B, C) 与 R2(A, D) CREATE TABLE r1 ( a INTEGER PRIMARY KEY, b INTEGER NOT NULL, c INTEGER NOT NULL ); CREATE TABLE r2 ( a INTEGER PRIMARY KEY, d INTEGER NOT NULL, FOREIGN KEY (a) REFERENCES r1(a) );分解成两个表后连接查询仍能恢复到原始数据这就是无损连接。课程作业里会要求你给出分解过程并验证是否满足无损连接与依赖保持。用 SQL 建表来“模拟”分解结果能帮你直观感受连接代价。一个值得留意的坑是分解到 3NF 时依赖保持容易满足分解到 BCNF 时则可能丢失函数依赖考试和作业都偏好让你论证这个取舍。3. 复刻课程数据库设计项目从需求分析到可运行原型3.1 课程设计作业的常见形态与选题逻辑课程后半段通常有一个占比较高的团队项目从零设计一个数据库系统并用 SQL 实现。历年常见选题不外乎书店库存、健身房会员、医院预约这类经典业务。这类项目的核心不是功能多炫而是考察你是否完整走了一遍“需求分析 → ER 建模 → 关系模式 → 建表约束 → 查询视图”的流程。我见过太多组把时间花在写花哨的 Java 界面上结果 ER 图里实体关系都理不清楚分数照样不高。我的观点是项目选题越“土”越好。选择自己熟悉业务场景能把精力留给约束设计、索引策略和事务边界而不是花大量时间去猜业务规则。作业文档里通常只给需求概述具体规则需要你们自己补充比如“预约不能冲突”“库存不能为负”这类业务约束最终都要落到 CHECK 约束或事务逻辑上。-- 以“健身房会员与课程预约”为例核心业务规则落地成约束 CREATE TABLE member ( member_id INTEGER PRIMARY KEY, member_name VARCHAR(50) NOT NULL, level VARCHAR(10) DEFAULT normal ); CREATE TABLE class_schedule ( schedule_id INTEGER PRIMARY KEY, class_name VARCHAR(50) NOT NULL, capacity INTEGER NOT NULL CHECK (capacity 0), current_count INTEGER NOT NULL DEFAULT 0 CHECK (current_count 0) ); CREATE TABLE booking ( booking_id INTEGER PRIMARY KEY, member_id INTEGER NOT NULL REFERENCES member(member_id), schedule_id INTEGER NOT NULL REFERENCES class_schedule(schedule_id), booking_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE (member_id, schedule_id) );这里的UNIQUE (member_id, schedule_id)解决“同一会员不能重复预约同一节课”的问题capacity与current_count两列组合实现“满员不可预约”的业务规则。实际在项目里减少可用名额和插入预约记录必须放在一个事务里完成否则并发下会出现超卖——这块内容课程后半段的并发控制章节会专门讲。3.2 用 Python SQLite 快速搭一个可运行原型项目交付通常要求“数据库设计文档 SQL 脚本 可运行程序”。不少学生会选 Java MySQL 的组合这本身没错但如果你只是想快速验证设计是否合理Python SQLite 是我体验下来最顺的组合SQLite 支持完整的 DDL/DML 语法Python 内置驱动不需要额外装服务端作业提交时附带一个.db文件即可。import sqlite3 # 连接数据库文件不存在时会自动创建 conn sqlite3.connect(gym.db) conn.execute(PRAGMA foreign_keys ON) # 关键SQLite 默认不检查外键必须手动开启 cur conn.cursor() # 创建核心表 cur.execute( CREATE TABLE IF NOT EXISTS class_schedule ( schedule_id INTEGER PRIMARY KEY, class_name VARCHAR(50) NOT NULL, capacity INTEGER NOT NULL CHECK (capacity 0), current_count INTEGER NOT NULL DEFAULT 0 CHECK (current_count 0) ) ) # 插入数据并提交 cur.execute(INSERT INTO class_schedule (class_name, capacity) VALUES (?, ?), (Yoga, 10)) conn.commit() conn.close()Python 代码里必须注意两点。第一PRAGMA foreign_keys ON必须放在每次连接的开始执行因为 SQLite 的外键检查默认关闭不开的话你在 Python 层删除一个被引用的会员booking 表里的记录不会被拦截完整性约束形同虚设。第二使用?占位符传参而不是字符串拼接这既是防注入的基本功也是课程强调的“参数化查询”实践。提交项目时数据库文件会带着你测试过的数据如果不想让老师看到你的脏测试数据提交前写个清理脚本把测试行删掉。3.3 从本地原型到课程验收索引与视图的加分配置课程项目验收时老师常会现场跑几个高频查询并问你“这个查询为什么快/慢”。因此提前给外键列和查询条件列建索引是必须做的功课。我的做法是根据作业文档里的查询需求反推索引而不是给每列都加索引。-- 高频查询按会员查预约记录外键列 member_id 需要索引 CREATE INDEX idx_booking_member ON booking(member_id); -- 高频查询按课程查预约量统计 CREATE INDEX idx_booking_schedule ON booking(schedule_id);参数选择上索引列的顺序也很讲究。如果查询条件是WHERE member_id ? AND schedule_id ?那么单个复合索引(member_id, schedule_id)通常优于两个单列索引。但如果你还要统计某节课的预约人数schedule_id单列索引反而是更直接的选择。课程不会要求你做基准测试但你要能讲清楚“为什么建这个索引”背后的选择逻辑。视图是另一个容易被忽略的加分项。把复杂的多表连接封装成视图既能让程序代码更简洁也是关系模型“逻辑独立性”的直接体现。我一般会在项目文档里单独写一节“视图设计理由”把每个视图对应的业务需求列清楚。4. 事务、并发与恢复DBMS 深水区为什么是拿分关键4.1 ACID 在课程作业里怎么被检验事务章节是这门课的分水岭也是作业难度跳变最大的地方。前面 SQL 练习靠熟练能拿满分事务题目则必须真正理解 ACID 每个字母的含义并且能在具体场景里指认哪条语句破坏了原子性哪个隔离级别下会出现脏读。课程作业常见的考察方式是给你一个转账场景让你分析不同隔离级别下的执行结果。用BEGIN TRANSACTION包裹多条 SQL这是实现原子性的基本手段但要问一句如果中途COMMIT失败数据库保证回滚吗答案是只要语句在执行过程中发生错误事务会进入 aborted 状态必须显式ROLLBACK否则后续语句全部被拒绝。-- 转账事务从 1001 账户扣款向 1002 账户入账 BEGIN; UPDATE account SET balance balance - 500 WHERE account_id 1001; -- 如果执行到这里时 1001 余额不足CHECK 约束触发整个事务必须回滚 UPDATE account SET balance balance 500 WHERE account_id 1002; COMMIT;这个例子里的关键点不是 SQL 本身而是约束与事务的联动。如果balance 0的 CHECK 约束在扣款语句上失败事务会进入 aborted 状态你需要手动ROLLBACK否则连接会被锁住后续所有语句都返回错误。实际做课程实验时不少同学在这里卡了很久以为是数据库坏了其实是没回滚。4.2 隔离级别与锁用两个并发查询验证读现象并发章节里课程会重点讲读已提交与可重复读之间的差异。PostgreSQL 默认隔离级别是读已提交这也是我们做验证实验时最常配置的环境。-- 会话 A开启事务并修改数据但不提交 BEGIN; UPDATE account SET balance balance 100 WHERE account_id 1001; -- 此时不 COMMIT保持事务打开 -- 会话 B在另一连接执行查询 SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT balance FROM account WHERE account_id 1001;在默认的读已提交级别下会话 B 看到的是修改前的数据因为 A 还没提交但 A 持有行锁此时会话 B 如果试图修改同一行会被阻塞直到 A 提交或回滚。这就是课程作业里最常见的锁等待场景。要验证可重复读与读已提交的区别需要把 B 也放进一个事务里连续查询两次期间 A 提交修改观察 B 两次查询结果是否一致。PostgreSQL 在可重复读级别下使用快照隔离两次查询结果一致而在读已提交下两次结果可能不同。这里的参数调整集中在SET TRANSACTION ISOLATION LEVEL语句修改只在当前事务内生效。对课程实验来说关键是理解“不同隔离级别解决不同异常”不要求把四种级别全背下来但脏读、不可重复读、幻读三个现象要能用实验复现。4.3 WAL 日志与恢复从崩溃里把数据找回来恢复机制是课程里最“黑匣子”的部分但恰恰也是很多线上事故的关键。课程通常会用 PostgreSQL 的 WAL 机制来讲日志先行与重做/撤销过程。做实验时最有价值的是观察pg_wal目录下的日志文件变化以及对比不同fsync配置下的崩溃恢复表现。PostgreSQL 中控制崩溃恢复行为的核心参数是fsync与synchronous_commit这些参数写在postgresql.conf里。默认配置下fsyncon每次提交都会把 WAL 刷到磁盘确保崩溃后不丢数据如果把fsyncoff性能会变快但断电后可能丢最近提交的事务。课程实验里我一般不建议关掉 fsync 去“测性能”因为丢数据的结果不可控这个开关在生产环境更是不能随便动。实际做恢复验证有一个更安全的办法用pg_ctl stop -m immediate模拟崩溃再重启数据库后检查是否丢失已提交的数据。但要注意-m immediate会跳过正常关闭流程相当于拔电源重启后实例会进入恢复模式这时候不要急着连接数据库等日志里的 “database system is ready” 出现再操作。5. 悉尼大学 DBMS 课程的高频踩坑点与排查路径5.1 本地连接串写错psql 连不上课程服务器现象psql 输入密码后报connection refused或password authentication failed但确认密码没记错。原因一是服务器监听地址没包含你连的那个 IP二是pg_hba.conf里的认证方式与客户端请求不匹配。解决连接前先检查服务器端listen_addresses是否为*再确认pg_hba.conf中对应条目把md5或scram-sha-256写上作业一般要求用学校提供的虚拟机镜像本地自建 PostgreSQL 时这些参数最容易漏。5.2 索引没生效查询计划器不用你的索引现象建了索引后跑EXPLAIN ANALYZE发现还是Seq Scan作业问“为什么索引没生效”。原因表数据量太小优化器认为顺序扫描比索引扫描更快或者查询条件里对索引列做了函数计算。解决数据量只有几百行时不用纠结索引是否生效课程报告里你只需解释“数据规模小时优化器选择顺序扫描更优”即可如果确实想看到索引扫描可以临时调低enable_seqscanSET enable_seqscan off;但记得这只是实验手段不是生产调优做法。5.3 函数依赖求错候选键整个范式判断全歪现象给定函数依赖后闭包求出来的候选键和别人不一样导致后面 2NF/3NF 判断全部算错。原因闭包运算漏掉了依赖的传递性比如 A→B、B→C 时闭包里少了 C。解决按课程教的算法一步步求F闭合集合不要跳步。我习惯写一个小脚本列出依赖集逐个求闭包再对照候选键定义其实手工慢慢推一两次之后后面就顺畅了。5.4 外键约束在 SQLite 里失效现象Python SQLite 项目里删除父表记录子表数据还在约束像没写一样。原因SQLite 默认不启用外键检查。解决每次连接都执行PRAGMA foreign_keys ON如果用的是 SQLAlchemy则要在连接字符串里配置事件监听器确保每次连接都自动开启。这也是我前面特别强调这条参数的原因。5.5 元组锁定导致死锁现象两个事务各更新一行然后交叉更新对方的行数据库抛出deadlock detected。原因课程实验里为了教死锁概念特意让你制造这种场景。解决看到死锁错误不要慌回滚其中一个事务再以固定顺序获取锁。课程报告里把它写成“通过约定更新顺序来避免死锁”属于标准答案实际生产中这也是常见做法。6. 自学本课程的正确姿势用一份作业驱动整条知识链如果你不在悉尼大学但想按这门课的体系自学最有效的方式不是从头读教材而是给自己布置一个“数据库设计作业”选一个小区停车位预约场景从 ER 图开始一路做到事务与恢复实验把这门课的知识点全部串起来。我当年就是用“健身房预约”做完了这一整套收获比刷两遍教材都大。做完之后找三件事来验证自己是否真的掌握第一把 ER 图给一个不懂数据库的朋友看他能看懂业务规则说明建模成功第二用EXPLAIN ANALYZE分析自己的高频查询至少能给每个查询讲出执行计划里的一个开销来源第三模拟一次崩溃恢复并复盘看数据是否完整日志里能否找到恢复路径。我的一个习惯是每个章节结束后用 markdown 写一份两百字的“我在这里犯了什么错”比笔记更值钱。全部做完你会发现后续再写业务代码时关于事务边界、约束设计、索引选择的判断会快很多遇到线上数据问题也不会只会上网搜sqlmap fingerprint这类报错信息盲目试错而是能从数据库运行机制去排查。希望这份梳理能帮你少踩一些没必要踩的坑也让你在这个方向上投入的时间真正有回报。本文还有配套的精品资源点击获取
返回列表