当数据库开始让 on-call 团队精疲力尽时

2023年4月18日 4 min read

简介

数据库不仅仅在崩溃时才会发生故障。

还有一种更隐蔽的故障类型:系统仍在运行,但团队必须不断地照看它。延迟 (latency) 无法预测,修复 (repair) 时间长,on-call 令人精疲力尽,操作复杂,而且每次事件 (incident) 都会消耗大量时间。

Discord 在“How Discord Stores Trillions of Messages”一文中所述的从 Cassandra 迁移到 ScyllaDB 的案例就是一个非常值得一读的例子。

1. 规模 (Scale) 暴露了问题

Discord 曾经使用 MongoDB,后来在消息规模急剧增加时转向了 Cassandra。到了数万亿条消息的阶段,他们的 Cassandra 集群变得更难操作。

Discord 的文章提到了以下问题:

  • 延迟不稳定;
  • 维护费力;
  • 集群修复和操作复杂;
  • 墓碑 (tombstones) 影响数据读取;
  • on-call/繁文缛节 (toil) 增加。

重点是:数据库并没有错。是工作负载 (workload) 和规模 (scale) 发生了变化。

2. 数据库何时成为负担?

一些迹象:

  • 每次事件都与数据库有关;
  • 查询延迟的波动无法预测;
  • 最初的模式 (schema) 不再适合工作负载;
  • 删除/修改数据会产生巨大的副作用;
  • 团队必须花太多时间进行调整 (tuning);
  • 增加节点并不能彻底解决问题。

在那个时候,问题不再是“哪个数据库最好”,而是“哪个数据库最适合这个工作负载”。

3. 给小型 CRM 的教训

一个小型 CRM 暂时还不需要考虑 Cassandra 或 ScyllaDB。但关于工作负载的教训却非常贴近。

例如,CRM 中的活动日志 (activity log):

创建了线索 (lead_created)
发送了消息 (message_sent)
打开了报价 (quote_opened)
更新了交易 (deal_updated)
跟进失败 (followup_failed)

如果日志增长很快,常见的查询可能是:

  • 获取客户的时间线;
  • 获取交易的最新活动;
  • 过滤过去 24 小时内的错误事件;
  • 统计已发送的跟进数量。

表设计必须从查询模式 (query patterns) 出发,而不仅仅是从纸面上漂亮的实体 (entities) 出发。

4. 一个更简单的活动日志模式 (schema)

CREATE TABLE activity_logs (
  id UUID PRIMARY KEY,
  workspace_id UUID NOT NULL,
  entity_type TEXT NOT NULL,
  entity_id UUID NOT NULL,
  event_type TEXT NOT NULL,
  payload JSONB,
  created_at TIMESTAMP NOT NULL
);

CREATE INDEX idx_activity_entity_time
ON activity_logs (workspace_id, entity_type, entity_id, created_at DESC);

在初期,这可能就足够了。当规模扩大时,你可以分离档案 (archives)、分区 (partitions)、事件存储 (event stores) 或搜索引擎。

5. 结论

Discord 的案例很棒,因为它不仅仅是一个数据库迁移的故事。它是一个关于工作负载、运营成本和团队健康的故事。

一个好的系统不应该只是速度快。它必须足够易于操作,这样团队就不会被数据库拖入无休止的 on-call 循环中。

参考资料

New posts, shipping stories, and nerdy links straight to your inbox.

2x per month, pure signal, zero fluff.


分享这篇文章: