关于Kafka日志留存(log retention)策略的介绍,网上已有很多文章。不过目前其策略已然发生了一些变化,故本文针对较新版本的Kafka做一次统一的讨论。如果没有显式说明,本文一律以Kafka 1.0.0作为分析对象。

所谓日志留存策略,就是Kafka保存topic数据的规则,我将按照以下几个方面分别介绍留存策略:

  • 留存策略类型
  • 留存机制及其工作原理

一、留存策略类型

目前,与日志留存方式相关的策略类型主要有两种:delete和compact。这两种留存方式的机制完全不同。本文主要讨论针对delete类型的留存策略。用户可以通过设置broker端参数log.cleanup.policy来指定集群上所有topic默认的策略类型。另外也可以通过topic级别参数cleanup.policy来为某些topic设置不同于默认值的策略类型。当前log.cleanup.policy参数的默认值是[delete,compact],这是一个list类型的参数,表示集群上所有topic会同时开启delete和compact两种留存策略——这是0.10.1.0新引入的功能,在0.10.1.0之前,该参数只能两选一,不能同时兼顾,但在实际使用中很多用户都抱怨compact类型的topic存在过期key消息未删除的情况,故社区修改了该参数配置,允许一个topic同时开启两种留存策略。

再次强调下, 本文只讨论delete类型的留存策略。

二、留存机制及其工作原理

在开始详细介绍各种留存机制之前,先简要说下Kafka是如何处理日志留存的。每个Kafka broker启动时,都会在后台开启一个定时任务,定期地去检查并执行所有topic日志留存,这个定时任务触发的时间周期由broker端参数log.retention.check.interval.ms控制,默认是5分钟,即每台broker每5分钟都会尝试去检查一下是否有可以删除的日志。因此如果你要缩短这个间隔,只需要调小log.retention.check.interval.ms即可。

鉴于日志留存和日志删除实际上是一个问题的两个方面,因而我们下面讨论的是关于Kafka根据什么规则来删除日志。但有一点要强调一下,待删除的标的是日志段,即LogSegment,也就是以.log结尾的一个个文件,而非整个文件夹。另外还有一点也很重要,当前日志段(active logsegment)是永远不会被删除的,不管用户配置了哪种留存机制。

当前留存机制共有3种:

  1. 基于空间维度
  2. 基于时间维度
  3. 基于起始位移维度

前两种策略相信大家已经耳熟能详,而第三种策略由于新加入的时间不长,目前网上对其的介绍并不多。我们一个一个来看。

2.1 基于空间维度

也称size-based retention,指的是Kafka定期为那些超过磁盘空间阈值的topic进行日志段的删除。这个阈值由broker端参数log.retention.bytes和topic级别参数retention.bytes控制,默认是-1,表示Kafka当前未开启这个留存机制,即不管topic日志量涨到多少,Kafka都不视其为“超过阈值”。如果用户要开启这种留存机制,必须显式设置log.retention.bytes(或retention.bytes)。

一旦用户设置了阈值,那么Kafka就会在定时任务中尝试比较当前日志量总大小是否超过阈值至少一个日志段的大小。这里所说的总大小是指所有日志段文件的大小,不包括索引文件的大小!如果是则会尝试从最老的日志段文件开始删起。注意这里的“超过阈值至少一个日志段的大小”,这就是说超过阈值的部分必须要大于一个日志段的大小,否则不会进行删除的,原因就是因为删除的标的是日志段文件——即文件只能被当做一个整体进行删除,无法删除部分内容。

举个例子来说明,假设日志段大小是700MB,当前分区共有4个日志段文件,大小分别是700MB,700MB,700MB和1234B——显然1234B那个文件就是active日志段。此时该分区总的日志大小是3*700MB+1234B=2100MB+1234B,如果阈值设置为2000MB,那么超出阈值的部分就是100MB+1234B,小于日志段大小700MB,故Kafka不会执行任何删除操作,即使总大小已经超过了阈值;反之如果阈值设置为1400MB,那么超过阈值的部分就是700MB+1234B > 700MB,此时Kafka会删除最老的那个日志段文件。

2.2 基于时间维度

也称time-based retention,指的是Kafka定期未那些超过时间阈值的topic进行日志段删除操作。这个阈值由broker端参数log.retention.ms、log.retention.mintues、log.retention.hours以及topic级别参数retention.ms控制。如果同时设置了log.retention.ms、log.retention.mintues、log.retention.hours,以log.retention.ms优先级为最高,log.retention.mintues次之,log.retention.hours最次。当前这三个参数的默认值依次是null, null和168,故Kafka为每个topic默认保存7天的日志。

