alitrack

不用 ES 也能分析日志——DuckDB 方案深度评测

凌晨三点,告警又响了。JVM heap 飙到 95%,ES 节点开始拒绝写入。你打开监控面板——shard 分布不均,三个节点两个爆满一个空闲,Logstash 的 pipeline 堵了三万条。一年前搭这套 ELK 的时候,日志量还不到现在的三分之一。

这不是个别情况。Elasticsearch 是日志分析的默认选择,但它也是运维人员的噩梦:JVM 调优、shard 管理、heap 分配、集群扩缩容——随便一个问题都能折腾一晚上。有没有不需要集群、不需要 JVM、不需要专职运维的日志方案?

有。DuckDB。

ELK vs DuckDB 架构对比

ELK vs DuckDB 架构对比

● ● ●

DuckDB 凭什么

DuckDB 常被叫作"OLAP 界的 SQLite"——一个嵌入式列式数据库,没有独立进程,没有网络端口,没有集群概念。把它嵌入你的 Go/Python/Rust 应用里,就是一个分析引擎。

三个关键特性让它天然适合日志分析:

列式存储 + 向量化执行。 日志分析本质上是 OLAP 负载——按时间窗口扫描、按字段聚合、按条件过滤。DuckDB 的列存引擎对这些操作做了极致优化:只读需要的列(投影下推),只扫匹配的行组(谓词下推),用 SIMD 指令批量处理。

原生 Parquet 支持。 直接用 SQL 查询 S3 上的 Parquet 文件,不需要导入、不需要建表、不需要维护 schema。SELECT count() FROM read_parquet('s3://bucket/logs/.parquet') 一行搞定。

零运维。 没有 daemon 进程,没有配置文件模板,没有集群协调。部署物就是你应用的二进制文件。

● ● ●

四种可落地的架构

社区已经跑通了从"本地命令行一把梭"到"生产级多租户平台"的完整光谱。

方案一:Fluent Bit → Parquet → S3 → DuckDB

这是目前社区讨论最多的"最小日志栈",法国工程师 David Guerrero 在 2026 年 6 月公开了他的生产级实现:

Fluent Bit (DaemonSet, 每节点 <100MiB 内存)     │  采集容器日志,buffered 5 分钟批量写入     ▼ S3 (按 dt=YYYY-MM-DD  Hive 分区)     │  每个小时结束后 compact 成单个 Parquet     ▼ DuckDB + Grafana (cache_httpfs 缓存热点文件)

关键点:

  • Fluent Bit 的 forward 模式可以把多个 daemon 的日志汇聚到一个 aggregator,避免几千个小文件
  • Hive 分区 dt=2026-07-25/hour=14 让 DuckDB 在查询时自动跳过无关分区
  • cache_httpfs
     扩展缓存 S3 上的 Parquet 文件,第二次查询近乎本地速度
  • 日志写入延迟约 5 分钟(buffer 窗口),对事后排查完全可接受

Guerrero 的 3 节点集群上,Fluent Bit 总内存占用不到 100 MiB,CPU 使用可以忽略。对比一下 Elasticsearch 默认的 4GB heap,差距是 40 倍。

方案二:Glintlog——开箱即用的 OTLP 全栈

如果你不想自己搭流水线,Glintlog(GPL v3.0)提供了一个完整的可观测性平台。用作者 Caio Ricciuti 的话说:"一个 binary 文件包含了全部——Web UI、API 服务器、gRPC 接收器、DuckDB 存储。"

架构:单 Go 二进制内嵌 DuckDB,接收 OTLP 协议日志(gRPC 4317 / HTTP 4318),自带 React 前端。

功能列表:

  • 日志浏览器(全文搜索、严重级别过滤、服务隔离)
  • Live Tail(SSE 实时推送)
  • 分布式追踪可视化 + 服务拓扑图
  • 拖拽式仪表板(统计卡片、直方图、趋势图)
  • LDAP / SSO 认证
  • 按时间范围删除日志的 Admin API

