2. etcd 基础回顾:Raft 协议、键值存储、Watch 机制、Lease 租约,以及它们在 Loki 中的角色

etcd 是 Loki 元数据存储的核心组件,负责保存日志流(Stream)的元数据、索引信息以及配置数据。理解 etcd 的四大基础机制——Raft 协议、键值存储、Watch 机制、Lease 租约——是后续分析写入放大与查询限流问题的前提。本节将逐一回顾这些机制,并明确它们在 Loki 架构中的具体角色。

2.1 Raft 协议:分布式一致性的基石

Raft 是一种用于管理复制日志的共识算法,它将问题分解为领导者选举、日志复制和安全性三个子问题。etcd 使用 Raft 保证集群中所有节点数据的一致性。

核心角色:
  • Leader(领导者): 处理所有客户端请求,负责日志复制。
  • Follower(跟随者): 被动接收来自 Leader 的日志条目。
  • Candidate(候选者): 在选举超时后发起新一轮选举。

在 Loki 中的角色: Loki 的 Ingester 组件将日志流元数据写入 etcd 时,请求首先到达 Raft Leader。Leader 将写入操作封装为日志条目,复制到多数 Follower 节点后,才返回成功响应。这一过程确保了元数据的高可用性和强一致性,但也是写入放大(Write Amplification)的根源之一——每次写入在集群内部被复制多次。

Raft 日志复制流程 Raft 日志复制流程 Loki Ingester Raft Leader Follower 1 Follower 2 写入请求 日志复制 日志复制 多数确认 返回成功 写入放大来源 1 次客户端写入 → Leader 写入本地日志 + 复制到 N-1 个 Follower 集群节点数越多,写入放大倍数越高(通常 3 节点集群放大 3 倍)

2.2 键值存储:元数据的组织方式

etcd 是一个分布式的键值存储系统,数据以目录结构组织。键(Key)是唯一的,值(Value)可以是任意字节序列。etcd 支持前缀查询、范围查询和事务操作。

概念 说明 Loki 中的使用
Key 唯一标识,支持分层路径(如 /loki/index/<tenant>/<stream>) 日志流 ID、索引条目、配置项
Value 存储的数据内容,大小有限制(默认 1.5 MiB) 日志流标签集合、时间戳范围、chunk 引用
Revision 全局单调递增的版本号,每次修改递增 用于 Watch 机制和 MVCC 控制
Lease 关联到 Key 的租约,到期自动删除 Ingester 心跳、临时元数据清理
性能提示: Loki 在 etcd 中存储的 Key 数量与活跃日志流数量成正比。当日志流数量达到数十万级别时,etcd 的键空间膨胀会导致查询延迟增加,这是写入放大问题的直接表现之一。

2.3 Watch 机制:事件驱动的元数据同步

Watch 机制允许客户端监听特定 Key 或前缀的变化。当被监听的 Key 发生创建、更新、删除操作时,etcd 会向所有 Watch 客户端推送事件通知。

Watch 的工作流程:

  1. 客户端通过 gRPC 建立 Watch 连接,指定监听的 Key 或前缀。
  2. etcd 在内存中维护 Watch 列表,记录每个 Watch 的起始 Revision。
  3. 当有写入操作发生时,etcd 将事件(Event)追加到事件历史缓冲区。
  4. etcd 异步将事件推送给匹配的 Watch 客户端。

在 Loki 中的角色:

  • Ingester 注册与发现: Ingester 启动时在 etcd 中注册自身信息,Querier 通过 Watch 监听 Ingester 列表的变化,动态感知哪些 Ingester 持有哪些日志流。
  • 元数据变更通知: 当日志流元数据更新时,Watch 机制通知相关组件刷新缓存。
  • 限流风险: 大量日志流频繁创建/删除会导致 Watch 事件风暴,触发 etcd 的限流机制(如 --max-request-bytes 和 --quota-backend-bytes),这是查询限流问题的关键诱因。
Watch 机制在 Loki 中的角色 Watch 机制在 Loki 中的角色 etcd 集群 Ingester Querier Compactor Distributor Watch 注册/心跳 Watch 元数据 Watch 配置变更 Watch 流分配 Watch 事件风暴是触发 etcd 限流的主要原因之一,尤其在日志流频繁创建/删除的场景下

2.4 Lease 租约:临时元数据的生命周期管理

Lease(租约)是 etcd 提供的一种 TTL(Time-To-Live)机制。客户端可以创建一个 Lease,设置过期时间,然后将 Key 关联到该 Lease。当 Lease 过期时,所有关联的 Key 会被自动删除。

Lease 的核心特性:

  • 创建与续期: 客户端创建 Lease 后,需要定期发送 KeepAlive 请求续期。
  • 关联 Key: 一个 Lease 可以关联多个 Key,一个 Key 只能关联一个 Lease。
  • 自动清理: Lease 过期后,etcd 自动删除所有关联的 Key,释放存储空间。
  • 租约时间: 默认 TTL 可配置,Loki 中通常设置为 10-30 秒。

在 Loki 中的角色:

场景 Lease 用途 影响
Ingester 心跳 每个 Ingester 创建一个 Lease,定期续期,标识自身存活 Lease 过期表示 Ingester 宕机,触发元数据重新分配
临时流元数据 新创建的日志流元数据关联短 TTL Lease 避免因 Ingester 崩溃导致残留元数据堆积
分布式锁 Compactor 使用 Lease 实现任务锁,防止并发冲突 确保数据压缩任务的唯一性
性能风险: 当集群规模较大(如 100+ Ingester)时,每个 Ingester 每秒发送 KeepAlive 请求,加上日志流元数据的 Lease 创建/续期,会产生大量的 etcd 写入请求。这些请求叠加 Raft 复制,进一步加剧写入放大问题。

2.5 四大机制在 Loki 中的协同关系

etcd 的四大机制并非独立运行,而是在 Loki 中形成紧密的协同关系。下表总结了它们之间的交互和影响:

机制 主要功能 Loki 组件 性能影响
Raft 协议 保证元数据一致性 所有写入操作 写入放大 3x(3 节点集群)
键值存储 元数据持久化 Ingester、Querier、Compactor 键空间膨胀导致查询延迟
Watch 机制 事件驱动的同步 Querier、Distributor 事件风暴触发限流
Lease 租约 临时元数据生命周期 Ingester、Compactor KeepAlive 请求增加写入负载
关键理解: 在后续章节的压测沙盘中,我们将看到:
  • Raft 复制是写入放大的根本原因——每次元数据写入在集群内部被复制多次。
  • Watch 事件风暴是查询限流的直接触发因素——大量 Watch 事件导致 etcd 拒绝新请求。
  • Lease 续期请求叠加在写入放大之上,进一步压榨 etcd 的写入吞吐。
  • 键值存储的膨胀速度决定了 etcd 的存储压力和查询性能退化速度。