Simon & Sam: 数据库、可靠性与闲聊
来源: Twitter/X Broadcast 嘉宾:
- Simon Eskildsen (@Sirupsen) — Turbopuffer 联合创始人兼 CEO,前 Shopify(从 1K 扩展到 1M RPS)
- Sam Lambert (@samlambert) — PlanetScale CEO,前 GitHub VP Engineering
- Ben (PlanetScale) — 主持人 日期: 2025 年 时长: ~75 分钟(已全部转录)
核心话题
1. Napkin Math 项目 — 性能建模的重要性
- Simon 维护的 Napkin Math 项目:记录各种基础设施的延迟/带宽数据(对象存储延迟、NVMe SSD 带宽等)
- 核心观点:Profiling 只能优化几个百分点;要获得真正的性能提升,需要坐下来构建模型,把数字乘在一起,看看系统表现距离底层硬件的理论极限有多远
- Simon 的梦想:Napkin Math 每天在所有现代机型上自动运行基准测试(类似 Lucene Benchmarks),生成延迟直方图
- Simon 和 Sam 都表示愿意联合赞助有人把 Napkin Math 做到下一个级别
- Turbopuffer 的设计灵感正是来自于对这些底层数字的深入理解
2. S3 架构深度解析
- S3 内部架构(Simon 的理解):
- 请求 → Proxy 层 → 元数据层(记录文件块位置)→ 分布式存储层
- 使用 Parity Encoding,一个块可能分布在多个节点
- 底层块大小估计 1-4 MB
- 关键洞察:元数据层 vs 存储层延迟差异巨大
- S3 元数据层 P50 ≈ 8ms
- GCS 元数据层 P50 ≈ 14-18ms(因为 Spanner 前端,跨 3 个 AZ)
- Azure 元数据层 P50 ≈ 4-5ms(最快!)
- 存储层 P50/P99 差异大:HDD、大规模分布式系统、高利用率
- ETag/条件请求只触及元数据层,可用于低延迟一致性检查
3. Turbopuffer 的架构设计哲学
- 核心设计约束:每个计算节点都可以随时死掉,不丢数据 → 完全依赖对象存储做持久化
- 强一致性选择:与 Elasticsearch 等最终一致性搜索系统不同,Turbopuffer 选择强一致
- 写入后立即可查询(不像 ES 默认需要等待段合并)
- 代价:每次查询需要访问对象存储确认一致性(8-15ms)
- 对搜索场景可接受(P99 50ms 在搜索领域完全 OK)
- 可选关闭一致性检查,直接从缓存返回(~1ms)
- 为什么这个架构对搜索是 feature 但对 OLTP 是 bug:
- OLTP 每个页面加载 = 数百次查询,5ms 地板延迟会拖垮整个应用
- Shopify/GitHub 这种 Rails 应用需要 ~500μs 级别的数据库延迟
- 搜索系统可以容忍更高延迟,但获得了免费的持久性和可靠性
4. 数据库对产品的极端重要性
- Sam: 数据库挂了 = 产品挂了,没有中间状态
- 即使只是只读模式,用户也无法改权限、删员工等 → 产品实质不可用
- 网页上每一个元素都来自数据库:session、时间线、通知、关注列表……
- 没有数据库,产品就是空模板
- 持久性同样关键:丢数据比宕机更可怕(“丢了别人的 Strava 数据”)
5. OLTP 数据库的缓存与故障切换
Buffer Pool 预热问题
- 故障切换后缓存不对 = 性能断崖
- Shopify 分片故障切换后,繁忙分片可能慢几分钟
- 解决方案:镜像读查询,让缓存达到正确平衡
- PlanetScale 的做法:
- 计划性故障切换:提前预热目标节点
- 预热方式:发送相同查询直到延迟收敛
- 曾经用 shell 脚本 + tcpdump 抓查询来预热(很 hacky 但有效)
- MySQL 特有优势:Buffer Pool Saving
- 可以把 LRU 列表保存到磁盘,恢复时直接加载
- PostgreSQL 没有这个功能
- Sam 认为 PlanetScale 应该更好地展示这些复杂性给用户
缓存层选择:Memcached vs 读副本
- Simon 更偏好读副本而非外部缓存(Memcached)
- 外部缓存的危险:从缓存拿到过期数据 → 写回数据库 → 数据污染
- 缓存没有 “五个 9” 的一致性保证
- 读副本更容易推理(虽然需要处理复制延迟)
- 读副本的挑战:
- 需要在写入后短暂路由到主库(Memcached 里设标记,10 秒后过期)
- 需要标记从副本返回的对象,防止写回
- 复制延迟高时需要告警和限流
控制复制延迟
- Shopify 方案:所有后台维护任务表达为枚举器,每批处理后检查复制延迟和 InnoDB 线程数
- 延迟过高则 sleep 等待
- 效果非常好,能精确控制复制延迟
6. Shopify Storefront 重写
- Simon 和 Justine(Turbopuffer 联合创始人)在 Shopify 重写了整个 Storefront
- 背景:Liquid 模板语言 → 动态渲染 → 查询瀑布依赖问题
- 核心优化:
- 全页缓存到 Memcached
- 预测性查询预加载:缓存上次渲染的 SQL 查询列表,在请求开始时并行发起,渲染时 resolve futures → 消除瀑布
- 提取为独立服务,脱离主 Rails 应用
- 验证策略(“JIT Render”):
- Nginx 前端同时发送请求到新旧系统
- 比较 HTML 输出,一致则切流量到新系统
- 用百分比图表跟踪兼容性进展
- 处理百万种不同模板 + 用户依赖 bug 的兼容性
- ~1.5 年后承载 95%+ 流量(约 2019 年开始)
- Simon 最初对大重写持怀疑态度,但最终效果很好
7. 招聘小故事
- GitHub 早期支持团队第一个 hire 是 Tekkub(WoW 插件作者),在 GitHub IRC 帮助社区被注意到
- PlanetScale 也用类似方式:从 Discord 社区里招了一个一直在帮忙的工程师
- 启示:参与社区、贡献开源库是获得工作的好方式
金句
“如果你只是跟自己赛跑,你永远不知道底层机器真正能做到什么。” — Simon,论 Napkin Math 的重要性
“数据库挂了,产品就是一个空模板。” — Sam,论数据库的重要性
“构建一个不丢数据的可靠存储引擎需要 5-10 年。我们完全搭载在对象存储上,所以不需要做这件事。” — Simon,论 Turbopuffer 架构
“这个架构对大多数 OLTP 系统是 bug,但对 Turbopuffer 是 feature。” — Sam,论 disaggregated storage
关键数字
| 指标 | 数值 |
|---|---|
| S3 元数据层 P50 | ~8ms |
| GCS 元数据层 P50 | ~14-18ms |
| Azure 元数据层 P50 | ~4-5ms |
| OLTP 可接受延迟 | ~500μs |
| 搜索可接受 P99 | ~50ms |
| Shopify 故障切换缓存预热 | 数分钟 |
| Storefront 重写承载流量 | >95%(~1.5 年后) |
后半段内容(38:00 - 75:00)
8. 多数据中心与 Active-Active
- Simon 的核心观点:你不需要 Active-Active
- Active-Active = 多个数据中心同时服务生产流量,工程成本巨大
- 多数据中心主要是为了防自己的故障,而非云厂商故障
- 云厂商骨干网故障时,故障切换也帮不了你
- Shopify 的演进:
- 第一阶段(IPO 前):Chicago 主 + Ashburn 备,硬件闲置,20 点 spreadsheet 手动切换
- 第二阶段(2015,Pods):每个分片成为独立 Pod(数据库 + 缓存 + 所有层级)
- 128 个 Pod 可以独立在不同数据中心运行
- Slack bot 执行切换,几秒暂停写入,读取基本无中断
- Nginx Lua 做请求路由(Nginx Lua 是万能工具)
- Storefront 后来分离出去,可以在更多区域服务
- 对分布式共识数据库(Spanner 等)的批评:
- Simon: “我花了一辈子做数据库,但我不理解这些系统。我不理解时钟怎么工作。”
- 凌晨 3 点没人能理解分布式事务失败的原因
- 最终一致性比分布式事务失败更容易处理
- Google 为 Spanner 在数据中心放原子钟 → “如果你闲着没事,挺有趣,但是…”
- Sam: “复杂性商人们”把简单问题卖复杂了
9. 数据库可靠性与行业批评
- Sam 为什么开始公开批评竞品宕机:
- 以前行业有不成文规矩:不公开批评别人的故障(“downtime shaming”)
- Sam 以前也遵守,但现在改变了立场
- 原因:当前 AI hype 周期太疯狂,营销预算巨大,言辞已失去意义
- 某些平台持续可用性问题,甚至不更新状态页,用户只能在 Twitter 上报告
- 同理心转向用户:有慈善机构无法配送食物,有创业者因此可能丢掉六位数客户
- PlanetScale 帮助数百用户快速迁移,发放大量信用额度(来自利润而非 VC 资金)
- PlanetScale 的可靠性文化:
- ~60% 路线图用于可靠性工作
- 每次事故后大量修复工作,甚至在影响用户前就宣布事故
- 可靠性可以吞噬功能开发,但反过来不行
- 定期发布 “Code Red” 来纠正偏差
10. 创始人-市场契合度与诚实营销
- Simon 的顿悟:创始人最大优势是对问题领域的校准权重
- 经历多次迭代和失败后才确信:自己的判断方向是对的
- Turbopuffer 做过一些违背直觉的决策(如果早点相信自己就不会)
- 现在的态度:“盈利公司,按我的方式来,信任它或者找别人”
- 运营文化无法后天培养:
- 如果创始团队没有运营文化,公司永远不会有
- 在 Shopify 见过太多失败案例:纯工程师团队接手运营,项目失败
- 代码成本趋近于零的时代,运营能力更加稀缺和宝贵
- 诚实网站的哲学:
- 两家公司网站都是:这是产品,这是价格,这是它不擅长的
- Turbopuffer 网站有整页的限制说明
- 会因此丢客户,但客户也会在功能上线后回来
- “工程沟通就是尽可能快地把心智模型传递给别人,减少解读开销”
11. Logo 与品牌信任
- Turbopuffer 只在客户真正在生产环境使用后才放 Logo
- Anthropic logo:从首次请求到上网站 > 1 年
- Atlassian、Notion 同样如此
- Logo 滥用问题严重:有公司放了某客户 Logo,实际只是某个开发者注册了账号
- 游戏公司 Logo 的特殊风险:
- AAA 游戏公司警告 Sam:“你会收到死亡威胁”
- 不是因为宕机,而是玩家对游戏决策不满时会攻击供应商
- GitHub 曾因 GamerGate 相关事件员工收到死亡威胁
12. Redis HA 与工具选择
- 观众问题:PlanetScale 会做 Redis HA 吗?
- Sam: 最多请求的托管数据库是 Elasticsearch 和 ClickHouse(不是 Redis)
- PlanetScale 做了 Postgres,下一步要能说服自己是做某产品的最佳团队
- Simon 对 Redis 的建议:
- 尽量用一致性哈希的 Redis 集群,避免依赖 HA
- 不要在 Redis 中存持久化状态(队列除外)
- GitHub 曾因单线程 Redis 加载 80GB 数据导致 4 小时故障
- Turbopuffer 完全不依赖任何外部数据库
- Turbopuffer 用单个 JSON 文件在对象存储上做队列(因为不想引入依赖)