测试结论
GreptimeDB 在日志写入场景中表现出色,写入吞吐能力约为 Loki 的 1.5 倍;在使用低成本的对象存储时,GreptimeDB 写入性能依然保持较高水平,未出现明显下降; 借助全文索引和查询缓存,GreptimeDB 在关键词搜索和聚合等典型查询场景中,查询速度较 Loki 快 40 到 80 倍。对于热点查询,性能提升更是高达 500 倍以上; GreptimeDB 采用高效的列式存储和压缩算法,数据压缩率显著优于 Loki,而存储占用约为 Loki 的二分之一。
测试场景
测试数据
我们选取了线上环境中过去 3 个月的 Etcd 集群监控日志作为数据源,尽可能地还原实际的日志分析场景。每条日志包含时间戳、日志级别、Pod 信息、IP 地址、原始消息等字段,原始日志数据如下:
{"pod_name":"etcd-1","container_name":"etcd","pod_ip":"10.0.169.66","pod_labels":"
{\"app.kubernetes.io/component\":\"etcd\",\"app.kubernetes.io/instance\":\"etcd\",\"app.kubernetes.io/managed-by\":\"Helm\",\"app.kubernetes.io/name\":\"etcd\",\"apps.kubernetes.io/pod-index\":\"1\",\"controller-revision-hash\":\"etcd-7dc48bf797\",\"helm.sh/chart\":\"etcd-9.0.0\",\"statefulset.kubernetes.io/pod-name\":\"etcd-1\"}","message":"
{\"level\":\"debug\",\"ts\":\"2025-06-17T14:41:34.06945Z\",\"caller\":\"etcdserver/server.go:2231\",\"msg\":\"applyEntryNormal\",\"raftReq\":\"header:<ID:15307056038004875511 > alarm:<> \"}","timestamp":"2025-06-17T14:41:34.069544309"}
结构如下:
我们使用 Vector 这个开源可观测数据 Pipeline 来解析并写入上面的数据。整体测试的流程如图所示:

软硬件说明
硬件平台
其中数据库资源限制为 8 核 CPU 和 16GB 内存,剩余资源分配给 Vector 和监控组件。
软件版本
读写性能测试
写入表现
通过对比测试写入性能,我们可以得出以下结论:
GreptimeDB 在写入性能上表现优异:GreptimeDB 在写入测试中达到了 121k rows/s 的吞吐量,表现非常出色。在使用 OSS 对象存储作为存储底座时,GreptimeDB 写入性能依然维持在较高水平,达到 102k rows/s。 Loki 的写入吞吐量较低:相比之下,Loki 的写入性能为 78k rows/s,明显低于 GreptimeDB。
总体来看,GreptimeDB 在写入场景中展现了更高的吞吐量,使用对象存储的情况下,依然保持了出色的写入性能。
查询表现
在这次查询性能测试中,我们挑选了几种常见的查询类型,覆盖了日志分析中典型的使用场景:
关键词搜索:支持在日志中按关键词查找,同时结合时间范围和分页,用于快速定位特定信息; 按分钟聚合:统计每分钟内包含某关键词的日志数量,并按 Pod 维度分组,可用于查看一段时间内的变化趋势; Top error/warn pod 查询:统计产生 Warn 或 Error 日志最多的 Pod,帮助快速找到可能出问题的节点; Distinct 查询:用于查找某个字段的所有不重复值,比如有哪些服务名或主机名。
关键词搜索

图表横轴为查询的时间范围,纵轴为查询耗时(毫秒,越低越好); Loki 查询 30d、60d、90d 时间范围的查询时超时,图中留空。
按分钟聚合

Top error/warn pods 查询

Distinct 查询

通过上述数据对比可以发现:
借助全文索引,GreptimeDB 在关键词搜索和分钟级聚合等典型场景中展现出显著的查询优势。与 Loki 相比,GreptimeDB 的查询速度提升可达 40 到 80 倍;
对于部分热点查询场景,由于 GreptimeDB 内置的查询结果缓存机制,性能差距甚至超过 500 倍,大幅减少了重复查询的响应时间;
在 Distinct 查询场景中,Loki 表现更佳。为提升此类查询效率,GreptimeDB 可结合 Flow 功能对 Distinct 值进行预计算优化。
资源占用及压缩率
CPU 使用率:GreptimeDB 的 CPU 平均使用率约为 30%~35%,与 Loki 的 33.75% 相当,表现较为接近; 内存使用:GreptimeDB 在内存平均使用上略高于 Loki,约为 1.75GB 至 1.9GB,而 Loki 约为 1.4GB。内存峰值方面,GreptimeDB 和 Loki 均在 2.1GB 左右,差异不大。
总体来看,GreptimeDB 在保持更高写入性能的同时,资源消耗与 Loki 相当。
压缩率
原始数据文件(NDJSON)大小约为 83GB。在所有数据写入完毕后,我们统计各个数据库产品的持久化目录大小,可以计算得到以下压缩率:
GreptimeDB 在存储日志数据时表现出较高的压缩效率,数据占用仅约原始数据的 3%,压缩后数据大小约为 3.03 GB;相比之下,Loki 的数据压缩比为约 8%,数据大小为 6.59 GB,存储占用明显高于 GreptimeDB。
在对存储效率和成本敏感的场景中,GreptimeDB 更具优势。
附录
查询语句
Range Match
SQL
SELECT
*
FROM
${TABLE_NAME}
WHERE
message @@ '${KEYWORD}'
ANDtimestamp >= '${START_TIME}'
ANDtimestamp <= '${END_TIME}'
ORDERBY
timestampDESC
OFFSET ${OFFSET}
LIMIT ${LIMIT}
LogQL
"http://${DB_HOST}:${DB_PORT}/loki/api/v1/query_range" \
-d 'query={level="info"} |= `${QUERY}`' \
-d "limit=${LIMIT}" \
-d "direction=backward" \
-d "start=${START_TIME}" \
-d "end=${END_TIME}"
Minute bucket with match
SQL
SELECT
date_bin('1m'::interval, timestamp) AS minute_bucket,
pod_name,
pod_ip,
COUNT(*) AS log_count
FROM ${TABLE_NAME}
WHERE
message @@ '${KEYWORD}'
ANDtimestamp >= '${START_TIME}'
ANDtimestamp <= '${END_TIME}'
GROUPBY minute_bucket, pod_name, pod_ip
ORDERBY minute_bucket ASC, pod_ip ASC;
LogQL
"http://${DB_HOST}:${DB_PORT}/loki/api/v1/query_range" \
-d 'count_over_time({level="info"} |= `${QUERY}` [1m])' \
-d "direction=backward" \
-d "start=${START_TIME}" \
-d "end=${END_TIME}"
Top error warn pods
SQL
SELECT pod_name, pod_ip, COUNT(*) AS cnt
FROM ${TABLE_NAME}
WHERE
(level = 'warn'ORlevel = 'error')
ANDtimestamp >= '${START_TIME}'
ANDtimestamp <= '${END_TIME}'
GROUPBY pod_name, pod_ip
ORDERBY cnt DESC, pod_ip DESC
LIMIT ${LIMIT};
LogQL
"http://${DB_HOST}:${DB_PORT}/loki/api/v1/query" \
-d 'query=topk(${LIMIT}, sum by(pod_ip, pod_name) (count_over_time({level=~"warn|error"}[${RATE_INTERVAL}])))' \
-d "start=${START_TIME}" \
-d "end=${END_TIME}"
Distinct
SQL
SELECT
DISTINCT(${QUERY})
FROM
${TABLE_NAME}
WHERE
timestamp >= '${START_TIME}'
AND timestamp <= '${END_TIME}'
ORDER BY ${QUERY}
LIMIT ${LIMIT};
LogQL
"http://${DB_HOST}:${DB_PORT}/loki/api/v1/label/${QUERY}/values" \
-d "start=${START_TIME}" \
-d "end=${END_TIME}"
配置
GreptimeDB
Vector 配置
[sources.logfile]
type = "file"
include = ["/data/etcd_logs/output.json"]
[transforms.parse_log]
type = "remap"
inputs = ["logfile"]
source = '''
. = parse_json!(.message)
.parsed_message, err = parse_json(.message)
if is_object(.parsed_message) {
.level = .parsed_message.level
}
del(.parsed_message)
if exists(.timestamp) {
.timestamp = parse_timestamp!(.timestamp, "%Y-%m-%dT%H:%M:%S.%f")
}
.pod_name = .pod_name
.container_name = .container_name
.pod_ip = .pod_ip
.message_id = .message_id
.pod_labels, err = parse_json(.pod_labels)
if is_object(.pod_labels) {
."app.kubernetes.io/component" = .pod_labels."app.kubernetes.io/component"
."app.kubernetes.io/instance" = .pod_labels."app.kubernetes.io/instance"
."app.kubernetes.io/managed-by" = .pod_labels."app.kubernetes.io/managed-by"
."app.kubernetes.io/name" = .pod_labels."app.kubernetes.io/name"
."apps.kubernetes.io/pod-index" = .pod_labels."apps.kubernetes.io/pod-index"
."controller-revision-hash" = .pod_labels."controller-revision-hash"
."helm.sh/chart" = .pod_labels."helm.sh/chart"
."statefulset.kubernetes.io/pod-name" = .pod_labels."statefulset.kubernetes.io/pod-name"
}
del(.pod_labels)
'''
[sinks.greptime_logs]
type = "greptimedb_logs"
inputs = ["parse_log"]
compression = "gzip"
dbname = "public"
endpoint = "http://greptimedb:4000"
pipeline_name = "greptime_identity"
extra_params = { "custom_time_index" = "timestamp;datestr;%Y-%m-%dT%H:%M:%S%.9f%#z" }
table = "demo_logs"
batch.max_events = 1000
[sources.vector_metrics]
type = "internal_metrics"
[sinks.prometheus_exporter]
type = "prometheus_exporter"
inputs = ["vector_metrics"]
address = "0.0.0.0:9598"
建表语句
CREATE TABLEIFNOTEXISTS`demo_logs` (
`message`STRINGNULL FULLTEXT INDEX,
`level`STRINGNULL SKIPPING INDEX,
`target`STRINGNULL SKIPPING INDEX,
`pod_name`STRINGNULL SKIPPING INDEX,
`container_name`STRINGNULL SKIPPING INDEX,
`pod_ip`STRINGNULL SKIPPING INDEX,
`app.kubernetes.io/component`STRINGNULL SKIPPING INDEX,
`app.kubernetes.io/instance`STRINGNULL SKIPPING INDEX,
`app.kubernetes.io/managed-by`STRINGNULL SKIPPING INDEX,
`app.kubernetes.io/name`STRINGNULL SKIPPING INDEX,
`apps.kubernetes.io/pod-index`STRINGNULL SKIPPING INDEX,
`controller-revision-hash`STRINGNULL SKIPPING INDEX,
`helm.sh/chart`STRINGNULL SKIPPING INDEX,
`message_id`STRINGNULL SKIPPING INDEX,
`timestamp`TIMESTAMP(9) NOTNULL,
TIMEINDEX (`timestamp`)
)
ENGINE=mito
WITH(
skip_wal = 'true',
append_mode = 'true'
);
Loki
软件配置
auth_enabled: false
server:
http_listen_port:3100
grpc_listen_port:9096
common:
instance_addr:0.0.0.0
path_prefix:/tmp/loki
storage:
filesystem:
chunks_directory:/tmp/loki/chunks
rules_directory:/tmp/loki/rules
replication_factor:1
ring:
kvstore:
store:inmemory
query_range:
results_cache:
cache:
embedded_cache:
enabled:true
max_size_mb:100
schema_config:
configs:
-from:2020-10-24
store:tsdb
object_store:filesystem
schema:v13
index:
prefix:index_
period:24h
ruler:
alertmanager_url:http://localhost:9093
analytics:
reporting_enabled:false
ingester:
max_chunk_age:4800h
limits_config:
reject_old_samples:true
retention_period:365d
max_query_lookback:365d
max_query_length:0h
ingestion_rate_mb:10240
ingestion_burst_size_mb:10240
max_streams_per_user:10000000
max_global_streams_per_user:10000000
per_stream_rate_limit:10240M
per_stream_rate_limit_burst:10240M
cardinality_limit:20000000
Vector 配置
[sources.logfile]
type = "file"
include = ["/data/etcd_logs/output.json"]
[transforms.parse_log]
type = "remap"
inputs = ["logfile"]
source = '''
. = parse_json!(.message)
.parsed_message, err = parse_json(.message)
if is_object(.parsed_message) {
.level = .parsed_message.level
}
del(.parsed_message)
if exists(.timestamp) {
.timestamp = parse_timestamp!(.timestamp, "%Y-%m-%dT%H:%M:%S.%f")
}
.pod_name = .pod_name
.container_name = .container_name
.pod_ip = .pod_ip
.pod_labels, err = parse_json(.pod_labels)
if is_object(.pod_labels) {
."app.kubernetes.io/component" = .pod_labels."app.kubernetes.io/component"
."app.kubernetes.io/instance" = .pod_labels."app.kubernetes.io/instance"
."app.kubernetes.io/managed-by" = .pod_labels."app.kubernetes.io/managed-by"
."app.kubernetes.io/name" = .pod_labels."app.kubernetes.io/name"
."apps.kubernetes.io/pod-index" = .pod_labels."apps.kubernetes.io/pod-index"
."controller-revision-hash" = .pod_labels."controller-revision-hash"
."helm.sh/chart" = .pod_labels."helm.sh/chart"
."statefulset.kubernetes.io/pod-name" = .pod_labels."statefulset.kubernetes.io/pod-name"
}
del(.pod_labels)
'''
[sinks.loki]
type = "loki"
inputs = [ "parse_log" ]
compression = "gzip"
endpoint = "http://loki:3100"
out_of_order_action = "accept"
path = "/loki/api/v1/push"
batch.max_events = 1000
encoding.codec = "json"
healthcheck = false
remove_timestamp = false
[sinks.loki.labels]
source = "vector"
level = "{{level}}"
pod_name = "{{pod_name}}"
container_name = "{{container_name}}"
pod_ip = "{{pod_ip}}"
[sources.vector_metrics]
type = "internal_metrics"
[sinks.prometheus_exporter]
type = "prometheus_exporter"
inputs = ["vector_metrics"]
address = "0.0.0.0:9598"
监控数据
Loki 写入

Loki 在写入过程中,磁盘的峰值占用 60GiB(Disk Usage 黄色线段)。
GreptimeDB 写入

GreptimeDB OSS 写入

查询

其中黄色线条为为 Loki 资源占用,绿色线条为 GreptimeDB。
关于 Greptime
Greptime 格睿科技专注于打造新一代可观测数据库,服务开发者与企业用户,覆盖从从边缘设备到云端企业级部署的多样化需求。
GreptimeDB 开源版:开源、云原生,统一处理指标、日志和追踪数据,适合中小规模 IoT,个人项目与可观测性场景; GreptimeDB 企业版:面向关键业务,提供更高性能、高安全性、高可用性和智能化运维服务; GreptimeCloud 云服务:全托管云服务,零运维体验“企业级”可观测数据库,弹性扩展,按需付费。
欢迎加入开源社区参与贡献与交流!推荐从带有 good first issue
标签的任务入手,一起共建可观测未来。

⭐ Star us on GitHub:https://github.com/GreptimeTeam/greptimedb
📚 官网:https://greptime.cn/
📖 文档:https://docs.greptime.cn/
🌍 Twitter:https://twitter.com/Greptime
💬 Slack:https://greptime.com/slack
💼 LinkedIn:https://www.linkedin.com/company/greptime/
往期精彩文章:



点击「阅读原文」,立即体验 GreptimeDB!




