apache Kafka是如何实现删除数据文件(日志)的

afka删除数据有两种方式,一种是按照时间,超过一段时间后删除过期消息,第二种是按照消息大小删除数据的,消息数量超过一定大小后删除最旧的数据

但是Kafka的数据是存储在文件系统内的,随机删除数据是不可能的,那么,Kafka是如何删除数据的呢?

Kafka删除数据主逻辑

对应配置:  log.cleanup.interval.mins

当前使用值:1

file: core/src/main/scala/kafka/log/LogManager.scala

line: 271

/**

 * Delete any eligible logs. Return the number of segments deleted.

 */

def cleanupLogs() {

  debug("Beginning log cleanup...")

  var total = 0

  val startMs = time.milliseconds

  for(log <- allLogs) {

    debug("Garbage collecting '" + log.name + "'")

    total += cleanupExpiredSegments(log) + cleanupSegmentsToMaintainSize(log)

  }

  debug("Log cleanup completed. " + total + " files deleted in " +

                (time.milliseconds - startMs) / 1000 + " seconds")

}

 

Kafka 每隔 log.cleanup.interval.mins 分钟调用一次 cleanupLogs ,该函数对所有 Logs 执行清理操作,(目前不确定 Logs 对应的是 Topic 还是 Partition,目测应当是 Partition)

  • cleanupExpiredSegments 负责清理超时的数据
  • cleanupSegmentsToMaintainSize 负责清理超过大小的数据

清理超时数据 (必选策略)

对应配置:log.retention.hours

当前使用值: 72 (3天)

file: core/src/main/scala/kafka/log/LogManager.scala

line: 237

/**

 * Runs through the log removing segments older than a certain age

 */

private def cleanupExpiredSegments(log: Log): Int = {

  val startMs = time.milliseconds

  val topic = parseTopicPartitionName(log.name).topic

  val logCleanupThresholdMs = logRetentionMsMap.get(topic).getOrElse(this.logCleanupDefaultAgeMs)

  val toBeDeleted = log.markDeletedWhile(startMs - _.messageSet.file.lastModified > logCleanupThresholdMs)

  val total = log.deleteSegments(toBeDeleted)

  total

}

  • 该函数首先获取 topic,根据 topic 获取日志超时时间,该超时时间可以使用配置 log.retention.hours.per.topic 单独指定,如果没有单独指定,则使用统一的 log.retention.hours 配置。
  • 然后扫描所有该日志对应的 Segment 文件,对所有最近修改时间与当前时间差距大于超时时间的日志的 Segment 文件,标记为删除
  • 最后删除标记为删除的 Segment 文件

清理超大小数据 (可选策略)

对应配置:log.retention.bytes

当前使用值: -1 (默认值,即不采用该策略)

file: core/src/main/scala/kafka/log/LogManager.scala

line: 250

/**

 *  Runs through the log removing segments until the size of the log

 *  is at least logRetentionSize bytes in size

 */

private def cleanupSegmentsToMaintainSize(log: Log): Int = {

  val topic = parseTopicPartitionName(log.dir.getName).topic

  val maxLogRetentionSize = logRetentionSizeMap.get(topic).getOrElse(config.logRetentionBytes)

  if(maxLogRetentionSize < 0 || log.size < maxLogRetentionSize) return 0

  var diff = log.size - maxLogRetentionSize

  def shouldDelete(segment: LogSegment) = {

    if(diff - segment.size >= 0) {

      diff -= segment.size

      true

    } else {

      false

    }

  }

  val toBeDeleted = log.markDeletedWhile( shouldDelete )

  val total = log.deleteSegments(toBeDeleted)

  total

}

  • 当不需要删除或者日志总大小小于配置时,不执行
  • 按照从旧到新的顺序扫描各个 Segments,如果该Segment大小 <= 当前日志超过设置值的diff,该Segment会被标记为需要删除
  • 执行删除操作

按照 Segment 删除的影响

每个 Segment 文件实际会按照最后一条日志的时间进行删除。当日志中的最后一条日志没有超时时,该文件不会被删除。

对超过大小规则的影响

删除该Segment之后,数据仍然超过大小,才会删除该Segment。如果删除该Segment后,数据大小小于设定上限,则不删除该Segment。

Segment相关配置

log.segment.bytes

  • 当前使用值:1073741824
  • 注释:单个Segment 的大小设置,达到这个大小时,Kafka会新建一个 Segment 文件

log.roll.hours

  • 当前使用值:24
  • 隔一段时间,Kafka 会新建一个Segment文件,即便之前的Segment文件没有达到 log.segment.bytes

猜你喜欢

转载自blog.csdn.net/wr_java/article/details/107938146