kafka日志存储(八):LogCleanManager日志压缩

日志压缩
在很多实践的场景中,key和value的值是不丹变化的,如果消费者只关心key对应的最新value值,可以开启kafka的日志压缩功能,服务端要在后台启动Cleaner线程池,把相同key的消息进行合并。
每个Log都可以通过clean checkpoint部分切分成clean和dirty两部分,clean表示之前已经被压缩过的消息,dirty表示没有被压缩过的消息。
经过压缩之后,clean部分的offset可能是断断续续的,而dirty部门都是连续递增的。
每个Log需要进行日志压缩的迫切程度不同,每个Cleaner只选取最需要被压缩的Log进行处理,迫切程度是通过cleanableRatio决定的。
1. Cleander线程在选定需要清理的Log后,首先为dirty部分建立key和last_offset(这个key出现的最大offset)的对应关系。
2. 重新复制LogSegment,只保留last_offset中记录的消息,抛弃其他消息。
3. 对相邻的LogSegment进行合并,避免出现过小的日志文件和索引文件。
4. value为空的消息会被认为是删除此key对应的消息的标志,此标志消息会被保留一段时间。

LogCleanManager中各个字段如下:
checkpoints:Map[File, OffsetCheckpoint]类型,用来维护data数据目录与cleaner-offset-checkpoint文件之间的对应关系。
inProgress:HashMap[TopicAndPartition, LogCleaningState]: 用于记录正在进行清理的TopicAndPartition的压缩状态。
lock:保护checkpoints集合和inProgress集合锁
pausedCleaningCond:线程阻塞等待压缩状态由LogCleaningAborted转换为LogCleaningPaused

1. 当开始进行压缩的时候,会先进入LogCleanInProgress状态
2. 压缩任务被暂停时进入LogCleaningPaused
3. 压缩任务被中断,则进入LogCleaningPaused状态。
4. 处于LogCleaningPaused状态的topicAndPartition的日志不会被压缩,知道有其他线程恢复它的状态。

  def grabFilthiestLog(): Option[LogToClean] = {
    inLock(lock) { //加锁
      //拿到全部log的cleanercheckpoint
      val lastClean = allCleanerCheckpoints()
      val dirtyLogs = logs.filter {
          //过滤配置为delete的Log
        case (topicAndPartition, log) => log.config.compact  // skip any logs marked for delete rather than dedupe
      }.filterNot {
        //过滤掉包含inProgress状态的log
        case (topicAndPartition, log) => inProgress.contains(topicAndPartition) // skip any logs already in-progress
      }.map {
        
        case (topicAndPartition, log) => // create a LogToClean instance for each
          // if the log segments are abnormally truncated and hence the checkpointed offset
          // is no longer valid, reset to the log starting offset and log the error event
          //获取Log的第一条消息的offset
          val logStartOffset = log.logSegments.head.baseOffset
          //决定最终压缩开始的位置,firstDirtyOffset的值可能是logStartOffset,也可能是clean checkpoint
          val firstDirtyOffset = {
            val offset = lastClean.getOrElse(topicAndPartition, logStartOffset)
            if (offset < logStartOffset) {
              error("Resetting first dirty offset to log start offset %d since the checkpointed offset %d is invalid."
                    .format(logStartOffset, offset))
              logStartOffset
            } else {
              offset
            }
          }
          为Log创建一个LogToClean对象,在LogToClean对象用维护每个Log的clean部分字节数、dirty部分字节数,以及cleanableRatio
          LogToClean(topicAndPartition, log, firstDirtyOffset)
      }.filter(ltc => ltc.totalBytes > 0) // skip any empty logs
      //获取dirtyLogs集合中cleanableRatio的最大值。
      this.dirtiestLogCleanableRatio = if (!dirtyLogs.isEmpty) dirtyLogs.max.cleanableRatio else 0
      // 过滤掉cleanableRatio小于配置的minCleanableRatio值的Log
      val cleanableLogs = di
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值