1.1 关系型数据库基础


1.1 关系型数据库基础

本节摘要:关系型数据库用二维表组织数据,用键建立表间联系,用完整性约束守住数据质量。本节把表、元组、属性、域、主键、外键这六个底层概念逐一拆开,它们是全书每一次评审讨论的通用语言。位置:全书起点,先有这套词汇,后面的设计与优化才有讨论的地基。

从一次失败的数据合并说起

2019 年我接过一个图书借阅系统的二开需求。前任开发者把读者数据存在一个文本文件里,每行一条,逗号分隔。系统跑了一年,需求来了:统计每个校区借书最多的前十名读者。就这么一个需求,让维护者折腾了整整两天——因为有人昵称里带逗号,有人手机号后面多了个空格,还有三百多条记录的"借出日期"写成"3月5号"而不是标准格式。数据能查,但不敢信;能统计,但要先人工清洗。

这个故事的教训不是"文本文件不好",而是:当数据有了结构、有了关系、有了并发修改,就需要一套比文件系统更严格的组织纪律。关系型数据库就是这套纪律的工程化实现。它用三样东西建立纪律:一张张结构统一的二维表(结构统一)、表与表之间通过键连接(关系显式声明)、字段级别的完整性约束(非法数据进不来)。MySQL 是这套纪律目前世界上最流行的开源实现。

六个必须说准的术语

关系模型由 E.F. Codd 在 1970 年提出,数学根基是集合论。教科书术语和日常叫法有偏差,评审会上说错了会显得不专业,这里一次对齐:

术语 日常叫法 准确定义
关系 Relation 一个二维结构,行无序、列无序、行不重复
元组 Tuple 行/记录 表中的一条数据
属性 Attribute 列/字段 表中的一列,有名字有类型
域 Domain 取值范围 属性的合法值集合,如"性别"的域是固定的两个值
候选键 Candidate Key —— 能唯一标识一行的最小属性组,可有多个
主键 Primary Key 主键 从候选键中选定的那个,全表唯一且非空

两个容易被忽略的细节值得单独强调。其一,"行无序"是关系模型的数学承诺——你不能依赖"新插入的行排在最后"这种假设,第 4 章讲索引时会看到查询结果顺序由执行计划决定。其二,"行不重复"靠主键保证,没有主键的表在 InnoDB 里其实也会被偷偷塞一个隐藏列当主键,代价是你失去了主动控制数据物理位置的能力。

外键:关系的显式声明

光有表还不够,业务数据天然是关联的:读者借书、订单包含商品、商品属于分类。关系型数据库的做法是在表里放一个外键(Foreign Key)——指向另一张表主键的列,声明"这一行的业务含义依赖那张表的某一行"。

用一个被评审会反复追问的例子说明。订单系统的最小结构:

CREATE TABLE customer ( customer_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(64) NOT NULL, phone CHAR(11) NOT NULL ) ENGINE=InnoDB; CREATE TABLE orders ( order_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, customer_id BIGINT UNSIGNED NOT NULL, amount DECIMAL(10,2) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_order_customer FOREIGN KEY (customer_id) REFERENCES customer(customer_id) ) ENGINE=InnoDB;

外键声明之后,数据库替你守住两条底线:往 orders 插一条 customer_id 等于 99999 的订单——如果客户表里没有 99999,直接报错,脏数据进不来;反过来,要删除一个还有订单的客户,默认也会被拒绝。这两条防线就是引用完整性(Referential Integrity)。

演练:亲眼看见外键拦截脏数据

完整走一遍(背景→操作→结果→解读→变式)。背景:验证外键是否真的在工作。操作:先插客户再插订单,然后故意插一条坏订单。

INSERT INTO customer (name, phone) VALUES ('陈晓', '13800001111'); -- 结果:Query OK, 1 row affected INSERT INTO orders (customer_id, amount) VALUES (1, 59.90); -- 结果:Query OK, 1 row affected INSERT INTO orders (customer_id, amount) VALUES (88888, 59.90); -- 结果:ERROR 1452 Cannot add or update a child row: -- a foreign key constraint fails

解读:第三条语句在到达存储引擎之前就被 InnoDB 拒绝,因为 88888 在 customer 表中无对应主键值。注意报错发生在插入时而不是查询时——这正是数据库层校验和应用层校验的本质区别:它不依赖每一个写代码的人都记得做检查。

变式一:如果业务允许"先有订单、后补客户资料"(比如导入历史数据),可以声明外键但插入时临时关闭校验,或者在逻辑层改用"延迟关联"。变式二:互联网大厂常用做法是不建物理外键,只建逻辑外键(同样的列、同样的查询,但不下 FOREIGN KEY 约束),把校验放在应用层——换来的是写入性能和分库分表的自由度,代价是脏数据风险由应用承担。两种路线没有绝对对错,评审会上关键是把取舍说出口,而不是默认选一个。

易错点:三个高频误解

误解一:"主键就是编号列"。 主键是"能唯一标识一行的最小属性组",身份证号、手机号在理论上都可作为客户表的候选键。选自增 ID 当主键的理由是稳定(业务标识会变,如换手机号)、短小(主键会被二级索引复制,越长索引越胖)、无业务含义(不暴露信息)——这是工程选择,不是定义。

误解二:"加个外键更安全,那就都加上"。 每个外键在写入时都要做一次父表检查,批量导入时这个检查会成为明显瓶颈;高并发场景下外键还引入死锁风险。标准做法:核心强一致关系(如资金流水)用物理外键,其余用逻辑外键加应用层校验。

误解三:"关系型数据库什么都能存"。 图片、视频这类大二进制对象放进表里会把缓冲池撑爆,拖垮整库性能。正确姿势是表里只存对象存储的引用键。关系模型的强项是结构化、强关联、强一致的数据,识别数据形态本身就是设计能力的一部分。

评审清单与要点回顾

回到评审会。这一节的知识点转化成评审委员的提问就是:主键选了什么、为什么;哪些关系用了物理外键、哪些是逻辑外键、理由;有没有大二进制对象混进表里。

  • 关系模型三纪律:结构统一的二维表、键声明关系、字段级约束,缺一不可;
  • 主键三标准:稳定、短小、无业务含义,自增 ID 是默认答案而非唯一答案;
  • 外键是取舍:物理外键换来写入时强校验,逻辑外键换来性能与扩展自由;
  • 校验位置之争:数据库层校验不依赖人的自觉,应用层校验响应业务变化更快;
  • 行无序是承诺:想要顺序,用 ORDER BY 显式表达,别赌物理存储顺序。

下一节我们离开数据模型,跟着一条 SQL 语句走一遍 MySQL 的内部结构——你优化的每一处,都对应这条路径上的某一层。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U