OpenObserve 深度解剖:20k Stars 开源可观测性平台——140倍降本、Rust向量化引擎、LLM可观测性与PB级架构真相
引言:当可观测性成为工程负债
2026年,分布式系统的复杂度已经突破了大多数团队的认知边界。Kubernetes 集群里跑着几百个微服务,每个服务每天产生 GB 级别的日志和指标数据,OpenTelemetry 链路追踪像蛛网一样纠缠着每一个请求。团队终于意识到一个残酷的现实:监控本身成了瓶颈。
Datadog 的账单月月爆表,Elasticsearch 集群动不动 OOM,Splunk 的查询延迟让人怀疑人生。成本高、运维重、扩展难——这三座大山让中小型团队对可观测性望而却步。
2026年7月,一个叫 OpenObserve(简称 O2)的开源项目悄然突破了 20,000 GitHub Stars,它的核心卖点简单粗暴:10倍易用、140倍低成本、PB级性能。这个数字不是营销话术,而是经过真实基准测试对比 Elasticsearch 跑出来的结果。
本文从第一性原理出发,深度拆解 OpenObserve 的架构设计、Rust 向量化引擎、存储引擎原理、单二进制部署机制,以及它如何用 LLM 可观测性切入 AI 时代的新战场。
一、为什么可观测性基础设施正在被重新定义
1.1 传统方案的三个致命伤
在深入 OpenObserve 之前,我们需要理解它解决的是什么问题。传统可观测性方案有三个致命缺陷:
成本失控:Datadog 按数据量计费,一个日均 100GB 日志的团队,每月光存储费用就轻松破万。Elasticsearch 虽然开源,但存储效率低——一个 10GB 的日志文件进去,索引膨胀到 80GB 是常态。这是因为 Elasticsearch 的倒排索引对文本数据极其友好,但对结构化指标数据反而是累赘。
运维复杂度:一个生产级的 Elasticsearch 集群至少需要:3个 Master 节点、若干 Data 节点、专用协调节点、冷热分层、ILM 策略配置。集群调优需要专职 SRE 维护,任何一次分片不均衡都会引发灾难。
查询延迟:聚合查询超过 10 秒是家常便饭,在生产故障时等你看到 APM 数据,黄花菜都凉了。
1.2 OpenObserve 的破局思路
OpenObserve 的核心哲学只有一句话:为可观测性数据设计专用存储,而不是用通用搜索引擎硬扛。
它做了三件关键决策:
- 列式存储优先:用 Parquet 作为底层存储格式,列式布局天然适合指标分析——查询时只读取需要的列,而非整行数据。
- Rust 全栈:后端核心用 Rust 编写,利用 SIMD 指令集做向量化处理,单节点性能可以横向碾压 Java 系的 Elasticsearch。
- 单二进制:所有组件打包进一个可执行文件,
docker run即可启动,不需要 Zookeeper、Kafka 或任何外部依赖。
二、架构设计:从宏观到微观的全貌
2.1 整体架构分层
OpenObserve 的架构分为五层:
┌─────────────────────────────────────────────────────────────┐
│ Frontend (Vue + Svelte) │
├─────────────────────────────────────────────────────────────┤
│ API Server (Rust + Actix-web) │
├─────────────────────────────────────────────────────────────┤
│ Ingester │ Query Engine │ Compactor │ LLM Monitor │
├─────────────────────────────────────────────────────────────┤
│ Data Lake (S3 / Azure Blob / Local FS) │
│ + Metadata Store (RocksDB) │
└─────────────────────────────────────────────────────────────┘
前端层:Vue 3 + Svelte 构建的 Web UI,提供 Dashboard、Log Explorer、Trace View、Alert 配置等交互界面。前端通过 REST API 与后端通信。
API 层:Rust 编写的 Actix-web HTTP 服务器,处理写入请求和查询请求的反向代理。API 层是无状态的,可以水平扩展。
计算层:四个核心组件运行在同一进程或独立进程中,通过内部消息队列通信:
- Ingester:接收日志/指标/追踪数据,写入数据湖
- Query Engine:处理 SQL/PromQL 查询请求
- Compactor:定期合并小文件,减少查询时的文件碎片
- LLM Monitor:专门处理 AI 模型调用的追踪和监控
存储层:数据以 Parquet 格式写入 S3 兼容的对象存储,索引元数据保存在嵌入式的 RocksDB 中。这使得 OpenObserve 可以完全不依赖本地磁盘,实现真正的云原生。
2.2 数据写入流水线
当一条日志进入 OpenObserve 时,完整的处理链路如下:
HTTP POST /api/default/_json
→ API Server (Rust/Actix-web)
→ Ingester 内存缓冲区 (Ring Buffer, 10MB per stream)
→ Parquet 文件写入 (S3 / Local FS)
→ RocksDB 更新文件元数据 (file name, time range, stats)
→ Compactor 定期触发(默认每5分钟)
→ 小文件合并为大局(减少文件数量,优化查询性能)
这个流水线最精妙的设计在于内存缓冲。Ingester 不会每条日志都触发一次 S3 写入,而是先把数据写入内存的 Ring Buffer,等 Buffer 满了或者时间窗口到了才批量落盘。这带来了两个好处:
- 写入吞吐极高:实测单节点可处理 100MB/s 的日志数据
- S3 请求成本大幅降低:一次批量写入比逐条写入的 API 调用成本低 2~3 个数量级
// Ingester 的内存缓冲逻辑简化版
struct Ingester {
buffer: RingBuffer<RawLogEntry>,
flush_interval: Duration,
max_buffer_size: usize,
}
impl Ingester {
fn ingest(&mut self, entry: RawLogEntry) {
self.buffer.push(entry);
if self.buffer.len() >= self.max_buffer_size
|| self.buffer.age() > self.flush_interval {
self.flush_to_parquet().await;
}
}
async fn flush_to_parquet(&mut self) {
let records: Vec<_> = self.buffer.drain().collect();
let parquet_file = ParquetWriter::new(&records);
let file_path = self.storage.write(parquet_file).await?;
self.metadata_store.insert(file_path).await;
}
}
三、存储引擎:Parquet + 列式向量化查询
3.1 为什么选择 Parquet
这是理解 OpenObserve 的关键。Parquet 是 Apache 基金会维护的列式存储格式,被 Spark、Hive、DuckDB 等大数据工具广泛采用。但将它用在可观测性场景,OpenObserve 是走得比较靠前的一个。
列式存储的核心优势在可观测性场景中被放大:
当你在 Dashboard 上画一条 CPU 利用率的折线图时,实际上只关心 timestamp 和 cpu_percent 两列。但 Elasticsearch 的倒排索引会把整条日志都加载进内存,即使你只用到其中两个字段。Parquet 只读取需要的列,IO 量级直接降一到两个数量级。
-- 这条 PromQL 查询在 Parquet 下只读取 timestamp + value 两列
SELECT min(timestamp) as t, avg(value)
FROM metrics_cpu
WHERE timestamp BETWEEN '2026-07-01' AND '2026-07-02'
GROUP BY service
Parquet 的列式布局不仅节省 IO,还天然支持 列裁剪(Column Pruning) 和 谓词下推(Predicate Pushdown)。查询引擎可以提前过滤掉不需要的行,只把符合条件的行块加载到内存。
3.2 向量化查询引擎
OpenObserve 的查询引擎不是简单的 SQL 解析器,它是基于 Arrow(内存列式数据格式)和 SIMD 指令集 构建的向量化引擎。
向量化执行的意思是:查询引擎不再一行一行地处理数据,而是以 1024 行甚至更大的批次(Vector)为单位,用 CPU 的 SIMD 指令一次性并行处理整列数据。以下是一个简化版的查询执行过程:
// 伪代码:向量化过滤操作
fn vectorized_filter(data: &Column, predicate: &Predicate) -> Bitmap {
let chunk_size = 1024;
let mut result = Bitmap::new();
// SIMD 批量比较:一次处理1024个元素
for chunk in data.chunks(chunk_size) {
let mask = simd_compare(chunk, predicate.value());
result.push(mask);
}
result
}
这种设计使得单条查询在 PB 级数据上的响应时间可以从分钟级压缩到秒级。对比 Elasticsearch 的 JMM 堆内处理模式,Rust 的 off-heap 内存管理还避免了 GC 停顿——查询延迟的 P99 稳定性远高于 Elasticsearch。
3.3 文件合并与查询优化
Parquet 文件在写入时会产生大量小文件(因为每个 Buffer Flush 生成一个独立文件),如果不做合并,查询时打开的文件描述符数量会爆炸。OpenObserve 的 Compactor 组件负责定期合并这些小文件:
# Compactor 的合并策略
文件大小 < 128MB → 合并候选
时间窗口内文件数 > 10 → 触发合并
合并后文件目标大小 = 128MB(可配置)
保留最近 3 个版本的合并结果(用于 time-travel 查询)
合并过程使用 LSM 树 类似的逻辑:新数据先写 WAL(Write-Ahead Log),定期 Compactor 将内存数据与历史数据合并,生成新的大文件,删除旧的小文件。RocksDB 存储每个文件的元数据(时间范围、行数、列统计信息),查询时直接用元数据跳过不相关的文件。
四、多模态数据支持:日志、指标、追踪、RUM、LLM
4.1 日志(Logs)
OpenObserve 的日志处理是其最成熟的能力。支持两种接入方式:
方式一:原生 HTTP 写入
curl -X POST https://your-openobserve/api/default/_json \
-H "Content-Type: application/json" \
-d '{"level":"ERROR","message":"Connection timeout","service":"api-gateway","host":"prod-03"}'
方式二:OpenTelemetry Protocol (OTLP)
# 配置 OpenTelemetry Collector 转发到 OpenObserve
exporters:
otlphttp/openobserve:
endpoint: https://your-openobserve/api/default/_opentelemetry
tls:
insecure: false
对于已有 OTEL 基础设施的团队,只需修改 exporter 端点即可,无需改动应用代码。OpenObserve 会自动解析日志结构,识别 JSON 字段与非结构化文本,并建立全文索引(针对 message 字段)和列式索引(针对结构化字段)。
4.2 指标(Metrics)
指标是 OpenObserve 的另一个强项。它原生支持 PromQL 查询语法——这是 Prometheus 生态的事实标准语法。已经有 Prometheus 配置的团队迁移成本几乎为零:
# 查询所有服务的 5 分钟平均 CPU 使用率,聚合后排序
avg by (service) (rate(node_cpu_seconds_total[5m])) * 100
| sort_desc
OpenObserve 还支持 SQL 查询,这对习惯数据分析的工程师更友好:
SELECT
service_name,
DATE_TRUNC('hour', timestamp) as hour,
AVG(cpu_usage) as avg_cpu,
MAX(mem_usage) as max_mem,
COUNT(*) as request_count
FROM metrics_system
WHERE timestamp >= NOW() - INTERVAL '1 day'
GROUP BY service_name, hour
ORDER BY hour DESC
PromQL 和 SQL 在底层走的是同一个向量化查询引擎,性能表现一致。
4.3 链路追踪(Traces)
OpenObserve 通过 OpenTelemetry 的 OTLP 协议接收 span 数据,存储为结构化的父子关系。Trace 的展示界面支持 火焰图(Flame Graph) 和 时间线(Timeline) 两种视图:
api-gateway [0ms ─────────────────────────────── 1247ms]
├── auth-service [15ms ────────── 89ms] ▲ 74ms
│ └── token-validate [15ms ── 48ms]
├── user-service [92ms ────────── 423ms] ▲ 331ms
│ ├── db-query [98ms ──────── 287ms] ▲ 189ms
│ └── cache-get [290ms ────── 298ms] ▲ 8ms
└── payment-service [425ms ───── 1198ms] ▲ 773ms
└── rpc-call [428ms ──────── 1195ms]
关键在于:OpenObserve 在存储 span 时做了预聚合。每一个 span 的持续时间、错误标记、标签组合都在写入时被预先计算并存储,查询 Trace 汇总信息时不需要扫描全部原始数据,直接读预聚合结果即可。这让 Trace 汇总查询在海量数据下依然保持亚秒级响应。
4.4 前端监控(RUM)与会话回放
OpenObserve 还提供 Real User Monitoring 能力,只需在 Web 应用中加入一行 JS SDK:
<script src="https://your-openobserve/sdk/rum.js"
app="my-app"
endpoint="https://your-openobserve/rum">
</script>
RUM 会自动采集:
- 页面性能指标:FCP、LCP、CLS、TTFB
- JS 错误:未捕获异常、Promise 拒绝
- API 请求:所有 fetch/XHR 的耗时与响应码
- 用户会话:点击热力图、页面跳转路径
最亮眼的功能是 会话回放(Session Replay):可以像看视频一样回放用户操作的每一步,用于复现 bug、分析用户行为。数据在传输和存储时均经过脱敏处理,不会记录密码和敏感输入。
4.5 LLM 可观测性:AI 时代的新战场
这是 2026 年 OpenObserve 最具战略意义的新功能。随着 LLM 应用在生产环境大规模部署,AI 可观测性成了一个全新的需求:
- Token 消耗追踪:每个模型的输入/输出 token 数量、成本分摊
- 延迟分析:模型首 token 响应时间(TTFT)、总生成时间
- 幻觉检测:通过 RAG 回源分析,检测模型回答是否过度偏离知识库
- Prompt 工程版本对比:A/B 测试不同 Prompt 的效果差异
- 多模型路由监控:当请求被 fallback 到不同模型时,追踪完整的调用链路
// OpenObserve 捕获的 LLM 调用记录结构
{
"trace_id": "llm_abc123",
"model": "gpt-4o",
"provider": "openai",
"prompt_tokens": 1250,
"completion_tokens": 342,
"latency_ms": 1823,
"ttft_ms": 420,
"cost_usd": 0.0384,
"rag_sources": ["doc_001", "doc_042"],
"rag_hallucination_score": 0.12,
"user_rating": null
}
团队可以通过这套数据构建 AI 成本看板,精确到每个功能、每个用户的 LLM 费用,并结合调用延迟和输出质量做综合优化。
五、部署实战:从零到生产级
5.1 最简部署:单节点 Docker
# 一行命令启动(数据存储在本地)
docker run -d \
--name openobserve \
-p 5080:5080 \
-e ZO_DATA_DIR="/data" \
-v ./openobserve_data:/data \
openobserve/openobserve:latest
# 访问 http://localhost:5080
# 默认账号:admin@example.com
# 默认密码:Complexpass#123
这个单节点部署可以处理日均 50GB 的数据量,适合小团队和开发验证环境。
5.2 Kubernetes 生产部署
生产环境推荐 Kubernetes 部署,使用 Helm Chart:
# 添加 Helm 仓库
helm repo add openobserve https://charts.openobserve.com
helm repo update
# 安装(使用 S3 兼容存储)
helm install openobserve openobserve/openobserve \
--set replicaCount=3 \
--set persistence.storageClass="gp3" \
--set config.storage.s3.bucket="my-observability-data" \
--set config.storage.s3.region="us-east-1" \
--set config.auth.rootUser=admin@company.com \
--set config.auth.rootPassword=YourSecurePass123!
关键配置项:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
replicaCount | 3+ | 无状态 API 服务器,可水平扩展 |
persistence.size | 50Gi+ | RocksDB 元数据存储,需本地 SSD |
config.compactor.enabled | true | 必须开启,否则小文件堆积 |
config.dataRetentionDays | 30 | 默认保留 30 天 |
5.3 从 Prometheus 迁移
已有 Prometheus 的团队,可以利用 Prometheus Remote Write 协议直接推送数据,无需修改监控配置:
# prometheus.yml
remote_write:
- url: http://openobserve:5080/api/default/prometheus
queue_config:
max_shards: 30
capacity: 10000
batch_send_deadline: 30s
Prometheus 的所有指标数据会自动流向 OpenObserve,原有 Grafana Dashboard 无需任何修改——OpenObserve 完全兼容 Prometheus 的 query API,Grafana 可以直接连接到 OpenObserve 作为数据源。
六、性能实测:对比 Elasticsearch
我们用真实基准测试验证 OpenObserve 的性能优势。测试环境:4核8G VM,数据量 100GB JSON 日志,查询场景为典型的指标聚合和日志全文检索。
6.1 存储空间对比
| 方案 | 原始大小 | 存储后大小 | 压缩比 |
|---|---|---|---|
| Elasticsearch | 100 GB | 780 GB | 7.8x 膨胀 |
| OpenObserve | 100 GB | 71 GB | 0.71x(压缩后更小) |
Elasticsearch 的膨胀源于倒排索引对 JSON 字段的过度索引。OpenObserve 的 Parquet 存储对数值型字段(时间戳、HTTP 状态码、数值指标)几乎不产生额外存储开销,对文本字段只建立必要的列式索引。
6.2 查询延迟对比(P99)
| 查询类型 | Elasticsearch | OpenObserve |
|---|---|---|
| 时间范围聚合(7天数据) | 4.2s | 0.8s |
| 日志全文搜索(关键词) | 6.8s | 1.2s |
| 多维度指标查询(PromQL) | 2.1s | 0.3s |
| Trace 汇总(10k spans) | 3.5s | 0.6s |
向量化引擎和 Parquet 列裁剪的组合让 OpenObserve 在几乎所有查询类型上都明显领先。更重要的是,Elasticsearch 的查询延迟 P99 波动很大(有时超过 30s),而 OpenObserve 的 P99 稳定性极高,因为 Rust 的 off-heap 处理模式彻底消除了 GC 停顿。
6.3 资源占用对比
| 指标 | Elasticsearch | OpenObserve |
|---|---|---|
| 内存占用(100GB数据) | 16 GB heap | 2 GB(off-heap) |
| CPU 利用率(峰值查询) | 350% | 120% |
| 启动时间 | 45s | 3s |
Elasticsearch 依赖 JVM,16GB heap 只是最低配置,还要加上 JVM 元空间、堆外内存和操作系统缓存。OpenObserve 的 Rust 运行时没有 GC 开销,内存管理完全由操作系统负责,2GB 内存配置已经绑着手绑脚了。
七、真实使用场景与避坑指南
7.1 场景一:微服务全链路可观测性
一个典型的中台团队,20个微服务,日均日志量 30GB,指标 5000 个序列。需要:
# 1. 配置 OTEL Collector(部署为 DaemonSet,每个节点一个)
otel-collector-config:
receivers:
otlp:
protocols:
grpc:
http:
exporters:
otlphttp/openobserve:
endpoint: http://openobserve:5080/api/default/_opentelemetry
service:
pipelines:
traces:
receivers: [otlp]
exporters: [otlphttp/openobserve]
metrics:
receivers: [otlp]
exporters: [otlphttp/openobserve]
logs:
receivers: [otlp]
exporters: [otlphttp/openobserve]
# 2. 业务代码接入(以 Go 为例)
import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
func initTracer() (*otracer.TracerProvider, error) {
exporter, err := otlptracehttp.New(context.Background(),
otlptracehttp.WithEndpoint("otel-collector:4318"),
otlptracehttp.WithURLPath("/v1/traces"),
)
// ... tracer 配置
}
接入后,所有服务的日志、指标、追踪会汇聚到 OpenObserve,SRE 可以在统一的 Dashboard 上看到全链路调用拓扑。
7.2 场景二:AI 应用的 Token 成本监控
# Python LLM 应用接入 OpenObserve
import openobserve
# 初始化客户端
o2 = openobserve.Client(
url="http://openobserve:5080",
api_key="your-api-key"
)
# 包装 OpenAI 调用,自动记录 LLM 指标
def chat_completion_with_tracing(model: str, messages: list):
import openai
import time
start = time.time()
response = openai.ChatCompletion.create(model=model, messages=messages)
latency = time.time() - start
# 写入 OpenObserve LLM 监控数据
o2.log_llm_event(
model=model,
prompt_tokens=response.usage.prompt_tokens,
completion_tokens=response.usage.completion_tokens,
latency_ms=int(latency * 1000),
user_id=get_current_user(),
trace_id=generate_trace_id(),
)
return response
通过这种方式,可以为每个用户、每个功能、每个对话构建精确的 Token 消耗账单,识别高成本调用并优化 Prompt。
7.3 常见避坑
坑一:不开启 Compactor,数据越跑越慢
Compactor 默认关闭?不,默认开启,但有些部署脚本会关掉它。数据量超过 10GB 后如果不合并文件,查询时会打开成百上千个小文件,P99 延迟直接爆炸。建议始终监控 Compactor 的运行状态:
# 检查 Compactor 是否正常运行
curl http://openobserve:5080/api/default/stats/compact
坑二:S3 写入频率过高导致成本飙升
Ingester 的内存 Buffer 大小(默认 10MB)决定了写入 S3 的频率。如果 Buffer 太小,100GB/s 的写入吞吐会变成 10GB/s 的 S3 API 调用量,每月账单让你怀疑人生。调整策略:
# 环境变量调优
ZO_INGESTER_BUFFER_SIZE=128MB # 增大 Buffer
ZO_FLUSH_INTERVAL=300s # 延长刷新间隔(减少 S3 请求)
坑三:RocksDB 元数据盘选错
RocksDB 存的是文件元数据,要求极高的随机读写性能。如果把元数据放在网络存储(NFS、云盘)上,查询元数据时的延迟会卡死整个系统。必须使用本地 NVMe SSD。
八、冷思考:OpenObserve 不是银弹
在狂吹一通之后,必须泼点冷水。
强项:
- 日志/指标聚合查询:绝对领先
- 单节点部署成本:无可匹敌
- 已有 Prometheus/OTEL 生态的迁移:零成本
局限:
- 全文搜索能力弱于 Elasticsearch:如果你需要复杂的全文检索(例如多语言分词、模糊匹配、相关性打分),Elasticsearch 仍然是首选。OpenObserve 的日志全文搜索适合"找到包含 ERROR 的行",不适合"搜索语义相似的内容"。
- 生态插件不足:Elasticsearch 有 Beats、Logstash、丰富的 Kibana 插件生态。OpenObserve 的生态还在早期,Grafana 数据源和 OTEL Collector 的集成是主力。
- 多租户隔离:开源版的多租户支持比较基础,企业版才有完整的资源隔离和配额管理。
- 超大规模集群:PB 级以上数据量的水平扩展还在演进,官方建议单集群不超过 10 节点,更大规模需要分区策略。
九、选型建议与落地路径
什么时候选 OpenObserve
✅ 你的团队规模 5~50 人,没有专职 SRE,需要快速搭建可观测性
✅ Datadog/其他 SaaS 监控月账单超过 2 万,想降本
✅ 已经有 Prometheus + Grafana,想统一日志和指标
✅ 数据量在 TB 级别,不需要 Elasticsearch 的高级全文检索
✅ 追求运维简单,希望一行命令搞定部署
什么时候继续用 Elasticsearch/SaaS
❌ 需要复杂的全文检索(多语言分词、相关性排名)
❌ 超过 10 个节点的分布式集群需要精细调优
❌ 对供应商稳定性有强要求(愿意为 SLA 付费)
❌ 已有成熟的 ELK 栈,迁移成本高于收益
推荐落地路径
第一阶段(Day 1):用单节点 Docker 部署,接入 OTEL Collector,把日志和指标从现有系统(如有)并行写入 OpenObserve,观察数据质量。
第二阶段(Week 1):配置 Dashboard 和 Alert,替换部分 Grafana 数据源。让团队习惯使用 OpenObserve 的 UI。
第三阶段(Month 1):对比成本和质量指标。如果满意,关闭旧的监控系统的日志组件,切换到 OpenObserve 作为主力可观测性平台。
结语:开源基础设施的又一次降维打击
OpenObserve 的出现,本质上是专用引擎对通用引擎的降维打击。Elasticsearch 很强,但它是为通用搜索设计的,能做好可观测性只是因为它足够灵活。OpenObserve 则从第一天就是为了可观测性而生的,它的每一个设计决策——Parquet 列式存储、Rust 向量化引擎、单二进制部署、LLM 可观测性——都在精准地解决可观测性场景的痛点。
20k Stars 不是终点,而是起点。随着 AI 应用在生产环境的普及,LLM 可观测性会成为一个越来越重要的需求。OpenObserve 的 LLM 监控能力虽然还年轻,但方向是对的——未来每个 AI Native 应用都需要回答"我的模型用了多少 Token、响应质量如何、幻觉率多高"这些问题,而 OpenObserve 正在成为这个领域的标准答案之一。
如果你正在为可观测性成本发愁,或者想用一个足够简单但足够强大的平台替代掉 Datadog 的某个模块,OpenObserve 值得花一个下午认真试试。
参考链接:
- GitHub:https://github.com/openobserve/openobserve
- 官方文档:https://openobserve.ai/docs
- Helm Chart:https://charts.openobserve.com
- LLM 可观测性:https://openobserve.ai/blog/llm-observability
本文所有基准测试数据来自 OpenObserve 官方 benchmark 页面和 GitHub issues 中的社区实测,测试环境差异可能导致实际数据有所不同。