第 1 章 · 数据库系统概述

课程开始前,先看一段每个程序员都经历过的「没有数据库」的日子。大二课程设计,我用 CSV 文件当存储,用户表一张、订单表一张,代码里手写读写。然后陆续撞上一连串倒霉事:两个人同时跑程序,一个订单被写了两次(并发冲突);需求改了,给用户表加一列邮箱,全项目搜索 split(",") 改了三处,漏了一处,导入的数据错位;程序中途断电,CSV 写了一半,后半行是残缺的;想统计「每个城市订单数」,把整个文件读进内存自己 for 循环。每一件事单看都能修,加起来就是一台手工数据库:并发控制、模式变更、崩溃恢复、查询优化,全由我的应用代码代劳,而且每一处都写得心惊肉跳。

数据库系统(DBMS)干的事,就是把这些反复出现的苦役一次性做对,做成一个所有应用共用的专业层。本章建立全课的地图:DBMS 相比文件系统多给了什么、数据模型三代演化为什么最终是关系模型赢了、以及那套考试必考的三级模式两层映像到底在解耦什么。

1.1 DBMS 和文件系统差在哪

文件系统给的是「按名字读写字节」,往上的一切语义自己拼。DBMS 在字节之上补了一整套服务,逐项对比:

能力文件系统时代你自己干DBMS 替你干
数据组织自定义格式,程序间各猜各的统一的模式(表、约束)
完整性手写校验,总有漏网主键、外键、检查约束,数据库强制
并发自己加文件锁,常出事事务 + 锁/MVCC(第 7 章)
崩溃恢复自己设计备份和校对日志重放(第 8 章)
查询全量读进内存自己过滤声明式 SQL,优化器挑执行计划(第 9 章)

这张表里最值钱的一行可能是最后一句:你只说「要什么」,不说「怎么拿」。SQL 是声明式的,「统计每个城市的订单数」就是一句 SELECT city, COUNT(*) ... GROUP BY city,数据在磁盘上怎么摆、要不要用索引、先过滤还是先聚合,优化器的事。这条分工是关系模型胜利的根基,后面几章反复回头。

1.2 数据模型三代史:为什么是表赢了

「数据模型」指数据怎么组织、联系怎么表达。商用数据库走过三代,理解它们各自的死法,比背年代更有用:

层次模型(1960s):数据是树,父子指针,天生一对多。IMS 至今还在部分银行的核心系统里跑。死穴在多对多:一个学生选多门课、一门课有多个学生,树装不下,只能复制节点制造冗余,改一条数据要记得改所有副本。

网状模型(1960s~70s):把树放宽成图,任意两个记录类型之间都能拉指针,表达力够了。代价是程序员直接在指针网里导航:「从 A 记录沿客户链走到第三个,再跳订单指针」——写出来的程序和具体存储布局焊死,布局一改,程序重写。

关系模型(1970, Codd):一切皆二维表,数据之间的联系不再用指针,而用值表达(学生表里存了系编号,系表里有这个编号,值相等即关联)。这一步的解放意义怎么强调都不过分:应用只见「表和值」,看不见任何物理指针,数据怎么存、怎么串、挪到哪,应用毫无感知。

我第一次读懂 Codd 那篇论文的逻辑时,印象最深的其实是它的胆量:把「高效」从程序员的职责里拿走(指针导航没了,直觉上会变慢),全押在「优化器会比我聪明」上。历史证明这笔赌赢了,今天的优化器拿着统计信息和十几年积累的执行策略,绝大多数场景确实赢过手写指针。Codd 后来拿了图灵奖。

NoSQL 浪潮(2000s 后)在关系模型之外重开了文档、键值、图几个流派,它们不是来推翻关系的,是在「放弃部分一致性换取水平扩展」这个新约束下的特化解。第 10 章对比,此处按下。

1.3 三级模式与两层映像:解耦的教科书

ANSI/SPARC 把数据库的「长相」切成三层,层与层之间用映像缝合:

外模式    视图 A │ 视图 B │ 视图 C      ← 各应用看到的样子
             ↑ 外模式/模式映像(逻辑独立性)
模式      全库唯一的逻辑结构(表、列、约束)
             ↑ 模式/内模式映像(物理独立性)
内模式    实际怎么存:索引、行格式、文件组织

两层映像各管一种独立性:逻辑独立性指表结构变了(拆表、加列),改一下映像,应用用的视图不用动;物理独立性指存储侧折腾(加索引、换文件组织),模式层面完全无感。这段定义背下来就三分,但它的思想值得多看一眼:这是软件工程「依赖倒置」在数据领域的原生版本,分层抽象、稳定接口、隔离变化,1978 年的数据库文献已经写得清清楚楚。

一个日常的对应物帮你记忆:CREATE VIEW student_v AS SELECT 学号, 姓名 FROM ...,这就是外模式;CREATE INDEX 改的是内模式;而 CREATE TABLE 改的是模式。三个 DDL 对应三层,动手敲一遍,三层立刻不抽象。

1.4 DBMS 的内脏:五个部件

最后看一眼数据库软件内部的分工,后面九章其实就是在这些部件里各转一圈:

应用
 │ SQL
▼
查询处理器(解析器 → 优化器 → 执行器)     ← 第 2~4 章(SQL 语言)、第 9 章(优化)
▼
事务管理器(锁、MVCC、日志)              ← 第 7 章(并发)、第 8 章(恢复)
▼
存储管理器(缓冲池、文件组织、索引)        ← 第 9 章(索引)
▼
磁盘

外加一本数据字典(存表结构、约束、视图定义这些「关于数据的数据」)。后续章节预告一句:第 7 章的事务和第 8 章的恢复是 DBMS 真正难啃的硬骨头,也是它和「文件 + 应用代码」拉开差距的核心竞争力。

常见错误

  • 把「数据库」和 DBMS 混用。数据库是数据的集合,DBMS 是管理它的软件(MySQL、PostgreSQL 是 DBMS)。口语不追究,报告和论文里要分清。
  • 以为三级模式是三个数据库。是同一个库的三种「视图粒度」,一套数据,三种描述层次。
  • 用文件系统干了数据库的活。数据量一大、并发一来就现原形。SQLite(单文件嵌入式库)是「轻量但不牺牲事务」的底线选择,比手写 CSV 强百倍。

练习

  1. 动手:装 SQLite(零配置,单文件),sqlite3 test.db 后建一张表、插两行、查询。体验一下「没有服务进程的数据库」,想想它牺牲了什么(提示:并发写)。
  2. 手算:某表加了列,应用 A 用 SELECT * 全字段依赖,应用 B 只用视图。模式变更后谁要改代码?用两层映像的语言解释。
  3. 思考:CSV + 应用代码方案里,「查询」慢在哪一层?数据量到多大时手写遍历会明显扛不住?给一个数量级估计。
  4. 思考:关系模型用「值相等」表达联系,代价是查询时要做连接(join)。网状模型指针直达没有这个开销,为什么最后还是值赢了?从「数据独立性」和「优化器」两个角度各说一句。