课程开始前,先看一段每个程序员都经历过的「没有数据库」的日子。大二课程设计,我用 CSV 文件当存储,用户表一张、订单表一张,代码里手写读写。然后陆续撞上一连串倒霉事:两个人同时跑程序,一个订单被写了两次(并发冲突);需求改了,给用户表加一列邮箱,全项目搜索 split(",") 改了三处,漏了一处,导入的数据错位;程序中途断电,CSV 写了一半,后半行是残缺的;想统计「每个城市订单数」,把整个文件读进内存自己 for 循环。每一件事单看都能修,加起来就是一台手工数据库:并发控制、模式变更、崩溃恢复、查询优化,全由我的应用代码代劳,而且每一处都写得心惊肉跳。
数据库系统(DBMS)干的事,就是把这些反复出现的苦役一次性做对,做成一个所有应用共用的专业层。本章建立全课的地图:DBMS 相比文件系统多给了什么、数据模型三代演化为什么最终是关系模型赢了、以及那套考试必考的三级模式两层映像到底在解耦什么。
文件系统给的是「按名字读写字节」,往上的一切语义自己拼。DBMS 在字节之上补了一整套服务,逐项对比:
| 能力 | 文件系统时代你自己干 | DBMS 替你干 |
|---|---|---|
| 数据组织 | 自定义格式,程序间各猜各的 | 统一的模式(表、约束) |
| 完整性 | 手写校验,总有漏网 | 主键、外键、检查约束,数据库强制 |
| 并发 | 自己加文件锁,常出事 | 事务 + 锁/MVCC(第 7 章) |
| 崩溃恢复 | 自己设计备份和校对 | 日志重放(第 8 章) |
| 查询 | 全量读进内存自己过滤 | 声明式 SQL,优化器挑执行计划(第 9 章) |
这张表里最值钱的一行可能是最后一句:你只说「要什么」,不说「怎么拿」。SQL 是声明式的,「统计每个城市的订单数」就是一句 SELECT city, COUNT(*) ... GROUP BY city,数据在磁盘上怎么摆、要不要用索引、先过滤还是先聚合,优化器的事。这条分工是关系模型胜利的根基,后面几章反复回头。
「数据模型」指数据怎么组织、联系怎么表达。商用数据库走过三代,理解它们各自的死法,比背年代更有用:
层次模型(1960s):数据是树,父子指针,天生一对多。IMS 至今还在部分银行的核心系统里跑。死穴在多对多:一个学生选多门课、一门课有多个学生,树装不下,只能复制节点制造冗余,改一条数据要记得改所有副本。
网状模型(1960s~70s):把树放宽成图,任意两个记录类型之间都能拉指针,表达力够了。代价是程序员直接在指针网里导航:「从 A 记录沿客户链走到第三个,再跳订单指针」——写出来的程序和具体存储布局焊死,布局一改,程序重写。
关系模型(1970, Codd):一切皆二维表,数据之间的联系不再用指针,而用值表达(学生表里存了系编号,系表里有这个编号,值相等即关联)。这一步的解放意义怎么强调都不过分:应用只见「表和值」,看不见任何物理指针,数据怎么存、怎么串、挪到哪,应用毫无感知。
我第一次读懂 Codd 那篇论文的逻辑时,印象最深的其实是它的胆量:把「高效」从程序员的职责里拿走(指针导航没了,直觉上会变慢),全押在「优化器会比我聪明」上。历史证明这笔赌赢了,今天的优化器拿着统计信息和十几年积累的执行策略,绝大多数场景确实赢过手写指针。Codd 后来拿了图灵奖。
NoSQL 浪潮(2000s 后)在关系模型之外重开了文档、键值、图几个流派,它们不是来推翻关系的,是在「放弃部分一致性换取水平扩展」这个新约束下的特化解。第 10 章对比,此处按下。
ANSI/SPARC 把数据库的「长相」切成三层,层与层之间用映像缝合:
外模式 视图 A │ 视图 B │ 视图 C ← 各应用看到的样子
↑ 外模式/模式映像(逻辑独立性)
模式 全库唯一的逻辑结构(表、列、约束)
↑ 模式/内模式映像(物理独立性)
内模式 实际怎么存:索引、行格式、文件组织
两层映像各管一种独立性:逻辑独立性指表结构变了(拆表、加列),改一下映像,应用用的视图不用动;物理独立性指存储侧折腾(加索引、换文件组织),模式层面完全无感。这段定义背下来就三分,但它的思想值得多看一眼:这是软件工程「依赖倒置」在数据领域的原生版本,分层抽象、稳定接口、隔离变化,1978 年的数据库文献已经写得清清楚楚。
一个日常的对应物帮你记忆:CREATE VIEW student_v AS SELECT 学号, 姓名 FROM ...,这就是外模式;CREATE INDEX 改的是内模式;而 CREATE TABLE 改的是模式。三个 DDL 对应三层,动手敲一遍,三层立刻不抽象。
最后看一眼数据库软件内部的分工,后面九章其实就是在这些部件里各转一圈:
应用
│ SQL
▼
查询处理器(解析器 → 优化器 → 执行器) ← 第 2~4 章(SQL 语言)、第 9 章(优化)
▼
事务管理器(锁、MVCC、日志) ← 第 7 章(并发)、第 8 章(恢复)
▼
存储管理器(缓冲池、文件组织、索引) ← 第 9 章(索引)
▼
磁盘
外加一本数据字典(存表结构、约束、视图定义这些「关于数据的数据」)。后续章节预告一句:第 7 章的事务和第 8 章的恢复是 DBMS 真正难啃的硬骨头,也是它和「文件 + 应用代码」拉开差距的核心竞争力。
sqlite3 test.db 后建一张表、插两行、查询。体验一下「没有服务进程的数据库」,想想它牺牲了什么(提示:并发写)。SELECT * 全字段依赖,应用 B 只用视图。模式变更后谁要改代码?用两层映像的语言解释。