这里需要讨论下这“7天”是如何界定的?在0.10.0.0之前,Kafka每次检查时都会将当前时间与每个日志段文件的最新修改时间做比较,如果两者的差值超过了上面设定的阈值(比如上面说的7天),那么Kafka就会尝试删除该文件。不过这种界定方法是有问题的,因为文件的最新修改时间是可变动的——比如用户在终端通过touch命令查看该日志段文件或Kafka对该文件切分时都可能导致最新修改时间的变化从而扰乱了该规则的判定,因此自0.10.0.0版本起,Kafka在消息体中引入了时间戳字段(当然不是单纯为了修复这个问题),并且为每个日志段文件都维护一个最大时间戳字段。通过将当前时间与该最大时间戳字段进行比较来判定是否过期。使用当前最大时间戳字段的好处在于它对用户是透明的,用户在外部无法直接修改它,故不会造成判定上的混乱。

最大时间戳字段的更新机制也很简单,每次日志段写入新的消息时,都会尝试更新该字段。因为消息时间戳通常是递增的,故每次写入操作时都会保证最大时间戳字段是会被更新的,而一旦一个日志段写满了被切分之后它就不再接收任何新的消息,其最大时间戳字段的值也将保持不变。倘若该值距离当前时间超过了设定的阈值,那么该日志段文件就会被删除。

2.3 基于起始位移维度

用户对前两种留存机制实际上是相当熟悉的,下面我们讨论下第三种留存机制:基于日志起始位移(log start offset)。这实际上是0.11.0.0版本新增加的功能。其实增加这个功能的初衷主要是为了Kafka流处理应用——在流处理应用中存在着大量的中间消息,这些消息可能已经被处理过了,但依然保存在topic日志中,占用了大量的磁盘空间。如果通过设置基于时间维度的机制来删除这些消息就需要用户设置很小的时间阈值,这可能导致这些消息尚未被下游操作算子(operator)处理就被删除;如果设置得过大,则极大地增加了空间占用。故社区在0.11.0.0引入了第三种留存机制:基于起始位移

所谓起始位移,就是指分区日志的当前起始位移——注意它是分区级别的值,而非日志段级别。故每个分区都只维护一个起始位移值。该值在初始化时被设置为最老日志段文件的基础位移(base offset),随着日志段的不断删除,该值会被更新当前最老日志段的基础位移。另外Kafka提供提供了一个脚本命令帮助用户设置指定分区的起始位移:kafka-delete-records.sh。

该留存机制是默认开启的,不需要用户任何配置。Kafka会为每个日志段做这样的检查:1. 获取日志段A的下一个日志段B的基础位移;2. 如果该值小于分区当前起始位移则删除此日志段A。

依然拿例子还说明,假设我有一个topic,名字是test,该topic只有1个分区,该分区下有5个日志段文件,分别是A1.log, A2.log, A3.log, A4.log和A5.log,其中A5.log是active日志段。这5个日志段文件中消息范围分别是0~9999,10000~19999,20000~29999,30000~39999和40000~43210(A5未写满)。如果此时我确信前3个日志段文件中的消息已经被处理过了,于是想删除这3个日志段,此时我应该怎么做呢?由于我无法预知这些日志段文件产生的速度以及被消费的速度,因此不管是基于时间的删除机制还是基于空间的删除机制都是不适用的。此时我便可以使用kafka-delete-records.sh脚本将该分区的起始位移设置为A4.log的起始位移,即40000。为了做到这点,我需要首先创建一个JSON文件a.json,内容如下:

{"partitions":[{"topic": "test", "partition": 0,"offset": 40000}],"version":1}

然后执行下列命令:

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #000000; background-color: #ffffff }
span.s1 { }

bin/kafka-delete-records.sh --bootstrap-server localhost:9092 --offset-json-file a.json

如果一切正常,应该可以看到类似于这样的输出:

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #000000; background-color: #ffffff }
span.s1 { }
span.Apple-tab-span { white-space: pre }

Executing records delete operation

Records delete operation completed:

partition: test-0 low_watermark: 40000

此时test的分区0的起始位移被手动调整为40000,那么理论上所有最大消息位移< 40000的日志段都可以被删除了。有了这个机制,用户可以实现更为灵活的留存策略。

以上就是关于当前Kafka针对于delete留存类型的topic的3种留存机制。也许在未来社区会增加更多的留存策略,我们拭目以待~

