现代数据库设计核心指南
现代数据库设计核心指南
数据库设计是构建可靠、可扩展和高性能应用系统的基石。一个优秀的设计不仅能满足当前业务需求,还能从容应对未来的增长和变化。本文档旨在系统性地梳理数据库设计的核心原则与实践。
1. 宏观设计:业务切分与逻辑分层
1.1. 业务切分
数据库设计的起点是深刻理解并合理切分业务。这与领域驱动设计(DDD)和微服务的思想不谋而合。
- 核心思想:将复杂的单体业务划分为独立的、高内聚、低耦合业务领域(Domain)。每个领域对应一组独立的数据库表,甚至独立的数据库实例。
- 示例:一个电商平台可以被切分为用户中心、商品中心、订单中心、库存中心等。每个中心都有自己专属的数据,服务之间的调用通过 API 完成,而不是直接跨库查询。
- 好处:实现技术异构、独立扩展和快速迭代。
1.2. 数据库逻辑分层
在数据仓库和复杂报表场景中,对数据进行逻辑分层是保证数据质量和处理效率的关键。
- ODS (Operational Data Store) - 操作数据层:贴源层,直接同步业务库数据,用于临时存储和缓冲。
- DWD (Data Warehouse Detail) - 明细数据层:对 ODS 数据进行清洗、转换和规范化,形成事实表和维度表。
- DWS (Data Warehouse Summary) - 汇总数据层:基于 DWD 进行轻度聚合,形成面向主题的宽表。
- ADS (Application Data Service) - 应用数据层:面向具体的业务报表或应用需求,从 DWS/DWD 中抽取数据,形成最终结果。
2. 中观设计:表结构设计与拆分
2.1. 设计范式与反范式
- 第一范式 (1NF):确保所有字段都是原子性的,不可再分。
- 第二范式 (2NF):在 1NF 基础上,非主键字段必须完全依赖于主键(主要针对联合主键)。
- 第三范式 (3NF):在 2NF 基础上,任何非主键字段不能依赖于其他非主键字段(消除传递依赖)。
在实践中,我们通常会遵循 3NF 进行设计,但在某些性能敏感的场景下,会进行反范式设计。
- 合理的冗余:为了减少
JOIN操作,在表中故意增加冗余字段。例如,在订单表中冗余商品名称和价格,这样查询订单列表时就无需关联商品表。- 权衡:以空间换时间,但需要处理数据一致性问题(当源数据更新时,冗余数据也需要同步更新)。
2.2. 垂直拆分
将一个宽表(字段过多)拆分为多个窄表。
- 场景:
- 将不常用的字段或大字段(如
TEXT,BLOB)拆分到扩展表中。 - 将核心字段和非核心字段分离。
- 将不常用的字段或大字段(如
- 示例:将
products表拆分为products_core(id, name, price) 和products_extension(id, description, specifications)。
2.3. 水平拆分(分片 Sharding)
将一个大表的数据行,按照某种规则(如 user_id)分散到多个物理表或数据库中。
- 场景:单表数据量达到千万或亿级别,读写压力巨大。
- 分片键 (Shard Key):选择用于路由的字段至关重要,应选择区分度高、均匀分布且查询时常作为条件的字段。
- 策略:Range(范围)、Hash(哈希取模)等。
2.4. Oracle 分区
在数据库内部,将一张大表的数据按规则划分到不同物理存储段(分区)中,对应用透明。
- 类型:Range, List, Hash, Composite 等。
- 优势:提高查询性能(分区裁剪)、简化数据管理(按分区归档或删除)。
- 与分片的区别:分区是库内操作,逻辑上仍是一张表;分片是库外操作,数据分散在多个库中。
3. 微观设计:字段与索引优化
3.1. 索引设计
索引是提升查询性能最直接有效的手段。
- 普通索引:最基本的索引,没有唯一性限制。
- 唯一索引:索引列的值必须唯一。
- 复合索引(组合索引):在多个字段上创建的索引。遵循最左前缀匹配原则。例如,在
(col1, col2, col3)上建立索引,查询条件WHERE col1 = ?或WHERE col1 = ? AND col2 = ?都能使用该索引,但WHERE col2 = ?则不能。 - 覆盖索引:当查询的字段全部包含在索引中时,数据库引擎可以直接从索引中获取数据,无需回表查询主数据,性能极高。
3.2. 字段设计最佳实践
- 数据类型:选择最恰当、最小的数据类型。例如,能用
INT就不用BIGINT,能用VARCHAR(50)就不用VARCHAR(255)。 - 标准审计字段:为绝大多数表添加标准审计字段,便于追踪和排错。
created_at(或create_time):创建时间created_by:创建人updated_at(或update_time):最后更新时间updated_by:最后更新人is_deleted(或deleted):逻辑删除标记,避免物理删除带来的问题。
- 预留字段的争议:
- 不推荐:使用
reserve_1,reserve_2等无意义的预留字段是坏味道。它们缺乏业务含义,容易被滥用,导致后期维护困难。 - 推荐做法:当需要扩展时,明确地
ALTER TABLE添加新字段。对于非结构化或频繁变化的属性,可以引入JSON类型的字段来存储。
- 不推荐:使用
4. 高级优化方案
4.1. 物化视图 (Materialized View)
物化视图是一个物理存在的表,它存储了预计算(如 JOIN 或聚合)的结果。
- 与普通视图的区别:普通视图是虚拟的,每次查询都会重新计算;物化视图存储了真实数据。
- 场景:对于复杂的、耗时长的查询(特别是多表
JOIN和聚合),可以将其结果物化,供报表或前端快速查询。 - 刷新策略:需要定义刷新策略(如
ON COMMIT,ON DEMAND)来保证数据与基表的同步。
4.2. 中间表方案
与物化视图类似,但由应用层代码手动维护。通常用于将分散在不同库、不同表中的数据进行加工和汇总,形成一个宽表,专门用于满足特定的查询场景。
- 场景:跨库数据聚合、复杂报表预处理。
- 实现:通过定时任务(如
cron)或消息队列,定期或实时地将数据从源表同步到中间表。
本文由作者按照 CC BY 4.0 进行授权