当前位置: 首页 > news >正文

分布式专题——23 Kafka日志索引详解 - 指南

分布式专题——23 Kafka日志索引详解 - 指南

2025-09-28 10:38  tlnshuju  阅读(0)  评论(0)    收藏  举报

1 Kafka 的日志文件记录机制

  • Kafka 的日志文件记录机制是其能支撑高吞吐、高性能、高可扩展的核心所在,对业界影响巨大;
  • 每个 Broker 节点的消息数据(称为 Log 日志)是无状态的,这种无状态设计让 Kafka 集群易于水平扩展,比如可通过工具(如 kafka-reassign-partitions.sh)将无状态数据从旧 Broker 转移到新 Broker 以替换服务(数据转移并非简单复制粘贴,因底层是二进制文件,操作复杂)。

1.1 Topic存储消息的方式

1.1 log 文件记录消息的方式

  • 追加写入:在每个文件内部,Kafka 以追加的方式将消息写入 log 日志文件。Kafka 中的消息日志只允许追加操作,不支持删除和修改。因此,只有文件名最大的一个 log 文件是当前用于写入消息的日志文件,其他文件都是不可修改的历史日志;
  • 固定大小与文件命名:每个 log 文件保持固定的大小。当当前文件无法再记录新消息时,会重新创建一个 log 文件,并且以这个新 log 文件写入的第一条消息的偏移量来命名。这种设计是为了更方便地进行文件映射,从而加快读取消息的效率。

1.2 index和timeindex文件加速读取log消息日志

在这里插入图片描述

2 文件清理机制

  • Kafka 为避免过多日志文件给服务器带来压力,会定期删除过期的 log 文件,涉及以下配置属性:

    • log.retention.check.interval.ms:定时检测文件是否过期的时间间隔,默认是 300000 毫秒(即 5 分钟);

    • log.retention.hourslog.retention.minuteslog.retention.ms:这一组参数用于设置文件保留的时间。默认生效的是 log.retention.hours,默认值为 168 小时(即 7 天)。如果设置了更高时间精度的参数,以时间精度最高的配置为准;

    • 检查文件是否超时时,以每个 .timeindex 文件中最大的那条记录为准;

  • 过期日志文件的处理

    • log.cleanup.policy:日志清理策略,有两个选项。delete 表示删除日志文件;compact 表示压缩日志文件;

    • log.cleanup.policy 选择 delete 时,还有一个参数 log.retention.bytes,用于表示所有日志文件的大小。当总的日志文件大小超过这个阈值后,就会删除最早的日志文件,默认值是 -1,表示无大小限制;

    • 压缩日志文件不会直接删除日志文件,但会造成消息丢失。压缩过程中会将 Key 相同的日志进行压缩,只保留最后一条。

3 客户端消费进度管理

4 Kafka 的文件高效读写机制

4.1 Kafka 的文件结构

4.2 顺序写磁盘

  • 这一特性和操作系统有关,主要由硬盘结构决定;
  • 对于每个 log 文件,Kafka 会提前规划固定的大小,这样在申请文件时,能够提前占据一块连续的磁盘空间;
  • Kafka 的 log 文件只能以追加的方式往文件的末端添加(这种写入方式称为顺序写)。新的数据写入时,可直接往之前申请的磁盘空间中写入,无需再去磁盘其他地方寻找空闲空间(普通的读写文件需要先寻找空闲的磁盘空间,再写入,这种写入方式称为随机写)。因为磁盘的空闲空间可能不连续,存在很多文件碎片,所以随机写的效率会很低;
  • Kafka 官网测试数据表明,同样的磁盘,顺序写速度能达到 600M/s,基本与写内存的速度相当;而随机写的速度只有 100K/s,两者差距很大。

4.3 零拷贝

4.4 合理配置刷盘频率

  • 缓存数据断电会丢失,若缓存中的数据未及时写入硬盘(刷盘),服务突然崩溃时就可能丢失消息。通常认为最安全的方式是写一条数据就刷一次盘(同步刷盘),刷盘操作在 Linux 系统中对应 fsync 系统调用;

    fsync, fdatasync - synchronize a file's in-core state with storage device

    上面这是 Linux 系统中关于 fsyncfdatasync 函数的手册页(Manual Page)相关内容

    • 这里的in-core state指的是操作系统内核态的缓存,即 PageCache,这是应用程序接触不到的缓存;
    • 应用程序打开文件时,内容从 PageCache 中读取;修改文件内容时,也是先写到 PageCache 里,之后操作系统会通过自身缓存管理机制,在未来某个时刻将 PageCache 里的内容统一写入磁盘;
    • 对于缓存断掉导致数据丢失的问题,应用程序无法决定数据何时写入硬盘,只能尽量频繁通知操作系统进行刷盘操作,但这会降低应用执行性能,且不能百分百保证数据安全,应用程序在这个问题上只能取舍;
  • Kafka 在 Broker 端设计了一系列参数来控制刷盘频率:

    • flush.ms:指定强制刷盘的时间间隔。例如设置为 1000,就会在 1000 毫秒后执行 fsync。一般建议不设置该参数,利用复制(replication)保证数据持久性,让操作系统的后台刷盘能力发挥作用,因为这样更高效;

    • log.flush.interval.messages:表示同一个 Partition 的消息数积累到该数量时,就会申请一次刷盘操作,默认是 Long.MAX

    • log.flush.interval.ms:当一个消息在内存中保留的时间达到该数量时,就会申请一次刷盘操作,默认值为空,若为空则生效下一个参数;

    • log.flush.scheduler.interval.ms:检查是否有日志文件需要进行刷盘的频率,默认是 Long.MAX

  • 为了最大化性能,Kafka 默认将刷盘操作交由操作系统统一管理;

  • Kafka 没有实现写一个消息就进行一次刷盘的“同步刷盘”机制,无法保证非正常断电情况下的消息安全,这是所有应用程序都面临的问题;

    • RabbitMQ 官网明确提出服务端并不完全保证消息不丢失,若要提升消息安全性,需通过 Publisher Confirms 机制让客户端参与验证;
    • RocketMQ 提供了“同步刷盘”的配置选项,但每来一个消息就调用一次刷盘操作,服务器难以承受,后续可关注 RocketMQ 如何实现同步刷盘。