2. Loki 核心概念详解

在深入使用 Loki 之前,必须理解其三大核心抽象:日志流(Log Stream)、标签(Label) 和 块(Chunk)。这三个概念构成了 Loki 的存储与查询基石,也是它与传统 ELK 架构最根本的差异所在。

2.1 日志流(Log Stream)

日志流是 Loki 中最基本的日志组织单元。它代表一组具有完全相同标签集合的日志条目,按时间顺序排列。每个日志流在 Loki 内部都有一个唯一的 Stream ID(由所有标签的键值对哈希生成)。

  • 流标识:由标签组合唯一确定,例如 {job="nginx", env="prod", host="web-01"} 构成一个流。
  • 写入单元:Promtail 或其他采集器将日志行追加到对应的流中。
  • 查询范围:LogQL 查询时,通过标签匹配器选择目标流,再在流内进行时间范围过滤或内容搜索。
关键理解:在 ELK 中,日志是独立的文档(Document),每个文档可以有不同的字段结构;而在 Loki 中,日志是流内的连续行,所有行共享相同的标签元数据。这种设计大幅降低了索引开销。

2.2 标签(Label)

标签是 Loki 的元数据索引,每个标签是一个键值对(Key-Value Pair)。Loki 只对标签建立倒排索引,不对日志内容建立全文索引。标签的设计直接影响查询性能和存储成本。

标签特性 说明 最佳实践
基数(Cardinality) 标签值可能的取值数量 保持低基数(< 10000),避免使用用户ID、请求ID等高基数字段
命名规范 推荐使用小写字母、下划线 job、instance、level、service
常见标签 环境、服务名、主机、日志级别 env="prod"、service="gateway"
动态标签 从日志行中提取的临时标签 使用 | label_format 或 | line_format 在查询时派生
高基数陷阱:如果将每次请求的 TraceID 或 UserID 设为标签,会导致标签基数爆炸(数百万级别),Loki 的倒排索引将急剧膨胀,写入性能下降,存储成本飙升。正确的做法是将高基数信息保留在日志行内容中,通过 LogQL 的 |~ 正则表达式进行搜索。

2.3 块(Chunk)

块是 Loki 的实际数据存储单元。每个块包含一段时间窗口内、属于同一个日志流的连续日志行,经过压缩后存储到对象存储(如 S3、GCS、MinIO)中。

  • 块生命周期:采集器推送日志 → Ingester 在内存中构建块 → 块达到大小或时间阈值 → 刷新到对象存储 → 长期保存。
  • 块压缩:默认使用 gzip 或 snappy 压缩,压缩比通常可达 5:1 ~ 10:1。
  • 块元数据:每个块记录其所属流、时间范围、行数、原始大小、校验和等信息,存储在 Index 中用于快速定位。
性能特征:查询时,Loki 先通过标签索引找到匹配的流,再根据时间范围定位到对应的块,最后从对象存储中读取并解压块内容。块的大小(默认 1.5MB 未压缩)和压缩算法直接影响查询延迟。
Loki 核心概念关系图 Loki 核心概念关系图 日志源(Promtail) 日志流(Log Stream) 标签组合唯一标识:{job, env, host} 标签 元数据索引 块 压缩存储 对象存储(S3/GCS) 倒排索引 查询路径 LogQL 查询 → 标签匹配器 → 倒排索引定位流 → 时间范围定位块 → 读取对象存储 → 解压返回 全程只扫描匹配的块,不建立全文索引,查询速度与日志总量无关,只与匹配的块数量相关

2.4 与传统 ELK 的对比

ELK(Elasticsearch + Logstash + Kibana)与 Loki 代表了两种截然不同的日志处理哲学:索引优先 vs. 标签优先。以下从多个维度进行对比:

对比维度 ELK(Elasticsearch) Loki
索引策略 对日志所有字段建立倒排索引(全文索引) 仅对标签建立倒排索引,日志内容不索引
存储模型 文档(Document)→ 分片(Shard)→ 索引(Index) 日志行 → 日志流(Log Stream)→ 块(Chunk)
写入成本 高:分词、索引、写入副本,CPU/内存开销大 低:仅压缩、写入对象存储,无索引开销
存储成本 高:索引数据通常占原始日志的 50%~100% 低:仅存储压缩后的日志,通常为原始大小的 10%~20%
查询方式 全文搜索 + 聚合分析(DSL) 标签过滤 + 内容搜索(LogQL)
查询延迟 毫秒级(索引命中) 秒级(需扫描块内容)
适用场景 复杂搜索、聚合分析、业务洞察 实时监控、故障排查、Kubernetes 日志
运维复杂度 高:需要管理集群、分片、索引生命周期 低:无状态组件 + 对象存储,易于扩展
架构哲学差异:ELK 追求“搜索一切”,通过索引换取查询速度;Loki 追求“成本优先”,通过标签过滤缩小扫描范围,用计算(扫描块)换存储。在发布监控场景中,我们通常只需要按服务名、环境、时间范围快速查看日志,Loki 的模型天然匹配。
ELK vs Loki 架构对比 ELK vs Loki 架构对比 ELK 架构 日志源(Logstash/Filebeat) 解析 + 分词 + 建立倒排索引 Elasticsearch(索引 + 存储) 磁盘/SSD(索引 + 数据) 存储成本:高(索引占 50%~100%) 查询速度:毫秒级 Loki 架构 日志源(Promtail) 提取标签 + 压缩日志行 Ingester(内存中构建块) 对象存储(S3/GCS/MinIO) 存储成本:低(压缩后 10%~20%) 查询速度:秒级(需扫描块)

2.5 核心概念在发布监控中的映射

在发布工程自助门户场景中,Loki 的核心概念与业务需求直接对应:

  • 日志流:每个发布单元(如一个微服务实例)的日志构成一个独立的流,通过 {service="order-svc", version="v2.1.0", instance="pod-abc"} 唯一标识。
  • 标签:service(服务名)、version(发布版本)、env(环境)、level(日志级别)作为核心标签,用于快速筛选特定发布的日志。
  • 块:发布期间的日志被压缩存储,查询时只需扫描该时间窗口内的块,无需全量扫描。
发布监控最佳实践:在 Promtail 配置中,将 service、version、env 作为固定标签,将 level、instance 作为可选标签。避免将 trace_id、user_id 等高基数字段设为标签,应保留在日志行内容中,通过 LogQL 的 |~ 进行搜索。

2.6 小结

理解日志流、标签、块三者的关系,是掌握 Loki 的关键:标签决定了查询的筛选能力,日志流决定了数据的组织方式,块决定了存储与查询的效率。与 ELK 相比,Loki 牺牲了部分查询灵活性(不支持全文索引),换来了极低的存储成本和运维复杂度,使其成为发布监控、Kubernetes 日志等场景的理想选择。