如果你今天是自己搭了个数据库表,明天就被数据一致性、并发写入、权限混乱的问题追着跑,那这篇文章就是帮你把“数据库管理系统”这块地基一次补齐的。
很多人花了很多时间学 SQL 语法,却始终没想清楚一个问题:数据库管理系统(DBMS)到底在系统里承担什么角色?它凭什么能把数据托管得比文件更安全、更高效、更可靠?
这篇文章围绕“数据库管理系统基础”做一次系统梳理。从文件系统的痛点出发,讲清楚数据库、数据库管理系统、数据库系统之间的层次关系,再拆解关系模型、SQL 实操、事务与并发控制,最后给出常见误区与工程建议。无论你是刚开始学数据库的初学者,还是用过一段时间但基础不牢的开发者,这篇文章都适合收藏后认真读一遍。
1. 这篇数据库基础文章,到底在讲什么
先给读者画个像。你大概是这三类人之一:
- 计算机专业学生:正在上数据库系统概论课,概念听懂了,但不知道每个概念对应真实系统里的什么环节。
- 转行开发者:已经会写 CRUD,但没系统学过数据库原理,面试时一被问到“事务隔离级别”“范式设计”就心虚。
- 初级后台开发:项目里因为缺约束、缺事务、权限混乱吃过亏,想回头把基础补扎实。
本文的主要内容,并不是教你在某个数据库里执行多少条花哨的 SQL,而是帮你建立一套理解数据库的框架。框架一旦建立,你再看 MySQL、PostgreSQL、Oracle,会发现它们本质上都遵循同样的核心逻辑:用统一的模型组织数据,用标准化的语言操纵数据,用并发控制和恢复机制保证数据始终处于可靠状态。
需要提醒的是,标题里提到的 Neso Academy 数据库系统基础公开课,是很好的外部补充资源。它的特点是用系统的计算机视角讲数据库,概念推进很稳。你可以把它和理解这篇博客搭配着看:视频负责建立直觉,本文负责给出一份可检索、可落地、带实操的速查笔记。两者结合,效果会好很多。
2. 为什么需要数据库管理系统:先看清文件系统的瓶颈
想理解数据库管理系统为什么会出现,最好的方式是先看没有它的时候,程序员是怎么存数据的。
早期阶段,数据通常以文件形式保存。业务简单时,一个 CSV 文件、一个 JSON 文件就能满足需求。但一旦数据量变大、业务逻辑变复杂,文件存储会迅速暴露出一堆问题。
2.1 数据冗余与更新异常
同一个信息可能被复制到多个文件中。比如客户姓名同时出现在订单文件、物流文件、售后文件中。当客户改名时,你需要把所有文件都改一遍。一旦漏改,同一个客户在不同系统里就会出现不同的名字。这就是数据冗余带来的更新异常。
2.2 并发访问难以控制
两个用户同时修改同一个文件,后写入的人很可能直接覆盖先写入的人。没有锁机制、没有事务隔离,程序只能依赖“尽量少同时操作同一份文件”的侥幸。在真实业务里,这种侥幸大概率会变成事故。
2.3 程序与数据结构强耦合
文件格式一旦变动,所有读取该文件的程序都要跟着改。如果字段顺序、分隔符、编码方式发生变化,涉及的程序可能多达几十处。这种耦合让系统维护成本居高不下。
2.4 缺少统一的安全管理
文件系统的权限控制粒度很粗,通常只能控制到“文件能不能读、能不能写”。但你往往希望做到“这个用户只能读某些列,不能改某些表”。传统文件方式很难做到。
数据库管理系统就是为了解决这些问题而生的软件系统。它不只是在“存数据”这件事上做得更好,而是把抽象提到了数据层:你按照逻辑结构定义数据、操作数据,物理存储和访问细节由 DBMS 负责。这种抽象,才是它最核心的价值。
小结论:文件系统适合存储简单、低并发、无强一致要求的静态数据;一旦业务需要共享、并发、权限控制和可靠性保证,就到了引入数据库管理系统的时刻。
3. 数据库、数据库管理系统、数据库系统:三个概念别再用混
很多文章把这几个词混着用,导致初学者越看越糊涂。其实这三个概念是不同层次的东西。
| 概念 | 英文 | 含义 | 通俗类比 |
|---|---|---|---|
| 数据库 | DataBase,DB | 存储在计算机内,有组织、可共享的数据集合 | 仓库里的货物本身 |
| 数据库管理系统 | DataBase Management System,DBMS | 位于用户与操作系统之间的数据管理软件,负责定义、创建、维护、操纵数据库 | 仓库里的货物管理系统 |
| 数据库系统 | DataBase System,DBS | 数据库 + 数据库管理系统 + 应用程序 + 数据库管理员 + 用户 | 整个仓库运营体系,包括人、货、设备、流程 |
这里要展开讲一下。
数据库(DB)强调的是数据本身。它是按照某种数据模型组织起来的,长期存储在计算机内,可以被多个用户共享的数据集合。比如“学生信息库”里存着学生表、课程表、选课表,这些数据集合在一起,就是数据库。
数据库管理系统(DBMS)是软件,是管理数据库的工具。MySQL、Oracle、PostgreSQL、SQL Server,这些都是 DBMS。它们负责建表、查数据、加索引、管权限、做事务、出备份,所有这些能力都属于 DBMS 的职责范围。
数据库系统(DBS)的范围最大。除了数据库和 DBMS,还包括应用系统、数据库管理员(DBA)以及终端用户。一个正在运行的在线教育系统,背后有数据库、有 MySQL 实例、有业务应用、有运维人员,这一整套可以称为数据库系统。
平时大家口语说“我们用 MySQL 存数据”,其实是省略了“关系型数据库管理系统”这个完整名称。MySQL 不是“数据库”本身,至少要创建一个 database 之后,才有人在里面真正存放数据。
这种概念辨析并不是扣字眼。理解三个概念的边界,你在阅读官方文档、参与架构评审、讨论数据迁移时,才能和同伴使用同一套语言。
4. 数据库管理系统的内部构成:到底由什么在支撑运行
如果你把数据库管理系统当作一个黑盒,那它输入的是 SQL 语句,输出的是数据结果。但作为一个合格的开发者,至少要知道黑盒内部有哪几个关键模块,出了问题才能定位。
4.1 两大核心组件
从功能上划分,数据库管理系统内部可以分成查询处理器和存储管理器两大部分。
| 模块 | 主要子部件 | 职责 |
|---|---|---|
| 查询处理器 | DDL 编译器、DML 编译器、查询优化器 | 把 SQL 翻译成可执行计划,并选择代价更低的执行路径 |
| 存储管理器 | 权限与完整性管理器、事务管理器、缓冲区管理器、文件管理器 | 负责数据在磁盘和内存之间的读写,维护约束与权限,管理事务的提交与回滚 |
查询处理器做的事情,可以通过一条查询语句来理解:
SELECT * FROM student WHERE major_id = 1;这条 SQL 到达数据库后,DBMS 会先做语法解析、语义检查,生成逻辑计划,然后交给优化器生成物理执行计划。优化器决定是走索引还是全表扫描,是使用嵌套循环连接还是哈希连接。这些决策直接决定了你的 SQL 跑得快还是慢。
存储管理器做的事情,发生在你执行写入操作时。它要把数据写进缓冲区,再由缓冲区刷到磁盘文件;它要检查插入的数据是否违反非空约束、唯一约束、外键约束;它还要记录事务日志,保证系统突然崩溃后可以恢复。
没有这些底层模块,你执行一条 INSERT 语句就可能破坏整个数据库的一致性。很多开发者在业务中遇到“数据怎么多了”“数据怎么丢了”,查到最后往往就是某个底层机制没理清楚。
4.2 三层模式结构与两级映射
理解数据库管理系统,还有一个绕不开的结构:三层模式。
- 外模式:也叫用户模式或子模式,是用户能看到和操作的数据视图。一个数据库可以有多个外模式,对应不同用户的不同需求。
- 概念模式:也叫逻辑模式,描述数据库中全部数据的逻辑结构。它处于中间层,是数据库设计者视角下的全局逻辑结构。
- 内模式:也叫存储模式,描述数据在物理存储介质上的存储方式和访问方式。
两层映射是连接它们的桥梁:
- 外模式/概念模式映射:保证概念模式发生变化时,外模式可以保持不变,从而应用程序不必修改。这叫逻辑独立性。
- 概念模式/内模式映射:保证内模式发生变化时,比如存储引擎更换、数据文件重新组织,概念模式不受影响。这叫物理独立性。
理解三层模式和两级映射后,你就会明白一个常见问题的答案:为什么业务系统可以比较平滑地从 Oracle 迁移到 PostgreSQL?因为应用通过 SQL 访问的是外模式,只要逻辑模式不变,底层物理存储怎么调整,对应用层面是透明的。这就是数据库管理系统抽象能力的一个直接体现。
5. 关系模型与完整性约束:数据不混乱的根本保证
在所有数据模型中,关系模型是当前商业数据库的主流。它由 E. F. Codd 在 1970 年提出,理论基础是集合论和关系代数。关系模型受欢迎,是因为它足够简单:数据组织成二维表,表之间通过公共属性建立联系,查询可以用非过程化的 SQL 表示,用户不需要关心底层访问路径。
5.1 关系模型的基本术语
- 关系:一张二维表。
- 元组:表中的一行。
- 属性:表中的一列。
- 候选键:能唯一标识一个元组的最小属性集合。
- 主键:从候选键中选定一个,用于唯一标识每一行。
- 外键:一个关系中的属性,它引用另一个关系的主键,用于表达关系之间的连接。
- 域:属性的取值范围。
新手最容易犯的错误,是把主键简单理解为唯一索引。主键不仅要求唯一,还要求不能为空。这是关系模型对数据完整性的一个基本保证。
5.2 三种完整性约束
完整性约束是关系模型最有价值的部分,也是很多实际项目中被低估的部分。
| 约束类型 | 名称 | 规则 | 违反后果示例 |
|---|---|---|---|
| 实体完整性 | Entity Integrity | 主属性不能取空值,且取值唯一 | 插入两行相同 student_id 的数据 |
| 参照完整性 | Referential Integrity | 外键取值要么为空,要么等于被引用表中某行的主键值 | 学生记录了不存在的专业编号 |
| 用户定义完整性 | User-defined Integrity | 非空、唯一、CHECK 条件等业务规则 | 年龄列被写入负数 |
没有约束时,数据库可以往里塞任何数据。比如学生表里插入一条记录,major_id 指向一个根本不存在的专业。单条数据看着不碍事,但做统计报表时,关联不上专业表的数据就会变成脏数据,并且很难追溯。
有约束时,数据库就在源头拒绝了不合规的写入。这才是真正的数据安全。很多团队对约束有误解,觉得外键影响性能就不用,这其实是把性能优化和正确性对立的错误方式。正确的做法是先保证正确性,再通过索引、缓存、分区等手段解决性能瓶颈。
完整性约束的设计,应该在建表阶段完成,而不是等项目上线后再补。数据库领域有一条规律:在源头拒绝坏数据,永远比事后清洗坏数据便宜得多。
6. SQL 分层与完整实操示例
SQL 是关系数据库的标准操作语言。它内部又分成多个子语言,不同子语言负责不同职责。
| 子语言 | 全称 | 职责 |
|---|---|---|
| DDL | Data Definition Language | 定义库、表、索引、视图等结构 |
| DML | Data Manipulation Language | 插入、更新、删除数据 |
| DQL | Data Query Language | 查询数据 |
| DCL | Data Control Language | 控制权限与访问范围 |
| TCL | Transaction Control Language | 控制事务提交、回滚 |
下面用一个“学生选课管理”的小场景,走一遍常用 SQL。
6.1 用 DDL 建库建表,约束在源头设计
以 MySQL 8.0+ 为例,但下面语句在 5.7 也能运行。如果本地没有 MySQL,可以在 Docker 中快速启动一个示例:
docker run --name mysql-demo \ -e MYSQL_ROOT_PASSWORD=root123456 \ -e MYSQL_DATABASE=student_db \ -p 3306:3306 \ -d mysql:8.0创建数据库和表:
CREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARACTER SET utf8mb4; USE student_db; CREATE TABLE major ( major_id INT PRIMARY KEY AUTO_INCREMENT, major_name VARCHAR(64) NOT NULL UNIQUE, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE student ( student_id CHAR(8) PRIMARY KEY, name VARCHAR(32) NOT NULL, gender ENUM('M', 'F') NOT NULL, major_id INT NOT NULL, enroll_year YEAR NOT NULL, CONSTRAINT fk_student_major FOREIGN KEY (major_id) REFERENCES major(major_id) );这段 DDL 有几个关键点需要理解:
PRIMARY KEY同时保证唯一和非空,这是实体完整性。NOT NULL和UNIQUE属于用户定义完整性,直接约束业务规则。FOREIGN KEY将 student 表和 major 表连接起来,保证 student 中的 major_id 一定是 major 表中真实存在的专业,这是参照完整性。- 字符集使用
utf8mb4,避免中文和特殊字符出现乱码。
6.2 用 DML/DQL 写数据、查数据
插入数据:
INSERT INTO major (major_name) VALUES ('软件工程'), ('网络工程'), ('大数据'); INSERT INTO student (student_id, name, gender, major_id, enroll_year) VALUES ('20230001', '张同学', 'M', 1, 2023), ('20230002', '李同学', 'F', 2, 2023), ('20230003', '王同学', 'M', 3, 2024);查询数据:
SELECT s.student_id, s.name, m.major_name FROM student s JOIN major m ON s.major_id = m.major_id WHERE m.major_name = '软件工程';预期输出:
+------------+-----------+-------------+ | student_id | name | major_name | +------------+-----------+-------------+ | 20230001 | 张同学 | 软件工程 | +------------+-----------+-------------+JOIN 的作用是拿到两张表相关联的数据。如果当初不设置外键,这里的关联条件完全依赖于人工约定,一旦数据被写坏,查询结果就会不准确。
尝试插入一条违反外键约束的数据:
INSERT INTO student (student_id, name, gender, major_id, enroll_year) VALUES ('20230099', '错误同学', 'M', 999, 2024);数据库会拒绝执行,并返回外键约束失败的错误。这个拒绝行为,就是完整性约束在起保护作用。
6.3 用 DCL 控制权限,遵守最小权限原则
生产环境不建议业务服务直接使用 root 账号连接数据库。更合理的做法是,为不同服务创建不同账号,只授予必要权限。
-- 创建只读账号 CREATE USER 'app_read'@'%' IDENTIFIED BY 'StrongP@ss2025'; -- 只允许查询 student_db 下的所有表 GRANT SELECT ON student_db.* TO 'app_read'@'%'; -- 创建读写账号 CREATE USER 'app_write'@'%' IDENTIFIED BY 'AnotherP@ss2025'; GRANT SELECT, INSERT, UPDATE, DELETE ON student_db.* TO 'app_write'@'%'; -- 刷新权限 FLUSH PRIVILEGES;这里要特别强调:权限控制不是可有可无的规范,而是安全底线。只读账号即使泄露,攻击者也无法篡改数据;业务账号即使出错,影响范围也被限制在指定库表内。DCL 的使用,应该从第一天学数据库就开始养成习惯。
6.4 用 TCL 管理事务,保证数据的一致性
以账户转账为例,事务控制语句如下:
START TRANSACTION; UPDATE account SET balance = balance - 500 WHERE account_id = 1001; UPDATE account SET balance = balance + 500 WHERE account_id = 1002; COMMIT;第一条 UPDATE 成功后、第二条 UPDATE 还没执行时,如果系统突然崩溃,数据库会自动回滚,账户 1001 的扣款不会遗留。手动回滚的写法是:
ROLLBACK;事务是数据库可靠性的基石。后面单独用一章展开讲。
7. 事务与并发:数据库可靠性的最后防线
事务是数据库管理系统保证数据一致性的核心机制。它把一组操作打包成一个不可分割的执行单元:要么全部成功,要么全部失败。
7.1 事务的 ACID 特性
| 特性 | 英文 | 含义 |
|---|---|---|
| 原子性 | Atomicity | 事务内所有操作作为一个整体,不能只执行一部分 |
| 一致性 | Consistency | 事务执行前后,数据库都处于合法状态,完整性约束满足 |
| 隔离性 | Isolation | 并发执行的事务之间互不干扰 |
| 持久性 | Durability | 事务提交后,数据修改永久保存 |
很多人背得出 ACID,但真正设计系统时却不用事务。比如批量修改数据时逐条 UPDATE,中途一条失败,前面已经改掉的数据无法自动回滚,最终留下半成品数据。这就是没有正确使用事务的典型问题。
7.2 并发事务带来的三类问题
两个事务同时操作同一份数据时,会出现以下问题:
| 问题 | 描述 |
|---|---|
| 脏读 | 一个事务读到另一个事务未提交的数据。如果对方回滚,读到的就是错误的临时数据 |
| 不可重复读 | 一个事务内两次读取同一行数据,结果不同。原因是另一个事务在期间修改并提交了该行 |
| 幻读 | 一个事务内两次执行同一查询,结果集的行数不同。原因是另一个事务在期间插入了新行 |
7.3 隔离级别
数据库管理系统通过隔离级别来解决并发问题。SQL 标准定义了四个隔离级别,从低到高:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 不可能 | 可能 | 可能 |
| REPEATABLE READ | 不可能 | 不可能 | 可能 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 |
隔离级别越高,一致性越强,并发能力往往越弱。MySQL InnoDB 引擎默认使用 REPEATABLE READ;PostgreSQL 默认使用 READ COMMITTED。实际项目中要根据业务对一致性的要求做选择,而不是盲目追求最高隔离级别。
高隔离级别下,事务等待和死锁的概率也会上升。遇到死锁时,数据库管理系统会自动检测并牺牲一个事务,让它回滚,从而让另一个事务继续执行。开发者需要做的是,尽量把事务持续时间缩短,避免大事务长时间占用资源。
8. 数据库系统基础常见误区与排查思路
下面这些误区在初学者和早期工程师中非常常见。整理成表格,方便查阅。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 主键允许插入 NULL | 混淆唯一索引与主键的定义 | 检查表结构,查看约束定义 | 主键同时要求非空、唯一,设计表时严格约束 |
| 关联查询查不到数据 | 外键数据不一致,存在孤儿数据 | 使用 LEFT JOIN 找出未匹配记录 | 在源头增加外键约束,必要时清理已有脏数据 |
| 查询越来越慢 | 缺少索引,SQL 走了全表扫描 | 执行 EXPLAIN 查看执行计划 | 为常用查询条件建立合适的索引 |
| 数据更新后出现半成品状态 | 多条 DML 语句没有放在同一事务里 | 查看业务日志,检查事务控制语句 | 用事务包裹多条操作,异常时回滚 |
| 两个事务互相等待,全部失败 | 并发更新同一批数据导致死锁 | 查看数据库锁等待日志 | 调整事务访问顺序,缩短事务时间 |
| 业务账号权限过大,误删数据 | 使用 root 账号直连业务库 | 审计账号权限与连接方式 | 按最小权限原则创建专用账号 |
| 线上执行 DDL 导致锁表 | 表数据量大,DDL 在线变更受限 | 查看数据库 DDL 执行状态与锁等待 | 使用在线 DDL 工具,低峰期执行并做好备份 |
排查数据库问题,第一步永远是看日志。MySQL 的错误日志、慢查询日志、事务回滚日志,都是定位问题的第一手资料。第二步用 EXPLAIN 分析执行计划,判断 SQL 是否走了预期索引。第三步查看锁等待和事务状态,判断是否存在并发冲突。
不要一上来就猜、就改配置,那样往往会把问题弄得更隐蔽。
9. 数据库学习路线与工程实践建议
9.1 初学者建议按这条路径走
学数据库管理系统基础,不要急于追分布式、不要一上来就看分库分表。先按下面顺序建立体系:
- 概念层:理解数据库、DBMS、DBS 的区别,掌握三层模式和两级映射。
- 关系模型层:熟悉关系、元组、主键、外键、完整性约束。这是所有关系数据库的基础。
- SQL 实操层:在一台本机 MySQL 或 PostgreSQL 上完成建库、建表、增删改查、权限控制。
- 事务与并发层:动手演示脏读、不可重复读、幻读,理解隔离级别的差异。
- 设计层:学习 ER 模型和范式理论,练习把一个业务场景设计成三范式表结构。
- 性能层:学习索引原理,用 EXPLAIN 分析 SQL 执行计划。
每一步都要配合动手实验。只看书、只背定义,数据库是学不会的。推荐自己做一个最小选课系统,包含学生、课程、选课记录三张表,然后把约束、事务、索引、视图全用一遍。做完之后,你对数据库基础的理解会明显上一个台阶。
9.2 工程实践建议
- 规范命名:表名、字段名使用统一风格,比如小写字母加下划线,避免在代码中到处大小写转换。
- 备份先行:任何涉及生产数据的变更,都要先确认备份和回滚方案。
- 变更评审:DDL 变更尽量通过工单流程评审,不要直接在生产环境执行。
- 权限最小化:业务账号永远不要使用 root,权限范围越小越好。
- 事务短而快:避免在事务里做远程调用、耗时的文件操作,事务时间越短,锁竞争越小。
- 留好监控:至少掌握慢查询日志和连接数监控,这是发现数据库问题的第一个窗口。
数据库系统的工程能力和基础概念是相辅相成的。理论让你知道边界在哪里,工程实践让你知道这个边界在真实系统中意味着什么。
10. 写在最后
数据库管理系统不是“装个 MySQL、会写几条 SQL”这么简单。它是一整套关于数据组织、约束、安全、并发和恢复的系统性方案。
这篇文章帮你把几件关键事串了起来:文件系统为什么会被数据库替代,数据库管理系统内部有哪些模块,关系模型和完整性约束为什么能保证数据不混乱,SQL 各个子语言分别负责什么,事务和隔离级别怎么保护系统并发时的可靠性,以及初学者最容易踩的坑。
建议你别把本文只当科普读。打开终端,建一个学生选课库,把主键、外键、非空约束、唯一约束、事务提交和回滚全部敲一遍。真正跑通之后,你再看任何一本数据库教材、任何一门公开课,都会有完全不同的体感。
数据库的基础决定了你能走多远。把这块补齐,比追任何新技术都更值得。