Ricciuti 实测:DuckDB 列式 Parquet 格式相比 Elasticsearch,压缩率达到 140x。"对于结构化的、重复性高的日志正文,这个数字完全靠谱。你的磁盘账单下降一个数量级。"

代价:不像 Elasticsearch 能水平扩展到 PB 级。但对于 10 台以内服务器的小团队,够用了。

方案三:DuckViz——npx duckviz ./logs -r

最极端的场景:生产环境挂了,你需要立刻查日志,但 ELK 本身也挂了(这事经常发生在资源耗尽的时候)。

DuckViz(npm v0.3.2)把 DuckDB 编译成了 WebAssembly,直接在浏览器标签页里运行:

npx duckviz ./logs -r

CLI 会:

  1. 01
    遍历目录,AI 自动检测日志格式(NGINX、Sysmon、JSON、syslog…)
  2. 02
    启动一个 localhost HTTP 服务,带 bearer token 认证
  3. 03
    打开浏览器,DuckDB-WASM 从 localhost 拉取文件到内存
  4. 04
    每个日志文件变成一张 DuckDB 表

然后你直接在浏览器里写 SQL:CTE、窗口函数、time_bucket、regexp_extract、跨表 JOIN——导出时间线图表,贴进事后分析报告。

整个过程零安装、零上传、文件不离开本机。安全团队不需要审批——"没有云,没有供应商审查,就是一个 CLI 加一个浏览器标签页。"

方案四:RO9——四层存储的生产级平台

RO9(Apache 2.0)的目标更高——替代 Datadog。它的架构体现了 DuckDB 在可观测性领域的上限:

接入层 (Arrow IPC, 200K events/sec)     │ 热层 Redis (15 分钟, 零延迟)     │ 温层 NVMe Parquet (24 小时, <10ms)     │ 冷层 S3 Parquet (30 天, <100ms)     │ 归档层 Glacier (7 年, <1 小时恢复)     │ DuckDB 自动路由到对应层

压缩策略很激进:字典编码(重复字符串削减 95%)+ Delta 编码(时间戳削减 80%)+ RLE(稀疏数据削减 90%)+ Zstd(最终 60%)——综合 15-25x。

团队背景:在 Datadog 上花了 $60,000/年,只存 10GB/天的日志。换成 RO9 后目标月费 $75-150。

● ● ●

性能数据

先看一个直观对比——DuckDB Lab 做的 Nginx 日志分析测试,1GB 日志文件的处理时间:

分析维度
grep/awk
DuckDB + Streamlit
提升
1GB 全文扫描
3-5 分钟
5-10 秒
~30x
多维度交叉分析
多个管道命令组合
一条 SQL
—
响应码分布 + P95 延迟
手动脚本
实时交互看板
—

然后是 Walmart Global Tech 在 2023 年的基准测试,对比 DuckDB 和 Elasticsearch 的搜索和聚合性能(单位:秒,越小越好):

操作
Elasticsearch
DuckDB
差距
聚合查询
—
0.7s
—
全文搜索
0.015s
0.014s
DuckDB 略优
Join
—
8s
—
VM 成本
基准
降低 81%
ES 需要集群

DuckDB 在搜索上与 ES 持平甚至略优。这里需要说明:DuckDB 用的是全表扫描(在 agentlogsbench 六引擎对比中,DuckDB 的文本搜索被标记为"full table scan baseline"),因为列式扫描本身就比倒排索引的随机读磁盘快——前者是顺序读压缩列,后者是到处跳。但从 v1.5 开始,DuckDB 也有了 fts 扩展(BM25),可以在特定列上建索引加速关键词搜索。

● ● ●

什么时候不能用

DuckDB 不是万能药,有明确的边界:

