意义
数据库范式是数据库表结构设计的原则,它的核心目标是消除数据冗余,保证数据的一致性和完整性
然而,一致性和完整性与查询效率之间往往存在一定的权衡。随着范式的逐级提升,数据的规范性和完整性得到加强,但可能带来性能上的代价。
数据库范式
在这里不做教条式的说明,使用简单且尽量精准的说明归纳每个范式的内容
数据库范式是递进的,满足了较高一级范式,必然也满足所有较低一级范式。以下表格列出了各范式的核心含义及其区别,重点描述了每个范式比上一级增加的限制。
| 范式 | 含义 | 说明 | 备注 |
|---|---|---|---|
| 1NF | 属性应具有原子性 | 所有属性不可再分 | |
| 2NF | 消除部分依赖 | 所有非主属性依赖全部主键 | 主要针对联合主键,对于单一主键2NF自动满足 |
| 3NF | 消除传递依赖 | 所有非主属性直接依赖主键 | |
| BCNF | 进一步消除传递依赖 | 主键不依赖非主属性 | |
| 4NF | 消除多值依赖 | 一张表不描述多个独立的一对多关系 | |
| 5NF | 消除连接依赖 | 一张表不应该能继续进行无损非平凡分离 | 基本上只针对全主键表,全主键表应该不可再分。 简单来说如果一个全主键表的内容为简单的笛卡尔积,那么它就违反了5NF |
反范式
在很多情况下,我们对查询性能的要求要高于数据的一致性,这个时候就需要适当的引入反范式。
反范式的核心思想是通过数据冗余换取更高的查询性能和更好的系统扩展性,通常结合最终一致性模型
设计原则
总的指导原则是:绝大多数情况下,你应该从一个范式化的设计开始,直到有明确的性能需求和证据时,再进行有针对性的反范式优化。过早的优化是万恶之源
1. 业务初期/发展期
在项目开始和业务逻辑还不够清晰、成熟的阶段,强烈建议你严格遵守范式化,一般默认遵循到第三范式即可,如果业务对数据一致性有特殊要求再考虑使用更高等级的范式
原因如下
- 数据一致性和完整性:范式化有助于消除数据冗余,避免因业务变化频繁而引发数据同步问题
- 灵活性和可扩展性:范式化的设计能够更好地适应业务的变更和发展
- 简化写入和更新逻辑:反范式需要考虑同步,会复杂化写入逻辑,增加代码的复杂度和错误风险
- 避免误判性能瓶颈:现代数据库的性能非常强大,提前进行反范式优化可能是解决非实际问题,从而影响系统的灵活性和可扩展性
2. 业务成熟期/遇到瓶颈时
当业务发展稳定、数据量和并发量增加且有明确的性能瓶颈时,可以考虑适当引入反范式
原则为:
- 数据驱动:不要仅靠自己的猜测,使用慢查询日志或者数据库的 explain 工具定位性能瓶颈点
- 有的放矢:仅针对性能敏感点修改,不要扩大反范式范围
- 评估读写比例:若读写比例不大,反范式可能适得其反,需慎重考虑
- 设计好同步机制:这是反范式最关键的一步。必须有一个万无一失的计划来保证数据的至少最终一致性
例外
在少数情况下,可以在设计时引入反范式,但是要进行严格的评估避免误判
- 明确的高性能核心业务指标
- 几乎永不改变的数据
- 高反范式业务,如数据仓库或 olap 系统
反范式最佳实践
预计算
对于频繁计算或聚合的值,可以在数据写入时进行预计算并存储。例如在订单完成时计算出订单完成时间与开始时间的差值,冗余到表中避免后续高成本动态计算
核心信息冗余
将需要频繁 JOIN 其他表来查询的字段冗余到当前表中。例如将订单发起人姓名冗余到订单表中
汇总表
对于需要频繁 count sum 等聚合才做的场景可以建立专门的汇总表(尤其是可以容忍低一致性的场景例如点赞量等)
合并表
合并两个严格一对一对应且频繁 JOIN 的表,例如用户表和用户详情表
读写分离
如果系统已经读写分离,可以考虑结合 cdc 技术在读库中反范式,避免影响写入操作的一致性
反范式思想
许多 NoSQL 数据库本身就采用反范式设计,因为它们通常要求较高的性能和水平扩展能力,能够容忍一定程度的最终一致性,而不追求强一致性。
即使是放到具体业务上,分布式系统的强一致性也难以保证(成本和复杂度都极高),往往只需要保证最终一致性即可。
总结而言,设计数据库时要敬畏但不盲从范式。尤其是对于大型分布式系统来说,一致性和性能之间的平衡是必须认真思考的课题,适时地引入反范式以提升性能是完全合理的策略。
数据库范式与反范式权衡
1001

被折叠的 条评论
为什么被折叠?



