一佳技术

DORA 四指标的落地架构:两张通用表撑起多个度量域

管理层要的是"部署频率提升 3 倍",不是功能列表。本文讲我们的 DORA 度量架构:事件驱动 + 定时快照、用两张通用表替代每域一表、唯一键幂等与增量聚合的取舍。

2026-10-10 · 脉冲

DevOps 平台做得再全,缺了度量就少了「价值证明器」——管理层看不到「部署频率提升 3 倍」「MTTR 从 4 小时降到 40 分钟」这样的数字。DORA 四指标是行业通用语言,但落地时最容易掉进「每个度量场景建一套表」的坑。这篇文章讲我们如何用两张通用表撑起多个度量域。

一、先定原则:不侵入领域,只订阅事件

我们有一条铁律:度量是横切关注点,不修改任何业务领域的代码。部署、版本、缺陷、需求四个领域各自发布自己的领域事件(部署结束、版本发布、缺陷关闭、需求完成),度量模块只做订阅者:

这个约束带来的设计红利在扩展时兑现:新接一个度量域,领域代码零改动,度量侧只需要「加一个事件类型枚举 + 一个订阅方法 + 快照加几个 key」三步。

二、重构决策:放弃专用表,两张通用表打天下

原始设计是每个场景单独建表:部署度量一张表、事件度量一张表……很快暴露问题——每加一个域就要加两张表、一套聚合根、一组读写服务,而它们的逻辑高度相似(订阅事件 → 写原子记录 → 定时聚合)。

重构后收敛为两张表:

原子事件表(一次领域事件一行):

  • event_category 区分域:部署 / 版本 / 缺陷 / 需求;
  • event_type 细分含义:部署成功、部署失败、版本发布、缺陷关闭……;
  • source_type + source_code + event_type 组成唯一键——同一对象的同一状态变更天然幂等,消费者被重触发多少次都安全;
  • duration_seconds、metric_value 两个通用数值槽承载不同场景的耗时与度量值;
  • 按「域 + 时间」「产品 + 时间」建索引,看板查询全走索引。

统计快照表(一个周期一行):

  • 周期类型(日/周/月/季)+ 产品 + 环境 + 域构成唯一键;
  • 指标结果存为 JSON 键值对——不同域的指标 key 不同(部署域存次数与失败率,缺陷域存 MTTR 统计),一张表通吃,字段膨胀用约定约束而非 schema 修改。

三、写入与聚合的分工

写入侧追求实时与不丢:领域事件发生即写原子表,唯一键兜幂等。聚合侧追求查询体验:每日凌晨把原子表聚合进快照表,看板查询优先读快照,快照缺失时回退到原子表实时聚合——两者结果一致,只是速度差异。

失败率这类比值指标有个容易踩的细节:分母从哪来。变更失败率 = 失败部署 /(成功 + 失败),取消的部署不算失败也不该计入分母——口径在聚合层统一实现,原子表只存事实。

四、GraphQL 查询面

看板需要三类查询,全部收敛到一个度量查询入口:

查询数据源用途
当期指标(部署次数 / 失败率 / P50 / P95)原子表或快照表看板顶部数字卡
时序快照(按日/周/月)快照表优先趋势图
最近部署明细原子表下钻列表

时序与明细分开查是刻意的:明细可能很多(大团队每天上百次部署),走分页;时序聚合后每周期只有一行,一次拉完画图。

五、多域扩展的成本清单

部署域跑通后,接第二个域的成本实测是这样的:

  1. 事件类型枚举加 2 个值(如 VERSION_RELEASED / VERSION_RELEASE_FAILED);
  2. 订阅器加一个组装方法(从领域事件抽取 code、结果、耗时);
  3. 快照聚合的 JSON 里加对应的指标 key;
  4. 表结构不动、聚合根不动、查询入口不动。

这就是通用两表 + 分类枚举设计的回报:变化的永远是「往上叠的一小块」,不变的是底下那两张表。

小结

度量系统的架构难点不在指标算法(都是 count 和 average),而在演进成本——今天只做部署频率,明天要 MTTR,后天要需求交付周期。把「每个场景一套表」的思路换成「两张通用表 + 域分类」,等于把变化从 schema 层挪到了数据层。附带的好处是幂等纪律:唯一键放在数据库层而不是应用层,重复消费、补偿重放这些可靠性手段才有安全的操作空间。