在 HDD 上榨出 100GB/s+ 吞吐:HorizonVault 分布式存储引擎的设计拆解

2026-06-01 35 预计阅读时间:1 分钟
来源:my.oschina.net AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:12 分钟

Kafka 集群的数据膨胀是个老问题——本地 SSD 再快也扛不住无限增长,而一旦把数据搬到远端 HDD,吞吐和延迟往往断崖式下跌。HorizonVault 是中间件团队为这个矛盾自研的分布式存储引擎:目标很硬——在 HDD 集群上稳定跑出 100GB/s+ 级大吞吐,同时把读写延迟压到业务可接受的范围,支撑 Kafka 远程存储、冷温数据下沉和低成本大容量场景。

这篇文章拆解它怎么做到的,以及你在类似场景下可以怎么借鉴。

HDD 吞吐的物理瓶颈在哪

单块 HDD 的顺序写极限大约 150–200 MB/s,随机 I/O 则跌到 1–5 MB/s。要凑出 100GB/s+ 的集群吞吐,至少需要 500+ 块盘并行,而且必须把随机写压到最低。

HorizonVault 的核心思路是三点:

  1. 写路径全顺序化——所有写入先落 WAL(Write-Ahead Log),再以批量追加方式刷到数据段,杜绝随机写。
  2. 读路径靠索引 + 缓存分离——热元数据和索引常驻内存,冷数据按段定位后做一次顺序读,避免随机寻道。
  3. 段合并(Segment Compaction)异步化——合并操作不阻塞写入流,在后台把小段拼成大段,减少碎片对读延迟的干扰。

这三点组合的效果:写入端几乎全是顺序 I/O,HDD 的强项被充分利用;读取端通过索引缩小寻道范围,把"随机读"降级为"小范围顺序读"。

数据布局:段式存储 + 时间分区

HorizonVault 把数据按 Segment 组织,每个 Segment 是一个不可变追加块,大小通常在 64–256 MB 之间。Segment 内部再按时间窗口分区,和 Kafka 的日志段逻辑天然对齐:

/volume/partition-42/
  ├── seg-000001.data   # 数据块,追加写入,不可变
  ├── seg-000001.index  # 偏移量 → 文件位置索引
  ├── seg-000001.meta   # 时间范围、压缩算法、CRC 校验
  └── seg-000002.data
  └── ...

这种布局的好处:

  • 写入零碎片:新数据永远追加到当前活跃段,写满后封段(seal),再开新段。
  • 删除零开销:冷数据过期只需标记段级删除,整段回收,不需要逐条清理。
  • 迁移友好:整段可以作为一个单元在节点间搬移,不破坏内部顺序。

段封段后进入 compaction 队列,后台线程把多个小段合并成大段。合并期间原始段仍然可读,合并完成后原子切换引用,读请求无感知。

网络层:批量聚合 + 流式管道

单条消息走网络,RTT 和协议开销是吞吐杀手。HorizonVault 在客户端和存储节点之间加了一层 Batch Aggregator

  • 客户端本地攒批:默认攒到 4 MB 或 50 ms 再发送,减少 RPC 次数。
  • 存储节点接收端同样攒批:收到的多个 batch 合并成一个大的 WAL 写入,一次磁盘 I/O 搞定。
  • 流式管道:大段数据用零拷贝(sendfile / splice)直接从磁盘管道到网络,跳过用户态内存拷贝。

这组优化把网络 RTT 从"每条消息一次"降到"每批一次",在跨机房部署时效果尤其明显。

容错与恢复:WAL + 多副本 + 快速重建

HDD 故障率比 SSD 高,100GB/s+ 集群意味着盘多、故障频发。HorizonVault 的容错设计:

  • WAL 优先持久化:写入先落本地 WAL,确认后才返回客户端成功。WAL 本身做多副本同步写(通常 2–3 副本)。
  • 数据段副本异步补齐:封段后的数据段通过后台复制补齐副本,不阻塞写入流。
  • 故障恢复走段级重建:节点宕机后,调度器按段优先级(热数据优先)从存活副本拉取重建,单段 256 MB 在正常网络下 1–2 秒可完成。

关键权衡:WAL 多副本同步写牺牲了少量写入延迟(通常增加 1–3 ms),但保证了数据零丢失;数据段异步复制则把吞吐最大化。

实践:用 HorizonVault 承接 Kafka 远程日志存储

下面是一个可改造的 Kafka + HorizonVault 远程存储配置示例。假设你已经部署了 HorizonVault 集群(节点 hv-node-1hv-node-6),现在要把 Kafka 的冷数据下沉到远端。

步骤一:Kafka 开启远程日志存储

在 Kafka 的 server.properties 中添加远程存储配置:

# 开启远程日志存储
remote.log.storage.system.enable=true

# 远程存储策略:数据在本地保留 remote.log.retention.ms 后迁移到远端
remote.log.retention.ms=3600000

# HorizonVault 作为 RemoteStorageManager 实现
remote.storage.manager.class.name=com.horizonvault.kafka.HorizonVaultRemoteStorageManager

# HorizonVault 连接配置
horizonvault.endpoints=hv-node-1:9092,hv-node-2:9092,hv-node-3:9092
horizonvault.batch.size.bytes=4194304
horizonvault.batch.linger.ms=50
horizonvault.replication.factor=2
horizonvault.compression.type=zstd

步骤二:HorizonVault 集群部署配置(YAML)

以下是一个简化的 HorizonVault 节点配置,可直接改造用于测试或生产:

# horizonvault-node-config.yaml
cluster:
  name: hv-kafka-remote
  nodes: 6

storage:
  volumes:
    - path: /data/hv-disk0
      capacity: 4TB
      type: hdd
    - path: /data/hv-disk1
      capacity: 4TB
      type: hdd
  segment:
    maxSize: 256MB        # 段封段阈值
    compaction:
      enable: true
      interval: 300s      # 合合检查间隔
      minSegments: 4      # 最少合并段数
      targetSize: 1GB     # 合并后目标段大小

wal:
  syncReplicas: 2         # WAL 同步副本数
  flushInterval: 10ms     # WAL 刷盘间隔
  maxBatchSize: 8MB       # 单次 WAL 最大批量

network:
  port: 9092
  batchAggregator:
    maxSize: 4MB
    lingerMs: 50
  zeroCopy: true          # 启用零拷贝流式传输

recovery:
  segmentRebuildPriority: hot-first  # 热数据段优先重建
  rebuildConcurrency: 8              # 并行重建线程数

步骤三:启动节点并验证吞吐

# 启动 HorizonVault 节点(在每个节点上执行)
horizonvault-server start --config horizonvault-node-config.yaml

# 等待集群就绪后,用内置 benchmark 工具验证写入吞吐
horizonvault-benchmark write \
  --endpoints hv-node-1:9092,hv-node-2:9092,hv-node-3:9092 \
  --duration 300s \
  --payload-size 1KB \
  --concurrency 64 \
  --batch-size 4MB

# 预期输出示例(6 节点 × 12 块 HDD):
# Total throughput: ~108 GB/s
# Avg write latency: 12 ms (p99: 35 ms)

改造提示

  • segment.maxSize 根据你的 HDD 型号调整——大段减少碎片但增加合并成本,小段反之。
  • wal.syncReplicas 设为 2 是吞吐和安全的平衡点;如果数据绝对不能丢,设为 3,写入延迟会增加约 2 ms。
  • network.batchAggregator.maxSize 和 Kafka 端的 horizonvault.batch.size.bytes 保持一致,避免两端攒批参数不匹配导致额外拆包。

冷温数据下沉的治理策略

HorizonVault 不只是"把数据扔到远端",它还提供了一套数据治理机制:

  • 生命周期策略(Lifecycle Policy):按时间窗口自动降级存储介质——热数据 SSD → 温数据 HDD → 冷数据低频 HDD,每级可以配置不同的副本数和压缩算法。
  • 压缩分层:热段不压缩(读延迟优先),温段用 LZ4(速度和压缩率平衡),冷段用 ZSTD(压缩率优先,最高可达 5:1)。
  • 配额与限流:每个 Kafka topic 可以设置远端存储配额上限,防止某个 topic 吃掉整个集群容量;写入限流则保护 HDD 在高峰时不被压到随机 I/O 区。

这套治理让"低成本"不只是采购便宜硬盘,而是从数据流转的全链路压成本。

采纳建议与风险清单

适合的场景

  • Kafka 集群本地磁盘容量紧张,需要把 1 小时以上的历史数据迁到远端。
  • 日志/时序数据的冷温存储,写入量大、读取以时间范围查询为主。
  • 需要可治理的数据生命周期(自动过期、降级、压缩)。

不适合的场景

  • 强依赖低延迟随机读的业务(HDD 的随机读延迟在 10–20 ms 级,SSD 是 0.1 ms 级)。
  • 数据量小到单机 SSD 就能撑住——引入分布式系统的运维成本不划算。

风险点

风险 说明 对策
HDD 故障率高 1000 块盘的集群,年均故障约 3–6% WAL 同步副本 + 段级快速重建,热数据优先恢复
合合延迟抖动 后台 compaction 与前台写入争磁盘带宽 compaction 限流,只使用写入空闲的 I/O 配额
跨机房网络抖动 WAL 同步副本跨机房时 RTT 不稳定 WAL 副本优先同机房,数据段副本跨机房异步补齐
客户端攒批延迟 50 ms 的 linger 增加了写入感知延迟 业务可接受则保持;不可接受则减小 linger、增大并发

落地检查清单

  1. 确认 HDD 型号和数量,估算理论吞吐上限(盘数 × 顺序写极限)。
  2. 确认网络带宽是否匹配集群吞吐目标(100GB/s 写入需要至少 100GB/s 网络容量)。
  3. Kafka 端和 HorizonVault 端的攒批参数对齐。
  4. WAL 副本数根据数据安全等级选定(2 或 3)。
  5. 配置生命周期策略,明确热/温/冷的时间边界和压缩算法。
  6. 设置 topic 级配额上限,防止容量被单一 topic 打满。

HorizonVault 的核心贡献不是某个单点算法,而是把 HDD 的物理特性(顺序写强、随机写弱)从头到尾尊重了一遍——写路径全顺序、读路径靠索引缩小随机范围、网络攒批降 RTT、容错靠 WAL 同步 + 段级重建。这套思路在任何"低成本大盘集群"场景下都值得复用。


相关推荐