高并发小查询。 DuckDB 是 OLAP 引擎,为吞吐而设计,不是为 QPS。如果你有几百个用户同时搜日志关键词,Elasticsearch 的倒排索引 + 分布式架构更合适。DuckDB 适合的是"一个人排查问题"的分析场景。

实时告警。 DuckDB 本身没有告警引擎。你需要外挂 cron 定期跑 SQL,或者像 RO9 那样在 Redis 热层里做。Elasticsearch 的 Watcher 开箱即用。

FTS 索引不自动更新。fts 扩展(BM25)的索引是静态的——数据变更后必须手动重建索引。这意味着持续写入时,全文搜索索引会滞后。对日志分析影响不大(通常查的是历史窗口),但对实时全文搜索是硬伤。

单机瓶颈。 DuckDB 不分布式。单机能处理的日志量由硬盘大小决定。S3 + Iceberg 可以部分解决——把冷数据放对象存储,DuckDB 通过网络读 Parquet——但查询延迟会受网络带宽影响。

一句话:TB 级日志、几个人排查问题,DuckDB 完美。PB 级、几百个并发搜索用户,用 ES。

● ● ●

5 分钟实操:Nginx 日志分析

假设你有一个 Nginx access.log,只想快速看看哪些 API 最慢、哪些 IP 错误最多。不需要装任何数据库:

-- 1. 直接从日志文件建结构化表 CREATE TABLE logs AS SELECT     regexp_extract(line, '^([^ ]+)')                    AS ip,     regexp_extract(line, '\[([^\]]+)\]')                AS ts_raw,     regexp_extract(line, '"([^"]+)"')                   AS request,     regexp_extract(line, ' (\d{3}) ')::INT              AS status_code,     regexp_extract(line, ' (\d+) "')::INT               AS body_bytes,     regexp_extract(line, ' (\d+\.\d+)$')::DOUBLE        AS response_time FROM read_text('nginx_access.log');  -- 2. 状态码分布(一秒出结果) SELECT status_code, count(*) AS cnt,        round(count(*) * 100.0 / sum(count(*)) OVER(), 1) AS pct FROM logs GROUP BY status_code ORDER BY cnt DESC;  -- 3. 最慢的 10 个 API(含 P95) SELECT     regexp_extract(request, ' ([^ ]+) ') AS path,     count(*) AS cnt,     round(avg(response_time), 3) AS avg_ms,     round(percentile_cont(0.95) WITHIN GROUP (ORDER BY response_time), 3) AS p95_ms FROM logs WHERE status_code >= 400 GROUP BY path HAVING cnt > 10 ORDER BY avg_ms DESC LIMIT 10;  -- 4. 按 10 分钟窗口看错误趋势 SELECT     date_trunc('10 minutes', strptime(         regexp_replace(ts_raw, ':', ' ', 1, 1),         '%d/%b/%Y %H:%M:%S'     )) AS bucket,     count(*) AS errors FROM logs WHERE status_code >= 500 GROUP BY bucket ORDER BY bucket;

和 grep | awk | sort | uniq -c | sort -rn | head 的区别?DuckDB 一次扫描,出所有维度的结果。管道命令每个维度扫一次全文件。

● ● ●

总结

用 DuckDB 做日志分析,不是在组件层面平替 ELK——而是在方案层面降维,把"日志分析"从一个运维问题变成 SQL 查询问题。

ELK
DuckDB 方案
适用规模
TB-PB 级集群
GB-TB 级单机
运维复杂度
需要专人
零
全文搜索
倒排索引,实时
BM25 静态索引 / 列扫
聚合分析
慢
极快(列存+向量化)
存储成本
高
低一个数量级
部署物
3+ 个服务
1 个二进制

如果你有十几台服务器、每天几百 GB 日志、主要需求是事后排查和分析——应该认真考虑 DuckDB。最坏的情况,你只花了半天尝试了一个新工具。最好的情况,你再也不用半夜被 JVM OOM 告警吵醒了。