文章

现代数据库设计核心指南

现代数据库设计核心指南

数据库设计是构建可靠、可扩展和高性能应用系统的基石。一个优秀的设计不仅能满足当前业务需求,还能从容应对未来的增长和变化。本文档旨在系统性地梳理数据库设计的核心原则与实践。

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. 垂直拆分

将一个宽表(字段过多)拆分为多个窄表。

  • 场景
    1. 将不常用的字段或大字段(如 TEXT, BLOB)拆分到扩展表中。
    2. 将核心字段和非核心字段分离。
  • 示例:将 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 进行授权