首页/数据/数仓建模速查/维度中避免 EAV 反模式
维度与事实设计149 条

维度中避免 EAV 反模式

把维度属性做成「对象-属性-值」三列(EAV)看似万能,实际让查询、校验与统计全部复杂化;固定属性应列化,动态属性走标签表。

数仓建模速查数据更新于 2026-10-13

EAV 反模式是什么

EAV(实体-属性-值)指把维度属性拆成「对象、属性名、属性值」三列的行式存储,看上去能承载任意属性,实际代价很大:取一个属性多一次 join,值列统一成字符串后类型、比较与聚合语义全部丢失,唯一约束与枚举校验也无处落地。数仓中的基本判断是固定属性列化,动态属性走标签表。

基本信息

EAV 形态一行一属性,attr_name / attr_value
主要代价join 次数多、类型弱、校验难
替代方案固定属性列化 + 标签表承载动态属性
列化适用属性稳定、经常组合查询与筛选
标签表适用属性集合动态增长、候选值不固定
列存影响行式属性在列存引擎下读取放大明显

典型使用场景

  • 多属性筛选:需要同时按颜色、尺码、材质过滤商品时,列化写法可走索引
  • 组合指标:按两个属性交叉统计时,EAV 要自连接多次且性能急剧下降
  • 数据校验:列化后能加非空、枚举与范围约束,EAV 只能存字符串
  • 新增属性评估:判断该属性是稳定属性(列化)还是长尾属性(标签表)
  • 历史包袱梳理:识别已存在的 EAV 表,按访问频次逐批列化

EAV 的代价与列化对照

EAV 的代价与列化对照
-- EAV:每多取一个属性就多一次自连接
SELECT a.entity_id
FROM dim_eav a JOIN dim_eav b ON a.entity_id = b.entity_id
WHERE a.attr = 'color' AND a.val = 'red'
  AND b.attr = 'size' AND b.val = 'L';   -- 属性越多,join 越多
-- 列化:属性作为列,条件可直接走索引
SELECT product_id
FROM dim_product
WHERE color = 'red' AND size = 'L';   -- 同一查询在列化后无需自连接

列化还是标签表

判断条件建议做法原因
属性稳定、必填列化可加约束,查询走索引
属性长尾、可选标签表(对象 + 标签)避免维表列数膨胀
需要范围比较列化并保留数值类型字符串列无法正确比大小
属性常一起筛选列化并做组合索引EAV 需多次自连接
属性稳定且经常组合查询时应直接建列并建索引;判断标准是「属性集合是否由业务方固定下来」,而不是「属性会不会变」——几乎所有属性都会缓慢变化。
标签表仍然是行式结构,因此适合做单标签筛选与标签计数,不适合做多标签的复杂交叉分析;需要交叉分析时应在主题层把常用标签先列化。

易错点

  • 把 EAV 当成万能方案套用到数仓,性能与可用性双输,最后不得不整体重构
  • 值列统一用字符串,数值属性无法比较大小,聚合语义丢失
  • 缺少合法属性白名单,属性名拼写错误产生大量影子属性
  • 标签表直接替代维表做交叉分析,查询要自连接十几次,性能不可接受

数据来源与参考

  • 数据仓库工具箱:维度设计反模式
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