Loki 2.5 版本深度解析:查询性能增强、配置迁移与升级实战指南
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
Loki 2.5 是距离 2.4 发布近 6 个月后的重要版本,围绕"查询更快的并行化"与"更灵活的数据摄入"两条主线,引入了 regexp 匹配加速、二进制运算并行化、新对象存储 Schema、存储请求 Hedging 以及 Promtail 的多项新摄入方式。本文基于 v2-5 版本说明,结合当前仓库源码与 2.x 升级指南 中的迁移细节,系统梳理 2.5 的全部增强点、必须执行的配置迁移动作、默认值变化以及关键 Bug 修复,帮助你在升级前后准确评估影响面并完成平滑迁移。
版本概览:从 2.4 到 2.5
Loki 2.5 延续了项目"Like Prometheus, but for logs"的设计哲学,在保持日志查询体验一致的同时,把优化重点放在了三件事上:
- 查询性能:让常见 LogQL 正则表达式、二元运算与大规模并行查询跑得更快;
- 存储架构:通过新 Schema 缓解对象存储限流,引入请求 Hedging 对抗长尾延迟;
- 摄入能力:Promtail 获得 Docker Daemon 直连、Cloudflare、GELF 等多种新数据源接入方式。
同时,2.5 也包含了一批影响启动行为的配置变更(尤其是split_queries_by_interval的位置迁移),以及大量查询正确性、查询取消与稳定性相关的 Bug 修复。以下逐一展开。
核心性能与架构增强
Go regexp 性能优化:为常见日志正则场景加速
Loki 2.5 引入了对 Go 标准库regexp的深度优化成果。社区开发者 @bboreham 深入分析了 Goregexp库的实现(对应 PR 5315),并创建了一个性能改进的 fork,大幅提升了 Loki 中最常见正则使用场景的执行速度。
在日志查询中,|~ "regex"与|= "filter"等 LogQL 过滤操作是最高频路径之一,正则引擎的每次微小改进都会在长时间窗口、大标签基数查询中被显著放大。这一改动直接作用于 pkg/logql 下的 LogQL 执行引擎,属于对查询热路径的底层提速。
二进制运算显著加速:充分利用 Loki 的并行能力
版本说明指出,二进制运算(binary operations)现在显著更快,其关键在于"充分发挥 Loki 的并行性"(PR 5317)。在 LogQL 中,诸如rate(...) / sum(...)、count_over_time(...) * 2等运算会在查询引擎中被拆解为可并行执行的子任务。
从当前源码结构可以印证这一点:Loki 的查询执行由 pkg/engine 统一编排,而 2.5 通过让二进制运算也走并行调度路径,使拆分后的每个分片在独立 goroutine 中完成计算后再合并结果,从而把多核能力真正用起来。
新 Schema:更多路径前缀,规避 S3 限流
2.5 提供了一种新的存储 Schema(PR 5054),其核心思路是使用更多路径前缀来避免触发 S3 的速率限制。对象存储(如 S3)在单个前缀下的请求速率存在上限,当大量 chunk 落在同一目录前缀下时,高并发查询容易撞上限流;新 Schema 通过增加路径前缀的分散程度,把请求均匀打散到更多前缀上,从而降低单前缀压力。
同样的 Schema 变更也同步应用到了文件系统存储(filesystem store)(PR 5291):此前所有 chunk 都被放进同一个目录,升级后改为分散到多个子目录,既避免单目录文件数量过大,也减少了文件系统层面的压力。相关 Schema 定义与对象存储客户端实现可参见 pkg/storage 目录。
存储请求 Hedging:对抗长尾延迟
Hedging(对冲请求)是一种经典的分布式系统技巧:当一个请求超过设定延迟仍未返回时,并发发出第二个(乃至更多)重复请求,取先返回者作为结果,从而显著压缩高并发查询场景下的长尾延迟。Loki 2.5 将这一能力引入对象存储访问层(PR 4826),对应配置位于storage_config块下。
从当前仓库源码看,Hedging 的实现位于 pkg/storage/chunk/client/hedging/hedging.go,其配置结构体如下:
type Config struct { // At is the duration after which a second request will be issued. At time.Duration `yaml:"at"` // UpTo is the maximum number of requests that will be issued. UpTo int `yaml:"up_to"` // The maximun of hedge requests allowed per second. MaxPerSecond int `yaml:"max_per_second"` }对应的 YAML 配置形如:
storage_config: hedging: at: 250ms # 超过该时长未返回则发起第二个请求;默认为 0(禁用) up_to: 2 # 最多并发发出的请求数,默认为 2 max_per_second: 5 # 每秒允许的最大对冲请求数,默认为 5从源码可以看到,其底层基于hedgedhttp库构建http.RoundTripper,并实现了请求级速率限制与winner 追踪:当对冲请求总数超过MaxPerSecond时返回ErrTooManyHedgeRequests。在 pkg/storage/chunk/client/aws/s3_storage_client.go 的NewS3ObjectClient中,会基于同一份hedgingCfg同时构建普通与 Hedged 两套 S3 客户端,供不同场景选用。
Hedging 还自带完整可观测性指标(前缀loki_),便于验证其实际效果:
hedged_requests_total:发出的对冲请求总数;hedged_requests_rate_limited_total:因速率限制被拒绝的对冲请求数;hedged_requests_won_total:在对冲请求或原始请求中率先完成的请求数(按type维度区分)。
使用建议:at的取值应略高于对象存储的典型 P50/P75 延迟;若设得过小会频繁触发重复请求,反而放大存储压力。默认0表示禁用,需要显式开启。
Promtail 新摄入能力
2.5 为 Promtail 引入了三种全新的日志接入方式与两级速率限制能力,让日志采集的覆盖面与可控性同时提升:
直接从 Docker Daemon 做服务发现与 Tail
Promtail 现在可以直接对接 Docker Daemon(PR 4911):通过 Docker API 动态发现容器,并直接 Tail 容器日志,无需依赖 Docker 的 json-file 日志驱动落盘解析。这对于短生命周期容器、以及需要动态跟随容器启停的场景尤其实用,避免了日志文件尚未刷盘导致的采集缺口。
直接从 Cloudflare 拉取日志
新增 Cloudflare 数据源支持(PR 4813),Promtail 可以直接从 Cloudflare 拉取日志。该能力通常配合 Cloudflare 的 Logpush/API 接口使用,将边缘日志直接送入 Loki,省去中间转发环节。
接收 GELF 格式日志
Promtail 现在可以以 Graylog Extended Log Format(GELF)接收日志(PR 4744),即 Promtail 本身可以作为 GELF 协议的服务端监听并解析上报的日志。对于已按 GELF 标准输出日志的既有系统(许多日志库与中间件原生支持 GELF),可以零改造接入 Loki。
客户端侧全局速率限制与 Pipeline 级速率限制
2.5 为 Promtail 增加了两层级联的速率限制能力:
- 客户端全局速率限制(PR 5031):在 Promtail 的客户端(client)层面限制整体推送速率,防止采集量超过 Loki 的接收能力;
- Pipeline 可配置速率限制(PR 5051):在 pipeline 内部按 stage 进行限流,允许对特定标签或特定来源的日志做差异化速率控制。
两者结合,可以在采集源头就做好流量整形,而不是等到 Distributor 侧触发全局限额再被拒绝。
升级注意事项:必须执行的配置迁移
升级到 2.5 之前,请务必通读 2.x 升级指南 中 2.5.0 一节。本节列出最关键、影响面最大的变更。
split_queries_by_interval配置迁移(启动阻断项)
这是 2.5 中最可能"直接导致 Loki 启动失败"的变更。在 2.4 及之前,split_queries_by_interval可以同时定义在两个位置:
query_range: split_queries_by_interval: 10m和/或
limits_config: split_queries_by_interval: 10m在2.5.0 中它只能定义在limits_config区块:
limits_config: split_queries_by_interval: 30m如果你没有把该参数从query_range区块中删除,Loki 将直接启动失败。这是升级前必须完成的清理动作。
同时注意两点行为变化:
- 默认值变化:
split_queries_by_interval的默认值由0(禁用拆分)变为30m,即未显式配置时也会按 30 分钟间隔拆分查询; - CLI 标志不变:对应的命令行参数仍然是
querier.split-queries-by-interval,习惯用 flag 配置的部署无需改动。
从当前仓库源码 pkg/validation/limits.go 可以确认其最终归属:QuerySplitDuration字段的 YAML 标签即为split_queries_by_interval,位于 limits 配置结构中,并通过 flagquerier.split-queries-by-interval注册(注释明确说明"按时间间隔拆分查询并并行执行,值 0 禁用时间拆分,同时也决定启用结果缓存时缓存键的选取方式")。
拆分逻辑本身由 pkg/querier/queryrange/split_by_interval.go 执行:在 query-frontend 侧按时间区间把大查询切成多个子查询并并行下发;而在 pkg/querier/queryrange/limits/definitions.go 中可以看到,除了split_queries_by_interval外,2.5 之后还陆续细化了指标查询(split_instant_metric_queries_by_interval)、元数据查询(split_metadata_queries_by_interval)等独立拆分间隔,演进方向是"按查询类型差异化拆分"。
默认开启更多并行化:所有查询默认拆分与分片
2.5 继续推动"所有部署形态(包括单二进制模式)都默认利用并行性"。升级后:
- 所有查询默认会被拆分(split)并分片(shard),对应配置项
parallelize_shardable_queries的默认值变为true; - 如果你此前未显式开启这些选项,升级后 Loki 进程在查询期间的内存和 CPU 占用会上升,这是并行化的预期代价,需要通过容量评估确认资源余量。
分片(sharding)会将一次查询按标签集进一步切分为更细粒度的子查询,与时间拆分叠加后,单个用户查询会被拆成大量可并行的工作单元。这也解释了为何 2.5 要同步修复一批"sharding 下的查询正确性"问题(见下文 Bug 修复章节)。
其他默认配置值变化
2.5.0 还调整了多项默认值,升级指南中均有列出,摘录关键几项:
| 配置项 | 所属区块 | 旧默认值 | 新默认值 | 影响说明 |
|---|---|---|---|---|
parallelize_shardable_queries | query_range | — | true | 所有可分片查询默认并行分片执行 |
split_queries_by_interval | limits_config | 0s | 30m | 查询默认按 30 分钟间隔拆分 |
max_chunk_age | ingester | 1h | 2h | 块在内存中驻留更久再刷盘,减少对象存储写入次数 |
query_ingesters_within | querier | 0s | 3h | 结束时间超过 3h 的查询不再下发到 ingester;若经常写入旧数据需改回0s |
max_concurrent | querier | 20 | 10 | 单进程最大并发查询数下调 |
match_max_concurrent | frontend_worker | — | true | 取代parallelism设置,单进程查询并行度改由querier.max_concurrent控制 |
flush_op_timeout | ingester | 10s | 10m | 启动重放大 WAL 时避免context deadline exceeded刷盘失败 |
其中query_ingesters_within从0s变为3h值得特别留意:如果你有回填旧数据的习惯,需要主动把它改回0s,否则超过 3 小时的旧数据查询将不会命中 ingester 中的新写入数据。
丢弃旧的 Prometheus Rules 配置格式
2.5 移除了对旧版(内部命名为v0)Prometheus 告警规则格式的支持,仅保留 2.x 格式。若你仍在用类似下面的旧格式字符串,需要在升级前转换为新版:
groups: - name: example rules: - alert: HighErrorRate expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5 for: 10m labels: severity: page annotations: summary: High request latency旧格式形如ALERT <name> IF <expr> [FOR <duration>] [LABELS ...] [ANNOTATIONS ...]的文本声明,2.5 后不再被 Ruler 解析。
使用情况上报(Usage Reporting)
Loki 2.5 加入了匿名使用统计上报功能,将使用情况以匿名形式回报给 Grafana Labs,用于指导功能与文档的优先级决策。官方承诺不收集任何私有信息,所有报告完全匿名,并希望用户默认保持开启以帮助项目成长。
该功能的实现位于 pkg/analytics/reporter.go:Reporter服务按约 4 小时为周期上报统计,通过对象存储中的种子文件(loki_cluster_seed.json)与 KV 存储中的 token 来为集群生成稳定且匿名的身份标识;同时采集进程级指标,不涉及日志内容本身。
若你希望关闭该功能,在配置中显式设置即可:
analytics: reporting_enabled: false对应的 CLI 标志为reporting.enabled(默认true)。从源码可见,当reporting_enabled为false时,NewReporter直接返回 nil,上报服务不会被启动,因此该开关是"完全关闭"而非"降低频率"。
Bug 修复总结
2.5.0 修复了大量问题,完整列表见仓库根目录的 CHANGELOG。以下是按类别归纳的重要修复:
查询正确性(Query Correctness)
分片/拆分的默认开启,使得这批正确性修复尤为关键:
- PR 5474:当 LogQL 表达式变更了标签(labels mutated)时,禁用 count/avg 聚合的分片,避免聚合结果错误;
- PR 5444:分片执行时不再插入缺失的数据点,保证采样/步长对齐的准确性;
- PR 5423:正确设置 headblock 迭代器的 hash 值,避免分片合并时数据错位;
- PR 5289:修复使用 LogQL 变更 Labels 时日志去重(deduplication)失效的问题;
- PR 5006:修复查询 step 大于拆分间隔时查询拆分异常的问题。
查询取消(Query Cancellation)
- PR 5113:修复 query-frontend 与 query-scheduler 之间的取消(cancel)传递问题,避免已取消查询继续占用资源;
- PR 5080:在 querier 的部分下游请求中正确处理
context取消; - PR 5075:修复 frontend 中一个可能的取消(cancellation)问题。
其他重要修复
- PR 5413:修复 Azure blob 客户端中的一个死锁;
- PR 5334:修复 live tailing(实时追踪)中可能导致内存爆炸的问题;
- PR 5144:修复 ruler 使用 Basic Auth 进行 remote write 时的问题;
- PR 4741:修复 retention 未能完全清理索引的问题。
升级检查清单
综合以上内容,升级到 Loki 2.5 建议按如下清单逐项核对:
- 移除
query_range区块中的split_queries_by_interval(否则启动失败),改由limits_config统一配置,或依赖新的30m默认值; - 评估并行化默认开启带来的内存/CPU 增量,必要时通过
querier.max_concurrent与parallelize_shardable_queries调整; - 若存在旧数据回填场景,将
querier.query_ingesters_within显式设回0s; - 检查 Ruler 告警规则文件是否仍在使用已废弃的 Prometheus 1.x 格式,提前转换为 2.x 格式;
- 如业务对数据隐私有严格要求,显式设置
analytics.reporting_enabled: false; - 关注存储层可选优化项
storage_config.hedging,结合对象存储延迟基线评估是否开启; - 核对升级后的查询结果与旧版本是否一致,尤其是使用了标签变更、聚合分片路径的 LogQL 查询。
详细升级步骤与全部变更点请参阅 2.x 升级指南(2.5.0 一节)与 CHANGELOG,发行说明原文见 v2-5.md。
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考