在 Databricks 上使用 UNIQUE 约束进行声明与优化

在 Databricks 上使用 UNIQUE 约束进行声明与优化

💡 原文英文,约1700词,阅读约需6分钟。
📝

内容提要

Databricks 为 Unity Catalog Delta 表推出 UNIQUE 约束(公开预览),支持单表声明多个唯一性保证,列可为空,外键也可引用。约束默认仅记录元数据、不强制写入;验证数据后可用 RELY 标记,让优化器消除冗余连接和 DISTINCT 聚合。基准测试中连接查询提速 7.9 倍,DISTINCT 查询提速 12 倍。

🔎

延伸解读

UNIQUE 约束的定位:补充主键之外的唯一性声明

文章指出,主键只能有一个且列不可为空,而 UNIQUE 约束允许一张表声明多个唯一性保证,列可为空,外键也可引用。这解决了业务实体常有多个唯一标识(如用户名、邮箱、组合键)却无法在模式中完整表达的问题。读者应理解,UNIQUE 不是主键的替代,而是对主键未覆盖的唯一性语义的补充记录,便于用户和工具发现这些保证。

默认不强制写入:约束是元数据而非数据校验

与主外键类似,UNIQUE 约束在 Databricks 上默认仅记录元数据,写入重复的非空值不会失败。文章强调,这种设计让模式可以记录由摄入管道或上游系统维护的唯一性保证,同时成为可检查的表元数据。读者需注意,约束本身不保证数据质量,唯一性仍需外部流程维护;若数据违反约束,后续依赖 RELY 的优化可能产生错误结果。

RELY 的收益与风险:优化器信任的前提是数据已验证

文章说明,验证数据并确保管道持续维护后,可将约束标记为 RELY,让优化器消除冗余连接和 DISTINCT 聚合。基准测试中连接查询提速 7.9 倍,DISTINCT 查询提速 12 倍。但 RELY 不验证现有数据也不强制未来写入,仅让优化器假设约束有效。读者应仅在唯一性得到验证且持续维护时使用 RELY,否则基于该约束的重写可能返回错误结果。

可空性与列顺序:影响优化能否生效的细节

文章通过示例指出,可空 UNIQUE 约束下 NULL 可重复,因此 DISTINCT 聚合仍可能保留;若查询提供非空过滤,优化才可移除聚合。此外,外键引用复合 UNIQUE 时,父列必须按 UNIQUE 定义中的顺序列出,否则约束仍有效但连接消除不会触发。读者在声明约束时应关注列的可空性和顺序,以确保优化器能正确利用这些保证。

❓

Q&A

Databricks 的 UNIQUE 约束和 PRIMARY KEY 有什么区别?

PRIMARY KEY 每表只能有一个且列必须非空;UNIQUE 约束可以声明多个,列可为空,并且外键可以引用 UNIQUE 约束。两者都记录唯一性,但角色不同。

UNIQUE 约束默认会强制写入数据吗?

不会。UNIQUE 约束默认是信息性的(NORELY),不会在写入时强制检查,允许写入重复的非空值。它仅记录由管道或上游系统维护的唯一性保证,并作为元数据供工具和用户查看。

如何让 Databricks 优化器利用 UNIQUE 约束来优化查询?

验证数据满足唯一性保证后,删除并重新创建约束并加上 RELY 标记(例如 ALTER TABLE ... DROP CONSTRAINT ...; ALTER TABLE ... ADD CONSTRAINT ... UNIQUE (...) RELY)。这样优化器就能消除冗余的连接和 DISTINCT 聚合。

外键引用 UNIQUE 约束时需要注意什么?

外键可以引用 UNIQUE 约束。引用时,父列列表必须与 UNIQUE 约束定义的列顺序一致,类型和位置对应,列名可以不同。如果省略父列列表,则默认引用主键。顺序不一致时约束仍有效,但连接消除不会触发。

RELY 标记有什么风险?

RELY 不会验证现有数据或强制未来写入,它只是让优化器假设约束有效。如果数据违反被依赖的约束,基于该约束的查询重写可能产生错误结果。因此只应对已验证并持续维护的保证使用 RELY。

UNIQUE 约束能带来多大的性能提升?

在 TPC-DS SF10000(10TB)基准测试中,连接查询(store_sales 与 customer)在 RELY 下消除了连接,执行时间从 23.6 秒降至 3.0 秒,提速 7.9 倍;DISTINCT 查询(对 store_sales 的复合唯一列)消除了聚合,从 33.7 秒降至 2.8 秒,提速 12 倍。两者均减少了超过 99.99% 的 shuffle。

可空的 UNIQUE 约束还能用于优化 DISTINCT 查询吗?

可以,但需要查询额外提供非空保证。例如在查询中添加 IS NOT NULL 过滤条件,排除可能重复的 NULL 值后,优化器就能消除 DISTINCT 聚合。否则由于 NULL 可重复,聚合仍会保留。

🏷️

标签

➡️

继续阅读