GreptimeDB 深度实战:当可观测性告别「三件套」——从宽事件统一引擎到存算分离、Flow 流处理与 PB 级日检索亚秒的生产级完全指南(2026)
一、背景:可观测性的「三座大山」与统一引擎的必然
如果你做过生产级可观测性平台,一定对这套架构不陌生:
- Prometheus / Thanos / Mimir 负责指标
- Loki / Elasticsearch 负责日志
- Jaeger / Tempo 负责链路追踪
三套系统意味着三套存储层、三套查询语言(PromQL / LogQL / TraceQL)、三套采集 Agent、三套告警配置、三套 Dashboard。更致命的是,当故障发生时,你需要在三个系统之间跳转——先在 Grafana 看指标飙高,切到 Loki 搜日志,再到 Jaeger 追链路,手动在脑海中关联时间线。这个过程,5 分钟算快,30 分钟不稀奇。
Observability 2.0 的核心洞察很简单:指标、日志、链路,本质上都是带时间戳的事件(Wide Events)。既然都是事件,为什么不用一个引擎来存、一个查询语言来查?
GreptimeDB 就是这个思路的工程实现。它用 Rust 写的列式引擎,把三种信号统一为宽事件模型,以对象存储(S3 / GCS / Azure Blob)为主存储,支持 SQL + PromQL 双查询语言,内置 Flow 流处理引擎做指标派生。不是「三个数据库拼在一起」,而是「一个引擎从头设计」。
本文会从架构原理、存储引擎、查询引擎、Flow 流处理、部署实战、性能调优六个维度,把 GreptimeDB 彻底拆开,让你读完就能在生产环境落地。
二、核心概念:宽事件模型与 Observability 2.0
2.1 传统可观测性的根本问题
传统三件套的问题不是「不好用」,而是数据模型不统一:
| 信号类型 | 传统存储 | 数据模型 | 查询语言 |
|---|---|---|---|
| Metrics | Prometheus | 时间序列(name + labels → value) | PromQL |
| Logs | Loki / ES | 半结构化文本 | LogQL / Query DSL |
| Traces | Jaeger / Tempo | Span 树 | TraceQL |
这导致三个根本问题:
- 跨信号关联需要人工对齐:指标异常 → 搜日志 → 追链路,每一步都是手动切换
- 预聚合丢细节:Prometheus 的 counter / histogram 在采集时就聚合了,原始事件已不可追溯
- 存储成本线性爆炸:三套系统各管各的存储,冷热分层策略不同,运维复杂度 3x
2.2 宽事件模型:万物皆 Event
GreptimeDB 的核心数据模型是 Wide Event——每一行就是一个带时间戳的事件,列可以任意多:
CREATE TABLE http_requests (
ts TIMESTAMP TIME INDEX,
service STRING,
endpoint STRING,
status_code INT,
latency_ms DOUBLE,
trace_id STRING,
span_id STRING,
request_body STRING,
user_id STRING,
region STRING,
-- ... 可以有数百个字段
PRIMARY KEY (service, endpoint)
) ENGINE=metric WITH (
append_mode = 'true',
merge_mode = 'last_row'
);
一个 HTTP 请求事件,同时包含:
- 指标信号:latency_ms、status_code 可以聚合为 P99 延迟、错误率
- 日志信号:request_body 是日志内容
- 链路信号:trace_id、span_id 关联到 Trace
一条 SQL 就能跨信号关联:
-- 找出延迟超过 500ms 的请求,同时查看对应的 trace 信息和请求体
SELECT
ts, service, endpoint, latency_ms,
trace_id, span_id, request_body
FROM http_requests
WHERE latency_ms > 500
AND ts > NOW() - INTERVAL '1 hour'
ORDER BY latency_ms DESC
LIMIT 100;
这在传统架构里需要三条查询、三个系统。
2.3 为什么是列存而不是行存?
宽事件表每行可能有 200+ 个字段,但单次查询通常只涉及 5-10 个。列存引擎只读取查询涉及的列,I/O 量可以降一个数量级。更重要的是,列存对同类数据的压缩效率远高于行存——比如 status_code 列全是 200/404/500,用字典编码+RLE 可以压到极小。
GreptimeDB 的列存基于 Apache Parquet 格式,配合 Rust 的零拷贝反序列化,查询路径几乎没有不必要的内存分配。
三、架构分析:存算分离与四组件分布式模型
3.1 整体架构
GreptimeDB 有两种运行模式:
- Standalone:单进程,适合开发和小规模部署
- Distributed:四组件独立扩缩容,适合生产
分布式模式的核心组件:
┌─────────────────┐
│ Frontend │ ← 无状态,水平扩展
│ (协议适配层) │
└────────┬────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌───────▼──────┐ ┌────▼─────┐ ┌──────▼──────┐
│ Datanode │ │ Metasrv │ │ Flownode │
│ (存储+计算) │ │(元数据+路由)│ │ (流处理+MV) │
└───────┬──────┘ └──────────┘ └─────────────┘
│
┌───────▼──────┐
│ Object Store │ ← S3/GCS/Azure Blob
│ (主存储) │
└──────────────┘
3.2 Frontend:协议适配与查询路由
Frontend 是无状态层,负责:
- 协议适配:同时支持 OpenTelemetry gRPC、Prometheus Remote Write、MySQL 协议、PostgreSQL 协议、InfluxDB Line Protocol、Elasticsearch API、Loki API
- 查询解析与优化:把 SQL / PromQL 解析为逻辑计划,通过 DataFusion 优化器生成物理计划
- 分布式查询调度:把查询拆分到对应 Datanode,聚合结果返回
关键设计:Frontend 完全无状态,可以随意扩缩容。高并发场景下(比如 AI Agent 批量查询),加 Frontend 实例就行。
# Frontend 配置示例
frontend:
http_addr: "0.0.0.0:4000"
grpc_addr: "0.0.0.0:4001"
mysql_addr: "0.0.0.0:4002"
postgres_addr: "0.0.0.0:4003"
metasrv_addr: "127.0.0.1:3002"
# 连接池配置
query_timeout: "30s"
max_concurrent_queries: 1024
3.3 Datanode:存储引擎的核心
Datanode 是 GreptimeDB 的存储和计算核心,每个 Datanode 管理若干 Region(数据分片)。
写入路径:
写入请求 → WAL(预写日志)→ Memtable(内存表)→ Flush → SST(Parquet 文件)→ Object Store
关键设计点:
- WAL 保证持久性:写入先记 WAL,确认后才返回成功。WAL 存储可配置为本地磁盘或对象存储
- Memtable 是行存+列存的混合体:写入时按行追加,查询时按列读取
- Flush 是异步的:Memtable 达到阈值后异步刷盘,不阻塞写入
- Compaction 是多层合并:类似 LSM-Tree,小文件合并为大文件,优化查询性能
// Datanode 的核心写入逻辑(简化版)
pub async fn write_region(
region_id: RegionId,
rows: Rows,
) -> Result<WriteResponse> {
// 1. 写入 WAL
let wal_entry = wal.append(region_id, &rows).await?;
// 2. 写入 Memtable
memtable.write(region_id, rows)?;
// 3. 检查是否需要 Flush
if memtable.should_flush() {
tokio::spawn(async move {
flush_region(region_id).await;
});
}
Ok(WriteResponse {
success: true,
last_entry_id: wal_entry.id
})
}
3.4 Metasrv:大脑与调度器
Metasrv 管理集群的元数据,职责包括:
- Region 路由:哪个 Region 在哪个 Datanode 上?Frontend 通过 Metasrv 查询路由
- 自动重分区:当某个 Region 写入量过大,自动拆分为多个 Region
- Autopilot:自动调优——根据负载模式调整 Flush 频率、Compaction 策略
- 安全与权限:用户认证、RBAC
Metasrv 底层使用可插拔的 KV 存储(etcd 或 RDS),保证元数据的一致性。
3.5 Flownode:流计算引擎
Flownode 是可选组件,负责持续流计算和物化视图。这是 GreptimeDB 区别于传统时序数据库的核心能力——不仅存数据,还能在数据入库时实时计算派生指标。
原始事件 → Flownode → 派生指标
(高基数) (低基数,预聚合)
典型场景:每秒产生 10 万条 HTTP 请求事件(高基数),Flownode 实时聚合为每分钟 P99 延迟、错误率等指标(低基数),供 Dashboard 直接查询。
四、代码实战:从部署到全链路可观测
4.1 快速部署:单机模式
# Docker 一键启动
docker run -p 127.0.0.1:4000-4003:4000-4003 \
-v "$(pwd)/greptimedb_data:/greptimedb_data" \
--name greptime --rm \
greptime/greptimedb:latest standalone start \
--http-addr 0.0.0.0:4000 \
--rpc-bind-addr 0.0.0.0:4001 \
--mysql-addr 0.0.0.0:4002 \
--postgres-addr 0.0.0.0:4003
端口说明:
4000:HTTP API + Dashboard4001:gRPC(OTel、内部通信)4002:MySQL 协议4003:PostgreSQL 协议
4.2 分布式部署:Kubernetes + Helm
生产环境必须分布式部署。用 Helm 是最省心的方式:
# 添加 Helm 仓库
helm repo add greptime https://greptimeteam.github.io/helm-charts/
helm repo update
# 安装(含 etcd)
helm install greptimedb greptime/greptimedb-cluster \
--namespace greptimedb --create-namespace \
--set meta.etcd.endpoints='{etcd.etcd-cluster:2379}' \
--set storage.s3.bucket=your-bucket \
--set storage.s3.root=/greptimedb \
--set storage.s3.accessKeyId=YOUR_AK \
--set storage.s3.secretAccessKey=YOUR_SK \
--set storage.s3.region=us-east-1
关键配置——自定义 values.yaml:
# values.yaml - 生产级配置
frontend:
replicas: 3
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
service:
type: LoadBalancer
datanode:
replicas: 5
resources:
requests:
cpu: "4"
memory: "16Gi"
limits:
cpu: "8"
memory: "32Gi"
storage:
wal:
type: "local"
local:
path: "/greptimedb/wal"
size: "100Gi"
data:
type: "s3"
s3:
bucket: "your-bucket"
root: "/greptimedb/data"
metasrv:
replicas: 3
resources:
requests:
cpu: "1"
memory: "2Gi"
etcd:
endpoints:
- "etcd-0.etcd:2379"
- "etcd-1.etcd:2379"
- "etcd-2.etcd:2379"
flownode:
replicas: 2
resources:
requests:
cpu: "2"
memory: "8Gi"
4.3 OpenTelemetry 采集接入
GreptimeDB 原生支持 OpenTelemetry 协议,配置 OTel Collector 直接写入:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
http:
endpoint: "0.0.0.0:4318"
processors:
batch:
timeout: "5s"
send_batch_size: 8192
exporters:
otlphttp/greptime:
endpoint: "http://greptime-frontend:4000/otlp"
headers:
"X-Greptime-DB-Name": "public"
# 如果开启了认证
# "Authorization": "Bearer your-token"
service:
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/greptime]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/greptime]
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/greptime]
部署 OTel Collector:
kubectl apply -f otel-collector-config.yaml
kubectl run otel-collector --image=otel/opentelemetry-collector-contrib:latest \
--configmap=otel-collector-config
4.4 Prometheus 迁移:无缝替换 Thanos/Mimir
已有 Prometheus 的场景,可以先用 Remote Write 双写,逐步迁移:
# prometheus.yml
remote_write:
- url: "http://greptime-frontend:4000/v1/prometheus/write"
# 同时保留原有 Thanos 写入
- url: "http://thanos-receive:10908/api/v1/receive"
GreptimeDB 完全兼容 Prometheus Remote Write 协议,你的 PromQL 查询也直接可用:
# PromQL 查询通过 HTTP API
curl "http://greptime-frontend:4000/v1/prometheus/query?query=http_requests_total{service='api-gateway'}"
4.5 数据建模实战:一个完整的可观测场景
以微服务 API 网关为例,建模一个统一的宽事件表:
-- 创建数据库
CREATE DATABASE IF NOT EXISTS observability;
-- API 请求宽事件表
CREATE TABLE observability.api_events (
-- 时间列(必须)
ts TIMESTAMP TIME INDEX,
-- 上下文字段
service STRING,
endpoint STRING,
method STRING,
status_code INT,
latency_ms DOUBLE,
-- 链路关联
trace_id STRING,
span_id STRING,
parent_span_id STRING,
-- 日志内容
request_headers JSON,
request_body STRING,
response_body STRING,
error_message STRING,
-- 业务字段
user_id STRING,
tenant_id STRING,
region STRING,
az STRING,
-- 基础设施字段
pod_name STRING,
node_name STRING,
container_id STRING,
PRIMARY KEY (service, endpoint, region)
) ENGINE=metric WITH (
append_mode = 'true'
);
为什么用 JSON 类型存 headers? GreptimeDB 原生支持 JSON 类型,可以用 ->> 运算符直接查询 JSON 字段,不需要提前展开所有 header 键:
-- 查询特定 User-Agent 的请求
SELECT ts, service, endpoint, latency_ms,
request_headers->>'user-agent' AS user_agent
FROM observability.api_events
WHERE request_headers->>'user-agent' LIKE '%curl%'
AND ts > NOW() - INTERVAL '30 minutes';
4.6 索引策略:全文索引 + 倒排索引 + 跳跃索引
GreptimeDB 支持三种索引,针对不同查询模式:
-- 为 api_events 添加索引
ALTER TABLE observability.api_events ADD FULLTEXT INDEX idx_error_msg (error_message);
ALTER TABLE observability.api_events ADD INVERTED INDEX idx_trace (trace_id);
ALTER TABLE observability.api_events ADD SKIPPING INDEX idx_status (status_code);
索引选择指南:
| 索引类型 | 适用场景 | 示例 |
|---|---|---|
| Fulltext | 全文搜索,模糊匹配 | error_message LIKE '%timeout%' |
| Inverted | 精确匹配,高基数字段 | WHERE trace_id = 'abc123' |
| Skipping | 范围查询,低基数字段 | WHERE status_code = 500 |
五、Flow 引擎:实时流处理与物化视图
5.1 Flow Task 的核心概念
Flow 是 GreptimeDB 内置的流计算引擎,核心能力是在数据写入时实时触发计算,将结果写入目标表。本质是一个持续查询:
源表(原始事件)→ Flow Task → 目标表(派生指标)
与 Kafka + Flink 的区别:不需要额外的消息队列和计算集群,Flow 运行在 GreptimeDB 内部的 Flownode 上,数据零拷贝。
5.2 实战:从原始事件派生 P99 延迟指标
-- 创建目标表:每分钟 P99 延迟
CREATE TABLE observability.latency_p99 (
ts TIMESTAMP TIME INDEX,
service STRING,
endpoint STRING,
p99_latency DOUBLE,
request_count BIGINT,
PRIMARY KEY (service, endpoint)
) ENGINE=metric;
-- 创建 Flow Task
CREATE FLOW calculate_p99
SINK TO observability.latency_p99
AS
SELECT
date_bin(INTERVAL '1 minute', ts) AS ts,
service,
endpoint,
approx_percentile_cont(latency_ms, 0.99) AS p99_latency,
count(*) AS request_count
FROM observability.api_events
GROUP BY
date_bin(INTERVAL '1 minute', ts),
service,
endpoint;
approx_percentile_cont 是 GreptimeDB 的高性能近似分位数函数,基于 T-Digest 算法,精度 99%+,内存占用远小于精确排序。
5.3 多级聚合:秒级 → 分钟级 → 小时级
实际生产中,Dashboard 通常需要不同时间粒度的指标。可以链式 Flow:
-- 分钟级错误率
CREATE TABLE observability.error_rate_minute (
ts TIMESTAMP TIME INDEX,
service STRING,
error_rate DOUBLE,
total_requests BIGINT,
error_requests BIGINT,
PRIMARY KEY (service)
) ENGINE=metric;
CREATE FLOW calc_error_rate_min
SINK TO observability.error_rate_minute
AS
SELECT
date_bin(INTERVAL '1 minute', ts) AS ts,
service,
cast(count_if(status_code >= 500) AS DOUBLE) / count(*) AS error_rate,
count(*) AS total_requests,
count_if(status_code >= 500) AS error_requests
FROM observability.api_events
GROUP BY
date_bin(INTERVAL '1 minute', ts),
service;
-- 小时级错误率(从分钟级派生,而非原始事件)
CREATE TABLE observability.error_rate_hour (
ts TIMESTAMP TIME INDEX,
service STRING,
error_rate DOUBLE,
avg_requests_per_minute DOUBLE,
PRIMARY KEY (service)
) ENGINE=metric;
CREATE FLOW calc_error_rate_hour
SINK TO observability.error_rate_hour
AS
SELECT
date_bin(INTERVAL '1 hour', ts) AS ts,
service,
avg(error_rate) AS error_rate,
avg(total_requests) AS avg_requests_per_minute
FROM observability.error_rate_minute
GROUP BY
date_bin(INTERVAL '1 hour', ts),
service;
为什么从分钟级派生而非原始事件? 原始事件基数太高(每秒 10 万条),小时级聚合直接读原始事件会导致计算量过大。从分钟级派生,计算量降 60 倍,且结果一致(加权平均)。
5.4 Flow 的容错与 Exactly-Once 语义
Flow Task 基于 WAL 实现容错:
- 每个 Flow Task 维护自己的消费位点(类似 Kafka consumer offset)
- Flownode 故障后重启,从上次位点继续消费
- 结果写入目标表时使用幂等写入(基于时间窗口 + 分组键去重),保证 Exactly-Once
Flownode 重启 → 从 WAL 位点恢复 → 重新计算窗口内数据 → 幂等写入目标表
六、存算分离与对象存储架构
6.1 为什么对象存储是主存储?
传统时序数据库(InfluxDB、Prometheus)依赖本地磁盘,带来三个问题:
- 容量上限:单机磁盘有上限,扩容必须加机器
- 成本高昂:SSD 按 TB 计价是 S3 的 10-20 倍
- 运维复杂:数据迁移、副本管理、故障恢复全靠手动
GreptimeDB 把 S3 作为主存储,本地只做缓存:
写入 → WAL(本地)→ Memtable(内存)→ Flush → Parquet(S3)
查询 → 本地缓存命中?→ 返回
↓ 未命中
从 S3 读取 → 放入缓存 → 返回
6.2 分层缓存架构
┌─────────────────────────────────┐
│ Memory Cache │ ← 最热数据,纳秒级访问
│ (Memtable + 查询结果缓存) │
├─────────────────────────────────┤
│ Local Disk Cache │ ← 温数据,微秒级访问
│ (Parquet 文件本地副本) │
├─────────────────────────────────┤
│ Object Storage (S3) │ ← 全量数据,毫秒级访问
│ (Parquet 文件 + 元数据) │
└─────────────────────────────────┘
缓存配置:
# greptimedb.toml - 缓存配置
[datanode]
[datanode.storage]
# 内存缓存大小
mem_cache_size = "4GiB"
# 本地磁盘缓存路径和大小
local_cache_path = "/data/greptimedb/cache"
local_cache_size = "100GiB"
# S3 配置
storage = "S3"
[datanode.storage.s3]
bucket = "your-bucket"
root = "/greptimedb"
region = "us-east-1"
6.3 成本对比:GreptimeDB vs 传统方案
以一个典型的中大规模场景为例:
- 数据量:10TB/天(指标 + 日志 + Trace)
- 保留期:30 天热数据 + 90 天冷数据
- 查询 QPS:1000
| 方案 | 月存储成本 | 月计算成本 | 月总成本 |
|---|---|---|---|
| Prometheus + Loki + ES | $12,000 (SSD) | $6,000 | $18,000 |
| Thanos + Loki + ES | $4,000 (S3+SSD) | $8,000 | $12,000 |
| GreptimeDB | $800 (S3为主) | $4,000 | $4,800 |
存储成本降 50 倍不是营销噱头——S3 Standard 是 $0.023/GB/月,SSD 是 $0.10-0.20/GB/月,再加上三套系统的冗余存储(同一份数据存三次),差距就是这样拉开的。
七、查询引擎:SQL + PromQL 双语言实战
7.1 SQL 查询:关系型能力
GreptimeDB 的 SQL 引擎基于 Apache DataFusion,支持标准 SQL 语法:
-- 跨信号关联查询:找出延迟高的请求对应的错误日志和 Trace
WITH slow_requests AS (
SELECT trace_id, service, endpoint, latency_ms
FROM observability.api_events
WHERE latency_ms > 1000
AND ts > NOW() - INTERVAL '10 minutes'
)
SELECT
s.ts, s.service, s.endpoint, s.latency_ms,
s.error_message,
s.trace_id
FROM observability.api_events s
JOIN slow_requests sr ON s.trace_id = sr.trace_id
WHERE s.error_message IS NOT NULL
ORDER BY s.latency_ms DESC
LIMIT 50;
7.2 PromQL 查询:兼容 Prometheus
对于已有 Grafana Dashboard 的团队,PromQL 兼容意味着零迁移成本:
# HTTP API 查询
curl -G "http://greptime-frontend:4000/v1/prometheus/query" \
--data-urlencode 'query=histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service))'
# Range query
curl -G "http://greptime-frontend:4000/v1/prometheus/query_range" \
--data-urlencode 'query=http_requests_total{service="api-gateway"}' \
--data-urlencode 'start=2026-06-17T00:00:00Z' \
--data-urlencode 'end=2026-06-17T10:00:00Z' \
--data-urlencode 'step=1m'
7.3 SQL 与 PromQL 混合查询
GreptimeDB 独有的能力——在 SQL 中调用 PromQL 函数:
-- 用 SQL 的灵活性 + PromQL 的时序函数
SELECT
service,
promql_quantile(0.99, latency_ms) AS p99,
promql_rate(count) AS rps
FROM observability.api_events
WHERE ts > NOW() - INTERVAL '1 hour'
GROUP BY service;
八、性能优化:从写入到查询的全面调优
8.1 写入优化
批量写入:单条写入的 RPC 开销是批量写入的 100 倍。GreptimeDB 的写入吞吐在 batch size 8192 时达到峰值:
# Python 批量写入示例(使用 gRPC SDK)
import greptime
client = greptime.GreptimeDBClient(
host="greptime-frontend",
port=4001,
database="observability"
)
# 批量构造行数据
rows = []
for event in events:
rows.append({
"ts": event.timestamp,
"service": event.service,
"endpoint": event.endpoint,
"latency_ms": event.latency,
# ...
})
# 一次写入,批量提交
client.insert("api_events", rows)
Region 分区策略:默认按 Primary Key 哈希分区。如果某个 service 的写入量远大于其他,可以手动调整分区数:
-- 查看当前分区分布
SELECT region_id, row_count, disk_size
FROM information_schema.region_stats
WHERE table_name = 'api_events';
-- 如果某个 Region 过大,手动分裂
ALTER TABLE observability.api_events SPLIT REGION 0 AT (service='high-traffic-svc');
8.2 查询优化
时间范围裁剪:GreptimeDB 的元数据记录了每个 Parquet 文件的时间范围,查询时可以跳过不相关的文件。确保查询条件包含时间范围:
-- ✅ 好的查询:有时间范围
SELECT * FROM api_events
WHERE ts > NOW() - INTERVAL '1 hour'
AND service = 'api-gateway';
-- ❌ 差的查询:无时间范围,全表扫描
SELECT * FROM api_events
WHERE service = 'api-gateway';
合理使用索引:
-- 为高频查询字段建索引
ALTER TABLE api_events ADD INVERTED INDEX idx_service (service);
ALTER TABLE api_events ADD INVERTED INDEX idx_trace_id (trace_id);
ALTER TABLE api_events ADD SKIPPING INDEX idx_status_code (status_code);
ALTER TABLE api_events ADD FULLTEXT INDEX idx_error (error_message);
读取副本扩展:Frontend 是无状态的,查询压力大时直接加 Frontend 实例:
# Kubernetes 扩容
kubectl scale deployment greptimedb-frontend --replicas=5 -n greptimedb
8.3 Compaction 调优
Compaction 是 LSM-Tree 架构的核心后台任务,影响查询性能和存储空间:
[datanode.compaction]
# 触发 compaction 的 SST 文件数阈值
max_files = 8
# 最大 compaction 并发数
max_concurrent_tasks = 4
# compaction 的内存预算
max_memory_budget = "2GiB"
# 时间窗口 compaction(按时间分区合并)
time_window = "1h"
8.4 冷热数据分层
对于 30 天热数据 + 90 天冷数据的场景:
-- 设置表的 TTL
ALTER TABLE observability.api_events SET TTL = '120d';
-- 对冷数据使用更激进的压缩
ALTER TABLE observability.api_events SET COMPRESSION = 'zstd(19)' WHERE ts < NOW() - INTERVAL '30 days';
实际操作中,GreptimeDB 的 Compaction 会自动对旧数据使用更高压缩比。你只需要在 S3 层面配置生命周期策略,将超过 90 天的 Parquet 文件从 Standard 转为 Glacier:
// S3 Lifecycle Policy
{
"Rules": [{
"ID": "GreptimeDBColdStorage",
"Status": "Enabled",
"Transitions": [{
"Days": 90,
"StorageClass": "GLACIER"
}]
}]
}
九、告警与触发规则
9.1 内置告警规则
GreptimeDB 支持 SQL 风格的告警规则定义:
-- 创建告警规则:错误率超过 5% 触发
CREATE RULE high_error_rate
ON observability.error_rate_minute
WHEN error_rate > 0.05
EVALUATE EVERY INTERVAL '1 minute'
FOR INTERVAL '3 minutes' -- 持续 3 分钟才触发
ACTION NOTIFY 'webhook', 'https://your-webhook.example.com/alert'
WITH PAYLOAD = json_build_object(
'alert_name', 'HighErrorRate',
'service', service,
'error_rate', error_rate,
'threshold', 0.05
);
9.2 与 Grafana 集成
安装 GreptimeDB 的 Grafana 数据源插件后,可以直接在 Grafana 中使用 SQL + PromQL 双语言:
# 安装 Grafana 插件
grafana-cli plugins install greptimedb-greptimedb-datasource
配置数据源:
apiVersion: 1
datasources:
- name: GreptimeDB
type: greptimedb-greptimedb-datasource
access: proxy
url: http://greptime-frontend:4000
jsonData:
database: observability
十、生产案例:OceanBase Cloud 的 300TB 日志实践
OceanBase Cloud 是 GreptimeDB 最大的生产用户之一:
- 规模:80+ 个 GreptimeDB 集群,管理 300TB 日志数据
- 迁移前:使用 Grafana Loki,存储成本高,大规模查询慢
- 迁移后:存储成本降 60%+,一天内日志检索亚秒级返回
关键架构决策:
- 存算分离:计算节点按流量弹性伸缩,日志写入高峰期自动扩容
- 对象存储为主:300TB 数据全量存 S3,本地只做 100GB 缓存
- 多租户隔离:每个客户独立 Region,资源隔离 + 配额限制
-- 多租户配置
CREATE TENANT tenant_a WITH (quota = '100GB');
CREATE TENANT tenant_b WITH (quota = '500GB');
-- 按租户分表
CREATE TABLE tenant_a.logs ( ... );
CREATE TABLE tenant_b.logs ( ... );
十一、GreptimeDB vs 竞品:深度对比
11.1 vs Prometheus + Thanos/Mimir
| 维度 | GreptimeDB | Prometheus + Thanos/Mimir |
|---|---|---|
| 数据类型 | 指标+日志+Trace | 仅指标 |
| 查询语言 | SQL + PromQL | PromQL |
| 存储 | 原生对象存储 | 本地磁盘 + 对象存储(sidecar) |
| 高基数 | 原生支持 | 需要额外配置 |
| 长期存储 | S3 直接写 | 需 Thanos Sidecar/Receive |
| 运维复杂度 | 一个系统 | 多组件编排 |
11.2 vs Grafana Loki
| 维度 | GreptimeDB | Grafana Loki |
|---|---|---|
| 数据模型 | 结构化宽事件 | 标签+文本 |
| 查询语言 | SQL + LogQL 兼容 | LogQL |
| 索引 | 全文+倒排+跳跃 | 仅标签索引 |
| 大规模查询 | 列存 + 并行扫描 | 全文扫描慢 |
| 结构化查询 | SQL 原生支持 | 需要 LogQL 管道 |
11.3 vs Elasticsearch
| 维度 | GreptimeDB | Elasticsearch |
|---|---|---|
| 存储成本 | S3 为主,低成本 | SSD 为主,高成本 |
| 写入吞吐 | 高(Rust + 列存) | 中(JVM + 倒排更新) |
| 时序查询 | 原生优化 | 通用查询 |
| 资源占用 | 低(无 JVM) | 高(JVM 堆) |
十二、Go/Python/Rust SDK 实战
12.1 Go SDK:高性能写入
package main
import (
"context"
"fmt"
"time"
"github.com/GreptimeTeam/greptimedb-ingester-go"
"github.com/GreptimeTeam/greptimedb-ingester-go/table"
"github.com/GreptimeTeam/greptimedb-ingester-go/table/types"
)
func main() {
// 创建客户端
cfg := greptime.NewConfig("greptime-frontend").
WithPort(4001).
WithDatabase("observability")
client, err := greptime.NewClient(cfg)
if err != nil {
panic(err)
}
defer client.Close()
// 构造表结构
tbl, err := table.New("api_events").
WithTimestampColumnName("ts").
WithTagColumns("service", "endpoint", "region").
WithFieldColumns("latency_ms", "status_code", "error_message").
Build()
if err != nil {
panic(err)
}
// 批量写入
for i := 0; i < 10000; i++ {
tbl.AddRow(
types.Timestamp(time.Now()),
types.String("api-gateway"),
types.String("/api/v1/users"),
types.String("us-east-1"),
types.Float64(float64(i%500+10)),
types.Int32(int32(200+i%5*60)),
types.String(""),
)
}
// 提交写入
affected, err := client.Write(context.Background(), tbl)
if err != nil {
panic(err)
}
fmt.Printf("写入 %d 行\n", affected)
}
12.2 Python SDK:数据分析友好
from greptime import GreptimeDBClient
client = GreptimeDBClient(
host="greptime-frontend",
port=4002, # MySQL 协议
database="observability"
)
# SQL 查询
result = client.sql("""
SELECT
service,
approx_percentile_cont(latency_ms, 0.99) AS p99,
avg(latency_ms) AS avg_latency,
count(*) AS total
FROM api_events
WHERE ts > NOW() - INTERVAL '1 hour'
GROUP BY service
ORDER BY p99 DESC
""")
for row in result:
print(f"{row['service']}: P99={row['p99']:.1f}ms, Avg={row['avg_latency']:.1f}ms, Count={row['total']}")
12.3 Rust SDK:零开销写入
use greptimedb_ingester::client::Client;
use greptimedb_ingester::table::TableBuilder;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new("http://greptime-frontend:4000")
.database("observability")
.build()?;
let mut table = TableBuilder::new("api_events")
.timestamp_column("ts")
.tag_columns(&["service", "endpoint"])
.field_columns(&["latency_ms", "status_code"])
.build();
// 批量添加行
for i in 0..10000 {
table.add_row()
.timestamp(now())
.tag("service", "api-gateway")
.tag("endpoint", "/api/v1/users")
.field("latency_ms", (i % 500 + 10) as f64)
.field("status_code", 200 + (i % 5) as i32 * 60);
}
let result = client.write(&table).await?;
println!("写入 {} 行", result.affected_rows);
Ok(())
}
十三、监控 GreptimeDB 自身
13.1 内置 Metrics
GreptimeDB 暴露了 Prometheus 格式的自身监控指标:
curl http://greptime-frontend:4000/metrics
关键指标:
| 指标 | 含义 | 告警阈值 |
|---|---|---|
greptime_write_rows_total | 总写入行数 | - |
greptime_write_latency_seconds | 写入延迟 | P99 > 500ms |
greptime_query_latency_seconds | 查询延迟 | P99 > 5s |
greptime_region_write_rows | 单 Region 写入量 | 单 Region > 100k/s |
greptime_compaction_pending_tasks | 待合并任务数 | > 100 |
greptime_wal_size_bytes | WAL 大小 | > 10GB |
13.2 用 GreptimeDB 监控 GreptimeDB(递归之美)
-- GreptimeDB 监控自身的写入延迟趋势
CREATE FLOW monitor_self_write_latency
SINK TO monitor.greptime_write_latency_p99
AS
SELECT
date_bin(INTERVAL '1 minute', ts) AS ts,
'greptimedb' AS cluster,
approx_percentile_cont(value, 0.99) AS p99_latency
FROM greptime_metrics
WHERE metric_name = 'greptime_write_latency_seconds'
GROUP BY date_bin(INTERVAL '1 minute', ts);
十四、迁移路线图:从三件套到统一引擎
14.1 渐进式迁移(推荐)
不要一次性替换所有系统,按信号逐步迁移:
阶段一:指标先行(1-2 周)
- 配置 Prometheus Remote Write 双写到 GreptimeDB
- 验证 PromQL 查询结果一致
- Grafana 添加 GreptimeDB 数据源,并行对比
- 确认无误后,切 PromQL 查询到 GreptimeDB
阶段二:日志迁移(2-4 周)
- 配置 OTel Collector 同时写 Loki 和 GreptimeDB
- 验证 SQL 查询日志的能力
- 对比查询性能(GreptimeDB 的列存 + 索引通常更快)
- 切日志查询到 GreptimeDB
阶段三:链路追踪(2-3 周)
- 配置 OTel Collector Trace 数据写入 GreptimeDB
- 验证 trace_id 关联查询
- 替换 Jaeger/Tempo
阶段四:Flow 流处理替换 Recording Rules(1-2 周)
- 把 Prometheus Recording Rules 迁移为 Flow Task
- 验证派生指标一致性
- 下线 Thanos/Mimir 的 Recording Rules
14.2 回滚方案
每个阶段都保留双写,迁移出问题时可以秒级回滚:
# OTel Collector 双写配置
exporters:
otlphttp/greptime:
endpoint: "http://greptime-frontend:4000/otlp"
otlphttp/loki:
endpoint: "http://loki:3100/otlp"
十五、总结与展望
核心收获
- 宽事件模型是 Observability 2.0 的数据基础——一种信号,一张表,一条 SQL
- 存算分离 + 对象存储是成本优化的关键——S3 比 SSD 便宜 10-20 倍,且容量无上限
- Flow 引擎让流计算内嵌于数据库,省去了 Kafka + Flink 的额外运维
- SQL + PromQL 双语言兼顾了分析灵活性和 Grafana 兼容性
- Rust + Arrow + DataFusion的技术栈决定了性能上限极高
GreptimeDB 的局限与取舍
- 不支持事务:时序/可观测场景不需要 ACID 事务,但如果你想在上面跑 OLTP,不行
- 更新代价高:列存 + LSM 架构,更新意味着重写整个 Parquet 文件。追加模式才是正确用法
- 学习曲线:SQL 查询可观测数据对习惯了 LogQL/PromQL 的团队需要适应
- 生态仍在成长:相比 Prometheus 的生态,GreptimeDB 的 Exporter/Integration 还在补全
2026 展望
根据 GreptimeDB 的 2026 路线图,几个值得期待的方向:
- AI Agent 原生查询优化:针对 LLM Agent 的自然语言→SQL 查询路径优化
- 更完善的 Autopilot:自动调优 Region 分区、Compaction 策略、缓存大小
- 边缘到云的统一:资源受限设备上的轻量 GreptimeDB 实例,数据同步到云端
- 更丰富的分析函数:异常检测、预测、根因分析等内嵌算法
GreptimeDB 代表了可观测性基础设施的演进方向——不是在三个系统之间做选择,而是用一个统一的引擎消除选择本身。对于正在被三件套的运维成本和跨系统关联查询折磨的团队,现在就是试水的最佳时机。
相关资源:
- 官方文档:https://docs.greptime.com
- GitHub 仓库:https://github.com/GreptimeTeam/greptimedb
- Helm Charts:https://github.com/GreptimeTeam/helm-charts
- OTel Collector 配置:https://docs.greptime.com/user-guide/ingest-data/for-observability/opentelemetry/