选 DuckDB 而不是 SQLite
文章摘要
这是 Traceway(一个自托管 OpenTelemetry 可观测性后端)作者 Jovan Stojiljkovic 的系列基准测试第三篇。原标题其实更克制——《同一台 16 美元的机器上跑 SQLite 与 DuckDB:每一道悬崖都往后挪了 100 倍》。前两篇他论证了”一台便宜机器就能跑完整的可观测性栈”,并用 SQLite 实测出了若干限制;这一篇把同样的方法论原封不动地跑在 DuckDB 存储引擎上。
硬件是 Hetzner 纽伦堡机房的一台 CCX13(文中标价 16.49 美元/月),同一个 Traceway 二进制,只换底层存储引擎。核心结果:
- 写入吞吐:metrics 从 6.17 万点/秒涨到 25.4 万点/秒(4 倍);spans 从 3.05 万涨到 9.57 万(3 倍);logs 从 4877 条/秒涨到 7.52 万条/秒(15 倍)。
- 读取悬崖(作者定义为”真实仪表盘页面仍能加载的最大表规模”,三个真实端点探测中位数不超过 5 秒且无超时):metrics 从 100 万行推到 1 亿行,spans 从 10 万推到 1000 万,logs 从 10 万推到 1000 万——三个信号全部整整挪后 100 倍,且延迟持平或更好。
- 单机吞下 10.01 亿个指标点,磁盘占用仅 10.8 GB(列式压缩后约每点 10.8 字节),全部摄入耗时不到一小时,机器全程存活。
作者称”读取悬崖”这个词用得贴切,因为再往上一个 10 倍级台阶,查询不是变慢,而是干脆不返回。他专门用把探测超时放宽到 60 秒的方式重跑了整个阶梯,想给失败的格子填上真实耗时,结果是根本填不上:所有失败的查询在 61 秒被切断时都还在跑。这说明悬崖不是斜坡,两个数据库都一样——从几秒直接跳到超过一分钟,典型的聚合超出 4 GB 内存预算后开始溢写的特征。
方法论上作者相当坦白,主动披露了三处与上一篇的差异:负载生成器升级了(原来那台在 DuckDB spans 测试中自己先崩了,而数据库还是 0% 错误率);新增了”消化门”(DuckDB 摄入停止后会做 WAL 检查点,不等它稳定就探测等于在测一台忙碌的引擎);只统计 2xx 确认的行数而非尝试行数。最重要的一条披露在后端而非测试架子上——最初几轮 DuckDB 跑分他自己都不信,追查下去发现是他自己摄入路径的一个真实 bug:没有准入控制,持续的大批次会把进程打爆内存。他加了并发上限并用 503 + Retry-After 应答过载后重跑了全部测试。修复前的旧结果低估了 DuckDB——spans 低 23%,logs 差了近一半。
局限他也列得很清楚:单次跑分(承诺的三次取中位数又欠了一期)、没测写入并发下的读取、没测混合信号负载、没校验结果正确性、全部用默认配置。
结论部分他没有一边倒:只有两种情况他仍会选 SQLite 构建——一是需要一个 Go 能编到哪就跑到哪的二进制(DuckDB 构建需要 CGO 和 glibc,镜像只能是 Debian 而非 Alpine);二是数据量本就远低于上一篇的天花板时,SQLite 所有工作都内联完成、没有延迟检查点和消化窗口,是”能用的最简单方案”。但一旦 logs 重要、或者保留期超过一小时,比较就不再接近了。最后一个星号他也自己打上:这些数字之所以存在,是因为基准测试先找出了他自己代码里的崩溃 bug,”引擎从来不是问题,问题在它前面的代码”。
HN 评论精华
这条讨论的真实走向和文章内容关系不大——两条主线分别是”苹果比橘子”和”这是 AI 写的”,两者都相当尖锐。
第一条主线:这个对比本身不成立。
- brightball 的最高赞评论一句话定调:这是工作负载特定的,标题应该叫《在分析场景下选 DuckDB 而不是 SQLite》。tptacek(HN 老资格)附议同一句话。dangoodmanUT 的类比更刻薄:”这标题不如叫《钉钉子时请选锤子而不是扳手》。”
- cynicalsecurity 直白开骂:”拿苹果比橘子干得漂亮。DuckDB 是列式 OLAP 引擎,SQLite 是行式 OLTP,在这个用例上 DuckDB 当然碾压。”ethin 补了同样的意思:一个是通用引擎,一个是为列式分析手工调优的引擎,后者当然赢。
- coldbrewed 给出了本帖最有价值的反驳视角:正因为两者不可比,DuckDB 才令人兴奋。以前哪怕是 OLAP 负载,只要你需要一个单文件、低开销的本地 SQL 数据库,SQLite 依然是最好的选项——即使技术上根本不合适。他自己踩过坑:用 SQLite 存全互联网 rDNS 扫描结果,一个
count()要跑 8 分钟。DuckDB 的意义是”我们终于可以不再把 OLAP 硬塞进 OLTP 数据库了”。 - oathvz 点出商业动机:”这是钓鱼换饵的文章。拿苹果比橘子,’哦顺便看看我们的产品’。”
- crustycoder 欣慰地总结:”看到这么多’这是苹果那是橘子’的评论真让人安心,说得对,各位。”
第二条主线:AI 味太重,读不下去。
- otterley:”AI 泔水。内容或许有价值,但这种行文让人读不下去。各位,请用自己的声音写作——尤其是公司博客。这对你作为作者有好处(熟能生巧),对读者也有好处(你本来是想影响他们的)。”headz 附和:”感觉像在读一段 Claude Code 的对话记录。”jaredezz:”我连那个 cliff 标题都没撑过去。”d1l 更冲:”AI 泔水真的累人,太他妈懒了。”k3liutZu 说自己已经对代码注释、code review、PR 里无处不在的 AI 文风感到疲劳了。
- datadrivenangel 的态度最微妙:”这是 AI 泔水,但我真的很想知道更多关于他们写入模式和批处理方式的细节。”
- 由此引出一段有意思的元讨论:knuckleheads 把文章丢进 AI 检测器,说一半立刻被标红,并 @dang 提议 HN 花钱买 Pangram 订阅来检测首页文章。tptacek 澄清规则:”HN 不允许 AI 评论,但允许 AI 投稿。”HN 版主 tomhow 详细解释了为什么不这么做:在自己站内检测并自动 kill 生成的评论是一回事,让软件伸进别人的站点、穿过反爬防御、抓内容再判断”人类作者成分”是另一回事;长文的 LLM 参与度本来就是连续谱,一旦开这个口,HN 上就会充斥”这篇到底有多少 AI 成分”的跑题元讨论,而减少跑题正是 HN 想优化的目标。他给出的启发式没变:”文章写得烂就不该在 HN 上,应该被 flag。”
第三条主线:DuckDB 的真实短板。
- shubhamjain 提出了最实用的一条:DuckDB 最大的缺点是并发模型。一个进程以读写模式打开数据库就会拿到文件的排他锁,只要写者还开着,其他进程连简单读取都做不了。他说自己不得不为了让另一个脚本里的查询能跑而关掉 duckdb CLI,”生产力杀手”。datadrivenangel 说 Quack(远程协议)现在算是一种绕法,coldbrewed 也提到了 DuckDB 的 server 模式。
- adsharma 补了一条更硬的:SQLite 有 rqlite、dqlite、litestream 等复制方案,DuckDB 没有——一年前提的 PR 就卡在同一个并发模型问题上。
- lanstin 抛出一个未经证实但值得注意的说法:DuckDB 用 C++ 写,在生产中崩得比 SQLite 多,用它必须自己做韧性设计。frollogaston 追问”这要是真的就是致命伤”,lanstin 说是朋友在一家卖廉价 Snowflake 代理的创业公司里的经历,新版本尤其明显。
- biophysboy 给出了本帖最平衡的收尾:选型时想清楚事务型 vs 分析型、单用户进程内 vs 多用户客户端-服务器就够了;不过他也认为 DuckDB 的适用范围比这里很多人想的要宽,他自己经常拿它处理十亿行级别的表。
- 还有人注意到成本前提已经变了:raro11 指出文中那台 16.49 美元/月的 Hetzner CCX13 现在要价 51.09 美元了。