关于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. Redis 协议为例谈简单的协议分析

    怎样去研究一个协议的过程,协议的格式,好处,怎么样模拟发包等,下面是一个简单的过程记录. 研究的步骤: 协议相关的资料,RFC,官方文档等.弄清楚协议工作在4层还是7层,是二进制还是文本协议等 抓包, ...

  2. php引用传值详解

    php的引用(就是在变量或者函数 .对象等前面加上&符号) 在PHP 中引用的意思是:不同的名字访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的指针里面存储的是变量的内容在内存中存 ...

  3. mac os x下的一些小技巧

    1显示swap空间: sysctl vm.swapusage 其中sysctl中有很多可以控制和查看的项,可以通过sysctl -A列举,另外可以通过man sysctl来查看. 而实际swap文件和 ...

  4. Object对象你真理解了吗?

    前言 五一回家又断更了一个放假时间了~~~ 只有光头才能变强 回顾前面: ThreadLocal就是这么简单 多线程三分钟就可以入个门了! 多线程基础必要知识点!看了学习多线程事半功倍 Java锁机制 ...

  5. IOS使用FMDB封装的数据库增删改查操作

    // //  DBHelper.h //  LessonStoryBoard // //  Created by 袁冬冬 on 15/10/29. //  Copyright (c) 2015年 袁冬 ...

  6. UML图中类之间的关系

    类图中的关系大致一下几种 l 泛化 l 关联 l 依赖 l 组合 l 聚合 泛化 泛化是子集和超集的关系,常使用继承和实现来表示: 继承:子类到超类的实线和空心三角箭头表示 实现:子类到超类的虚线和空 ...

  7. 【图文详解】HDFS基本原理

    本文主要详述了HDFS的组成结构,客户端上传下载的过程,以及HDFS的高可用和联邦HDFS等内容.若有不当之处还请留言指出. 当数据集大小超过一台独立的物理计算机的存储能力时,就有必要对它进行分区,并 ...

  8. unity零基础开始学习做游戏(一)为了实现你的游戏,你需要提前做的准备工作

    -------小基原创,转载请给我一个面子 正所谓,工欲善其事,必现准备好电脑.接下来跟着小基一步一步来搭建你的开发环境吧 1.下载安装unity 上面的那个是破解软件,下面是unity5.5.6的安 ...

  9. Python-Web框架之 - 利用SQLALchemy创建与数据库MySQL的连接, 详解用Flask时会遇到的一些大坑 !

    经过这个小项目算是对Django与Flask这两个web框架有了新的认识 , Django本身的轮子非常齐全 , 套路也很固定 , 新手在接触Django框架时 , 不会陷入到处找轮子的大坑 ; 那么 ...

  10. go Mutex (互斥锁)和RWMutex(读写锁)

    转载自: https://blog.csdn.net/skh2015java/article/details/60334437 golang中sync包实现了两种锁Mutex (互斥锁)和RWMute ...