简介:这份《数据库原理与应用(第四版)》PDF是计算机专业学习数据库基础的实用教材,适合正在学习数据库原理的本科生、备考期末考试或自学数据库技术的读者。内容系统梳理了数据与数据库、数据库管理系统、数据库系统组成、数据模型、三级模式与两级映像等核心知识点,并对数据库安全性、视图更新、DBA职责等常见考点作了复习式归纳,可帮助读者在短时间内建立完整的数据库知识框架。包体为1个PDF文件,压缩包整体仅399KB,方便下载后随时查阅。资源已有4854人浏览学习,适合配合课程讲解用于考前冲刺、概念复习或入门自学,尤其是需要掌握数据模型、模式结构和数据库系统组成等高频考点的人群。内容预览中还覆盖了选择题、填空题、问答题等典型复习题型,便于对照知识点进行查漏补缺。
1. 数据库原理与应用第四版 PDF:一本教材背后的完整知识边界
"大学教材"这个标签,很容易让从业者低估《数据库原理与应用(第四版)》这类 PDF 的价值。目录翻下来,关系代数、ER 图、范式、事务并发,每个词都眼熟,但真被问到"为什么要这么写查询""两个事务同时改一行会发生什么",很多人答不到点子上。这本教材把数据库原理里最稳定的部分——关系模型、设计方法、事务理论——按顺序铺开,这些内容二十年没怎么变,恰恰是 MySQL、达梦、人大金仓这些具体产品背后共同的底座。它适合两类人:想系统补一遍原理但没耐心的开发,以及准备数据库面试却只会背概念的候选人。读法不是从头翻到尾,而是按"建模→查询→事务"三条线对照实际数据库逐个验证。至于向量数据库这类新话题,教材本身不会展开,但索引与查询的底层逻辑仍然是这套原理的延伸,吃透原书再迁移过去并不难。
2. 关系模型与关系代数:教材的第一个分水岭
2.1 为什么已经在写 SQL,还要回头读关系模型
关系模型的价值不在"表"这个形态,而在它的数学约束。Codd 把 Relation 定义为集合:元组无序、无重复、每个属性落在确定域上。这些约束现在看像是理论上的清规戒律,却直接决定了 SQL 的语义。SELECT DISTINCT之所以存在,是因为关系语义不允许重复元组;ORDER BY只改变查询结果的展示顺序,不代表表里有物理顺序,这两点直接影响你对索引和分页的理解。
我一般会建议把教材里"关系的基本性质"一节反复读几遍。主键为什么不能为空,外键为什么能被数据库约束检查,NULL 为什么不能跟空字符串画等号,答案都在这一节。最容易翻车的是 NULL 的语义:NULL 表示未知,跟它做任何比较的结果也是未知,所以 WHERE 条件遇到 NULL 会整行被过滤。排查线上数据"少了几行"的时候,十有八九是这里出了问题,而不是查询写错。
还有一个常被忽略的差异:关系代数是集合运算,而 SQL 实际执行是多重集运算。SELECT不加DISTINCT时结果允许重复行,因为 SQL 走了 bag 语义而非 set 语义。教材在讲投影时说"结果要去重",但 MySQL 不加DISTINCT就不去重。面试问UNION和UNION ALL的区别时,本质就是问这两种语义的取舍:前者去重,后者保留全部行。
2.2 五种基本运算与 SQL 语句的对应关系
关系代数是 SQL 的语义基础。教材讲五种基本运算:选择、投影、并、差、笛卡尔积,其中乘积配合条件可以演化出连接。把它们逐一对上 SQL,写复杂查询时你对优化器在背后做什么就会更清晰。下面这张表是教材第二章到 SQL 之间的"翻译表",建议贴在工位旁边。
| 关系运算 | 符号 | 对应 SQL | 作用 |
|---|---|---|---|
| 选择 | σ | WHERE | 按条件过滤行,不改变列集合 |
| 投影 | π | SELECT 列清单 | 按列裁剪,可加 DISTINCT 去重 |
| 并 | ∪ | UNION | 两个关系纵向合并,隐式去重 |
| 差 | − | EXCEPT / NOT EXISTS | 保留左集合中不在右集合的行 |
| 连接 | ⋈ | JOIN | 按连接条件横向拼接两个关系 |
2.3 用数据库增删改查的视角验证关系代数
把教材结论搬到真实数据库里验证,最快的方式是对着增删改查各写一遍。下面这组 SQL 可以在商品表和订单明细表上直接跑,MySQL 和达梦都兼容:
-- 选择运算:过滤价格大于 100 的商品行,列保持不变 SELECT * FROM product WHERE price > 100; -- 投影运算:只取两列,DISTINCT 体现关系"无重复元组"的集合特征 SELECT DISTINCT category_id, price FROM product; -- 连接运算:订单明细关联商品,补上商品名称 SELECT oi.order_id, p.product_name, oi.quantity FROM order_item oi JOIN product p ON oi.product_id = p.product_id WHERE oi.quantity >= 2; -- 选择下推:先过滤再连接,缩小中间结果第一句对应选择运算,WHERE 条件里的列有没有索引,决定它是全表扫描还是索引过滤。第二句对应投影,DISTINCT 去重背后有一次排序或哈希操作,列多时开销不小,没必要就别加。第三句最值得琢磨:WHERE 里的 quantity 条件可以先于 JOIN 执行,这叫选择下推。优化器通常会自动做这件事,但如果你写的是子查询,某些数据库不一定能把条件推导到最内层,手动把过滤条件挪近表引用,执行计划往往更快。三条命令都跑一遍,关系代数"运算的封闭性"——运算结果还是关系、还能继续参加运算——就变成直觉了。
提示:在 MySQL 里执行
EXPLAIN看执行计划时,关注select_type和possible_keys两列,能直接观察选择下推是否真的生效。
3. 从 ER 图到关系模式:数据库设计落地路径
3.1 建模顺序与三要素判断
教材里数据库设计的标准路线是:需求分析 → 概念结构设计(ER 图)→ 逻辑结构设计(关系模式)→ 物理设计。实际项目和课程设计里,大家总想跳过后两步直接建表,结果就是表建到一半发现缺字段、多冗余,返工成本比画图高得多。我第一次做订单模块时就吃过大意亏,漏了中间表,后面每个查询都在拼接脏数据。
画 ER 图时抓住三个要素:实体、属性、联系。判断一个对象应该建模成实体还是属性,标准是它有没有独立描述的必要。订单金额是订单的属性,但优惠券有自己的一堆字段,就应该独立成实体。联系则必须判断基数:1:1、1:N、M:N,这三种情况转关系模式时的处理方式完全不同,是最容易出错、也是面试最常考的点。
M:N 联系必须拆成中间表,这是课程设计里最常见的遗漏。学生选课、订单和商品、角色和权限,都是 M:N。漏拆中间表的后果很典型:要么 R 端信息在表中重复存储多行,要么无法记录关联发生时点的数量、时间等附加属性。记住一个口诀:M:N 一旦存在,中间表就是唯一正解,附加属性全部放到中间表上。
3.2 订单系统的 ER 转关系模式实例
用一个最小订单系统说明转换规则。实体包括客户、订单、商品;客户和订单是 1:N,订单和商品是 M:N,所以订单明细是中间实体。转换方式如下表,之后建表顺序按"主表 → 从表 → 中间表"执行,保证外键引用的表先存在。
| 联系 | 基数 | 转换方式 |
|---|---|---|
| 客户-订单 | 1:N | 在 N 端(订单表)加外键 customer_id |
| 订单-商品 | M:N | 拆出中间表 order_item,存两个外键和数量 |
| 客户-会员卡 | 1:1 | 外键放在任意一端,通常放访问频繁的会员卡表 |
CREATE TABLE customer ( customer_id INT PRIMARY KEY, -- 客户主键,关系模型要求的完整性 customer_name VARCHAR(64) NOT NULL ); CREATE TABLE orders ( -- 订单,N 端放外键 order_id INT PRIMARY KEY, customer_id INT NOT NULL, -- 外键列,对应 1:N 的 N 端 order_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_orders_customer FOREIGN KEY (customer_id) REFERENCES customer(customer_id) ); CREATE TABLE order_item ( -- M:N 中间表,联合主键 order_id INT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL DEFAULT 1, price DECIMAL(10,2) NOT NULL, -- 快照价格,商品改价不影响历史订单 PRIMARY KEY (order_id, product_id), FOREIGN KEY (order_id) REFERENCES orders(order_id), FOREIGN KEY (product_id) REFERENCES product(product_id) );代码里三个参数细节值得说明。order_item用(order_id, product_id)联合主键,防止同一商品在同一订单里重复录入,这是 M:N 中间表的标准写法。price字段存下单时的快照价,而不是去商品表实时读,历史数据不可变是交易系统的硬约束。外键约束默认RESTRICT,删除父记录时如果存在引用会被拒绝,避免数据悬空。真实系统里为了写入性能会去掉部分外键,但设计阶段保留它们能让约束关系显式化,这是教材和工程实践的主要差异点。
3.3 范式判断步骤与反范式场景
范式是检验关系模式好坏的标准。实际工作中判断到 3NF 就够用,BCNF 和更高范式主要出现在面试题里。判断步骤我习惯固定成三问:有没有重复组(1NF),非主属性是否完全依赖主键(2NF),非主属性之间有没有传递依赖(3NF)。
一个典型的 2NF 违反例子:订单明细表里同时存 customer_name,客户名称依赖订单而不是订单明细,属于对联合主键的部分依赖,拆出去才对。3NF 违反则是员工表里同时存部门编号和部门地址,部门地址传递依赖员工主键。判断时先找主键,再列所有非主属性与主键的依赖关系,把不满足的列挪到它真正依赖的那张表。这套动作熟练之后,看任何一张表都能在三秒内说出它破坏了几条范式。
反范式不是不讲范式,而是明确知道代价后的选择。报表查询需要跨五张表 JOIN 时,预聚合列或状态冗余能把响应时间从秒级降到毫秒级,代价是写入时要维护一致性,通常用应用层双写或定时任务补偿。教材强调规范化,工程强调权衡,两个视角合起来才是完整的判断力。
4. 事务、并发锁与恢复:原理里最难嚼、系统里最要命的部分
4.1 ACID 不是口号,是日志与锁的分工
教材关于事务的章节,核心是 ACID 四个性质和它们在真实数据库里的落地方式。隔离性靠锁和版本控制,持久性和原子性靠日志。最常见的学习误区是把 ACID 背成口号:原子性不是"操作要么全做要么全不做"这么简单,它依赖 undo log 在失败时回滚;持久性依赖 redo log 先落盘再做数据页修改,这就是预写日志 WAL 的基本思路。
理解日志之后,故障排查就有方向了。数据库崩溃重启后,redo log 保证已提交事务不丢,undo log 保证未提交事务被回滚。教材里"日志先行"那一页,值得画一张时间线图推演:事务提交时什么先写、什么后写,崩溃点落在不同位置时恢复结果分别是什么。把这个推演做一遍,比背十遍"ACID 是什么"有用得多,面试问"commit 之后断电会不会丢数据"时,你能直接说出关键参数和落盘顺序,而不是含糊回答"应该不会"。
4.2 隔离级别与锁的粒度对照
四种隔离级别是教材与数据库面试题交汇最密集的部分。脏读、不可重复读、幻读三种异常,分别对应隔离级别的递进。实现机制上,基于锁的数据库用读锁、写锁和锁的持有时间来区分;而 MySQL InnoDB 用 MVCC 版本链实现快照读。下面这张表把异常、级别和常见实现一条线串起来。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 常见实现 |
|---|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 | 读不加锁 |
| READ COMMITTED | 避免 | 可能 | 可能 | 语句级快照,读锁即时释放 |
| REPEATABLE READ | 避免 | 避免 | 可能 | 事务级快照(MVCC) |
| SERIALIZABLE | 避免 | 避免 | 避免 | 加锁读或串行化执行 |
注意一个容易答错的细节:MySQL 的 REPEATABLE READ 用 MVCC 快照加 next-key lock,在绝大多数场景下避免了幻读,所以它敢拿这个级别当默认值;Oracle 和 PostgreSQL 默认是 READ COMMITTED,达梦数据库的默认隔离级别也是读提交。面试里如果说"MySQL RR 会幻读"并不严谨,更准确的表述是"在 RR 下通过间隙锁实现了可重复读,同时消除了大部分幻读"。
-- 事务级设置隔离级别,MySQL 语法 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; SELECT amount FROM account WHERE id = 1 FOR UPDATE; -- 排他锁,锁定行 UPDATE account SET amount = amount - 100 WHERE id = 1; COMMIT;FOR UPDATE是排查并发写问题时第一个要认识的语法。它给命中的行加排他锁,直到事务结束才释放,其他事务的更新会被阻塞。如果不加锁读,在 READ COMMITTED 下两次读取之间别的会话改了数据,就会看到不可重复读;换成 REPEATABLE READ 的快照读,则能稳定读到同一版本。锁的粒度也要心里有数:行锁、间隙锁、表锁的冲突范围和性能开销完全不同,间隙锁只出现在 RR 及以上级别。
4.3 数据库死锁与锁等待的排查命令
并发实操里真正头疼的数据库死锁。死锁是事务之间互相持有对方需要的锁形成等待环,MySQL 检测到死锁会立刻回滚其中一个事务,所以你看到的是 ERROR 1213 而不是挂死;真正让系统变慢的通常是长时间的锁等待,大量堆积之后就是连接池耗尽。排查时按下面两步走:
-- 第一步:查看当前锁等待关系,确认谁在等谁的锁 SELECT * FROM performance_schema.data_lock_waits\G -- 第二步:死锁发生后的现场分析,重点看 LATEST DETECTED DEADLOCK 段 SHOW ENGINE INNODB STATUS\GSHOW ENGINE INNODB STATUS输出的死锁段会记录双方执行的最近一条 SQL、持有的锁类型和等待的锁类型。看的时候优先找两条 SQL 的加锁顺序:事务 A 先更新 order 再更新 order_item,事务 B 相反,死锁就发生在交叉点上。解决方向是让所有事务按相同顺序加锁,其次把大事务拆小,减少锁的持有时间。Oracle、达梦的排查思路一致,只是视图名不同,达梦可以通过等锁监控视图定位阻塞源会话。
注意:调小
innodb_lock_wait_timeout只能让锁等待报错更早暴露,不能解决死锁本身;根治要靠减少加锁范围和控制事务长度。
5. 把教材结论转成数据库面试答案与验证技巧
5.1 高频问题的教材化回答口径
数据库面试题里,很多题直接长在教材目录上。三道最典型的:为什么数据库用 B+ 树做索引,四种隔离级别怎么区分,范式设计到第几范式够用。回答口径我在前面各章已经给出,这里补一个更通用的思路:任何一道"为什么"题,都要落到数据访问路径和代价上。B+ 树矮、支持范围扫描、叶节点顺序链接,这就是索引题答案的骨架;隔离级别题说出来哪个异常被哪个级别解决,再补一句 MVCC 实现,就已经超过大部分候选人。
5.2 用北风数据库复现教材案例
教材给的实例数据库,最常用的是北风数据库(Northwind),它有客户、订单、商品、供应商一整套表结构,天然适合练手。下载 SQL 脚本导入 MySQL 之后,按下面几步验证教材结论:
-- 1. 验证外键约束:删除被订单引用的客户,预期报错或提示有依赖 DELETE FROM customers WHERE customer_id = 'ALFKI'; -- 2. 验证 3NF 设计:统计每个客户的累计订单金额 SELECT c.customer_id, c.company_name, SUM(od.unit_price * od.quantity) AS total FROM customers c JOIN orders o ON c.customer_id = o.customer_id JOIN order_details od ON o.order_id = od.order_id GROUP BY c.customer_id, c.company_name ORDER BY total DESC;第一条命令如果成功删除,说明导入脚本时外键约束没生效,应该补开SET FOREIGN_KEY_CHECKS=1再试,顺便观察级联行为和RESTRICT的区别。第二条命令把三张表串起来,同时检验连接条件、聚合和分组逻辑,是订单分析报表的雏形。这套 SQL 在达梦、人大金仓和 Oracle 上同样通用,差别只在字段类型细节,这也验证了教材一句话:SQL 标准是跨产品的最大公约数。
5.3 课程设计搭不上线时的解法
做数据库课程设计时最常卡住的是第一步:题目拿到手不知道建几张表。回到教材的方法:先画 ER 图,确定实体间基数,再转关系模式,最后写建表和增删改查。全程用一个本地 MySQL 就够,配合 DBeaver 这类客户端看执行计划,教材里每个原理都能在半小时内验证一遍。这个闭环走通之后,再去做数据库同步、连接池调优这些具体工程问题,你会清楚地知道它们在原理体系中处于什么位置,而不是东拼西凑找答案。
本文还有配套的精品资源,点击获取