关于Kafka日志留存策略的讨论的更多相关文章

  1. Kafka日志清除策略

    一.更改日志输出级别 config/log4j.properties中日志的级别设置的是TRACE,在长时间运行过程中产生的日志大小吓人,所以如果没有特殊需求,强烈建议将其更改成INFO级别.具体修改 ...

  2. kafka 日志策略

    日志查看: usr/local/kafka/kafka_2.11-2.4.0/bin/kafka-run-class.sh kafka.tools.DumpLogSegments --files /t ...

  3. Kafka设计解析(二十一)关于Kafka幂等producer的讨论

    转载自 huxihx,原文链接 关于Kafka幂等producer的讨论 众所周知,Kafka 0.11.0.0版本正式支持精确一次处理语义(exactly once semantics,下称EOS) ...

  4. lagstash + elasticsearch + kibana 3 + kafka 日志管理系统部署 02

    因公司数据安全和分析的需要,故调研了一下 GlusterFS + lagstash + elasticsearch + kibana 3 + redis 整合在一起的日志管理应用: 安装,配置过程,使 ...

  5. kafka 日志结构

    1.kafka日志结构 直接举例子: 例如kafka有个名字叫 haha 的topic,那么kafka日志下面有kafka-0,kafka-1,kafka-2...,kafka-n,具体多少个,创建分 ...

  6. kafka日志同步至elasticsearch和kibana展示

    kafka日志同步至elasticsearch和kibana展示 一 kafka consumer准备 前面的章节进行了分布式job的自动计算的概念讲解以及实践.上次分布式日志说过日志写进kafka, ...

  7. 离线部署ELK+kafka日志管理系统【转】

    转自 离线部署ELK+kafka日志管理系统 - xiaoxiaozhou - 51CTO技术博客http://xiaoxiaozhou.blog.51cto.com/4681537/1854684 ...

  8. Kafka日志段读写分析

    引子 之所以写这篇文章是因为之前面试时候被面试官问到(倒)了,面试官说:"你说你对Kafka比较熟?看过源码? 那说说kafka日志段如何读写的吧?" 我心里默默的说了句 &quo ...

  9. ambari 修改kafka日志目录后,写入数据无法消费

    ## 起因:ambari 修改kafka日志目录后,写入数据无法消费 - 使用下面的客户端消费命令可以消费到数据 ./kafka-console-consumer.sh --zookeeper 192 ...

随机推荐

  1. leetCode之旅(14)-Number of 1 Bits

    题目描述: Write a function that takes an unsigned integer and returns the number of '1' bits it has (als ...

  2. 我的摸索过程之IIS下配置asp.net 的注意事项

    "在应用程序级别之外使用注册为 allowDefinition='MachineToApplication' 的节是错误的.如果在 IIS 中没有将虚拟目录配置为应用程序,则可能导致此错误. ...

  3. Android性能优化之UI渲染性能优化

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 本篇博客主要记录一些工作中常用的UI渲染性能优化及调试方法,理解这些方法对于我们编写高质量代码也是有一些帮助的,主要内容包括介绍CPU,GPU的职 ...

  4. java 中 printf()语句的理解

    对print和println的理解很简单,今天突然接触到printf(),有点懵,整理了下也帮自己理一理 printf是格式化输出的形式 下在举个例子: package other; public c ...

  5. ORACLE之TO_DATE (转载)

    转自 http://www.cnblogs.com/anran_guojianjun/archive/2009/09/11/1564535.html 一.在使用Oracle的to_date函数来做日期 ...

  6. java设计模式--简单工厂

    java设计模式--简单工厂 简单工厂不是一个标准的设计模式,但是很常用需要掌握. 在java应用开发中,要"面向接口编程". 1.java中接口的概念: 在java中接口是一种特 ...

  7. vue项目通过webpack打包生成的dist文件放到express环境里运行(vue+webpack+express)

    1.首先需要的原料肯定是vue打包生成的dist文件 在vue项目目录下运行:npm run build,等待运行结束,会在项目目录下生成一个dist文件夹,里面会生成一些文件(如下图示) 小的项目文 ...

  8. How 5 Natural Language Processing APIs Stack Up

    https://www.programmableweb.com/news/how-5-natural-language-processing-apis-stack/analysis/2014/07/2 ...

  9. Tornado、Bottle以及Flask

    最近接手一个Tornado项目代码,项目要在原有基础上做很大扩展,为了更好地吃透并扩展好这个项目,就对Tornado以及比较轻型的Bottle.Flask这些框架一一作了调研.其实若干年前做第一个Py ...

  10. 关于Python的那些话

    1.第一个选择:版本2还是3,我选择2,保守谨慎,3的成熟周期会很长2.三种基本的文本操作:     2.1.解析数据并将数据反序列化到程序的数据结构中     2.2.将数据以某种方式转化为另一种相 ...