设计数据持久层
0. 引言
持久层的设计包括持久化框架选择、持久层代码设计以及存储技术选型等,偏重持久层的数据存储技术本身。本文先讲解关系数据库的三大范式,再介绍 NoSQL 数据库的分类,帮助全栈工程师结合实际场景选择合适的技术。
1. 关系数据库
关系数据库以"关系模型"为基础建立,数据通过数学上的关系表示和关联起来,最终通过二维表格的结构表达。关系数据库除了带来明确的 schema 和关系以外,还带来了对事务的支持,即强一致性支持。
1.1 数据库范式
数据库的表设计是全栈工程师经常面对的问题,常见的规范要求被总结为不同的"范式"(Normal Form)。
第一范式(1NF):要求每个属性值都是不可再分的。满足 1NF 的关系被称为规范化的关系,1NF 是关系模式应具备的最起码条件。
比如下面这张 Books 表,有两本书重名(都叫 "Life"),但国际标准书号 ISBN 不同。同一属性 ISBN 中存放了多个值,并非不可再分,这显然违反了第一范式:

第二范式(2NF):要求去除局部依赖,即表中的属性必须完全依赖于全部主键,而不是部分主键。
例如原本想用 BOOK_ID 和 AUTHOR_ID 组成联合主键,但 BOOK_NAME 仅依赖部分主键 BOOK_ID,AUTHOR_NAME 仅依赖部分主键 AUTHOR_ID,违背了第二范式。解决办法是拆分:把可以独立被依赖的部分主键拿出去,原表被拆成 N 对 1 关系的两个表,不被范式依赖的那个"部分主键"变成"1"这一头的主键:

第三范式(3NF):要求去除非主属性的传递依赖,即在第二范式基础上,非主属性必须直接依赖于主键,而不能传递依赖于主键。
例如主键是 BOOK_ID,而 CATEGORY_NAME 是非关键字段,并非直接依赖于主键,而是通过 CATEGORY_NAME → CATEGORY_ID → BOOK_ID 的传递依赖实现。为了消除传递依赖,还是拆表,让传递链中间的 CATEGORY_ID 自立门户。
一般在设计中分析到第三范式就打住了,很少有情况考虑更严格的范式。例如 BC 范式与第三范式很像,但第三范式只消除非主属性对主属性的传递依赖,BC 范式更进一步要求消除主属性对主属性的传递依赖。此外还有第四、第五范式,要求更严格、解耦更彻底,但不太常用。
范式的程度越高,冗余度便越低。但每一次范式升级都意味着一次拆表过程,一旦过度解耦、拆分出太多零散的表,对程序员理解、数据模型建立乃至联表操作的 I/O 性能都不利。因此需要权衡,掌握好这个度——这一原则与分层设计是一致的。
2. NoSQL
在 NoSQL 出现以前,设计大型 Web 应用时,对量大且非结构化的数据缺乏特别理想的解决方法,工程师常常只能在传统关系数据库基础上使用 Sharding 和 Partition。Web 2.0 时代,用户代替传统媒体成为主要的数据制造者,海量、不定结构、弱关联关系、高可用性和低一致性要求的数据特点,让关系数据库力不从心;而 NoSQL 具有更好的横向扩展性、海量数据支持、易维护和廉价等优势,成为解决数据难题的利器。
同时,关系数据库云服务(如 RDS)的崛起将传统的数据库管理工作自动化,普通软件工程师也可以完成以往 DBA 才能完成的工作。于是数据问题有了两个层次的解决方案:
- 出现更适合业务的非关系数据库服务,即 NoSQL;
- 把关系数据库搬到云上,让企业从繁重的数据库管理工作中解脱出来。
2.1 NoSQL 数据库的分类
键值(Key-value)数据库:采用 key-value 访问模型,根据唯一 key 获取对应的值,key 被称为主键,访问过程通过 Hash 算法实现。本地的 Redis 和云上的 DynamoDB 都属于这一类。
列式(Columnar)数据库:经典数据库面向行,每一行数据放在一起,读取磁盘连续数据时可一气读取若干行;若要完整查询特定某些行的数据,行数据库是高效的。列式数据库将每一列的数据放在一起,如果处理逻辑要求取出所有数据中的特定列,列数据库是更好的选择。对于数据库来说,磁盘读写往往不是最慢的,最慢的是寻址操作,因此从随机访问变成顺序访问可以极大提高效率。此外,某一特定列往往满足特定格式,列数据库可以据此执行特定压缩操作。大数据处理中常用的 HBase 和云上的 Redshift 都属于这一类。
文档(Document)数据库:键值数据库的演化版,值以特定文档格式(如 JSON、XML)存储,文档携带的数据已指定既定的编码、格式等信息。一些文档数据库还针对文档特点提供了文档内容查询功能,比原始键值数据库功能更强。本地的 MongoDB 和 AWS 上的 DocumentDB 都属于这个类型。
对象(Object)数据库:当 value 变成可序列化的对象、特别是大对象时,就被归类为对象数据库。AWS 上最常用的 NoSQL 存储除了 DynamoDB 就是 S3:成本低廉、耐久性非常高(达到 11 个 9)、存储对象可以很大,用户常把它当作"文件系统"存放各种类型的文件,把 key 设计成类似操作系统文件路径的层级形式。当然,S3 的"文件"与操作系统的文件系统有很大区别。
| NoSQL 类型 | 代表产品 | 适用场景 |
|---|---|---|
| 键值数据库 | Redis、DynamoDB | 简单 key-value 访问,缓存 |
| 列式数据库 | HBase、Redshift | 大数据分析,特定列批量读取 |
| 文档数据库 | MongoDB、DocumentDB | 文档存储,内容查询 |
| 对象数据库 | S3 | 大对象存储,文件托管 |
此外还有图形(Graph)数据库、搜索(Search)数据库等类型,这里不再一一列举。
3. 小结
本文讲解了关系数据库的三大范式及其权衡原则,并介绍了 NoSQL 出现的背景与四类主要数据库的适用场景。关系数据库适合强一致、结构化数据,NoSQL 适合海量、非结构化数据,二者并非互斥,实践中常组合使用。设计数据持久层时,先明确数据特征,再选择合适的存储技术,是全栈工程师的基本功。
下一章我们将进入网络协议与 Web 接口,从 HTTPS 与 SSL/TLS 加密体系讲起,理解 Web 通信的安全机制。