博客
关于我
kafka日志存储(五):LogSegment
阅读量:250 次
发布时间:2019-03-01

本文共 4420 字,大约阅读时间需要 14 分钟。

为了防止Log文件过大,把log切分成很多个日志文件,每个日志文件对应一个LogSegment,在LogSegment中封装一个FileMessageSet和OffsetIndex对象。

 

class LogSegment(val log: FileMessageSet,//操作对应日志文件FileMessageSet对象                 val index: OffsetIndex,//操作索引文件的OffsetIndex对象                 val baseOffset: Long,//第一条消息的offset值                 val indexIntervalBytes: Int,//索引项之间间隔的最下字节数                 val rollJitterMs: Long,                 time: Time) extends Logging {      //自从上次添加索引项之和,在日志文件中累计加入的Message集合的字节数,判断下次索引项添加的时机。      private var bytesSinceLastIndexEntry = 0      //LogSegment的创建时间,当调用truncateTo方法吧整个日志文件清空的时候,此字段会重置为当前时间      var created = time.milliseconds}

LogSegment.append()实现了追加消息的功能。第一个参数是第一条消息的offset,如果是压缩的消息,则是内层消息的第一个offset。

 

def append(offset: Long, messages: ByteBufferMessageSet) {    if (messages.sizeInBytes > 0) {      trace("Inserting %d bytes at offset %d at position %d".format(messages.sizeInBytes, offset, log.sizeInBytes()))      //是否满足添加索引项目的条件      if(bytesSinceLastIndexEntry > indexIntervalBytes) {        //添加索引        index.append(offset, log.sizeInBytes())        this.bytesSinceLastIndexEntry = 0      }      // 写日志文件,更新bytesSinceLastIndexEntry      log.append(messages)      this.bytesSinceLastIndexEntry += messages.sizeInBytes    }}

read方法读取消息,有四个参数

// startOffset指定读取的起始消息的offset// maxOffset 读取结束的offset,可以为空// maxSize 指定读取的最大字节数// maxPosition 指定读取的最大物理地址  def read(startOffset: Long, maxOffset: Option[Long], maxSize: Int, maxPosition: Long = size): FetchDataInfo = {    if(maxSize < 0)      throw new IllegalArgumentException("Invalid max size for log read (%d)".format(maxSize))    // 日志文件的长度    val logSize = log.sizeInBytes // this may change, need to save a consistent copy    // 转换为物理地址,通过offsetIndex.lookup和FileMessageSet.searchFor,查找方法已在之前的博客中介绍    val startPosition = translateOffset(startOffset)    // if the start position is already off the end of the log, return null    if(startPosition == null)      return null    val offsetMetadata = new LogOffsetMetadata(startOffset, this.baseOffset, startPosition.position)    // if the size is zero, still return a log segment but with zero size    if(maxSize == 0)      return FetchDataInfo(offsetMetadata, MessageSet.Empty)    // 计算读取的字节数    val length = maxOffset match {      case None =>        // maxOffset为空,由maxPosition、maxSize决定读取的长度        min((maxPosition - startPosition.position).toInt, maxSize)      case Some(offset) =>        // maxOffset换成物理地址        if(offset < startOffset)          return FetchDataInfo(offsetMetadata, MessageSet.Empty)        val mapping = translateOffset(offset, startPosition.position)        val endPosition =          if(mapping == null)            logSize // the max offset is off the end of the log, use the end of the file          else            mapping.position        min(min(maxPosition, endPosition) - startPosition.position, maxSize).toInt    }    //返回消息    FetchDataInfo(offsetMetadata, log.read(startPosition.position, length))  }

LogSegment的recover方法根据日志文件重新索引文件。

 

def recover(maxMessageSize: Int): Int = {      // 清空索引文件,只是移动position指针,后续的写入会覆盖原有的内容。    index.truncate()    //修改索引文件的大小    index.resize(index.maxIndexSize)    //记录了已经通过验证的字节数    var validBytes = 0    //最后一个索引项对应的物理地址    var lastIndexEntry = 0    //FileMessageSet迭代器    val iter = log.iterator(maxMessageSize)    try {      while(iter.hasNext) {        val entry = iter.next        entry.message.ensureValid()        //符合添加索引的条件        if(validBytes - lastIndexEntry > indexIntervalBytes) {          // we need to decompress the message, if required, to get the offset of the first uncompressed message          val startOffset =            entry.message.compressionCodec match {              case NoCompressionCodec =>                entry.offset              case _ =>                //对消息进行解压缩,获取第一个消息的offset                ByteBufferMessageSet.deepIterator(entry).next().offset          }          //添加索引项          index.append(startOffset, validBytes)          lastIndexEntry = validBytes        }        //累加validBytes        validBytes += MessageSet.entrySize(entry.message)      }    } catch {      case e: CorruptRecordException =>        logger.warn("Found invalid messages in log segment %s at byte offset %d: %s.".format(log.file.getAbsolutePath, validBytes, e.getMessage))    }    //对日志文件进行截断,抛弃后面验证失败的message    val truncated = log.sizeInBytes - validBytes    log.truncateTo(validBytes)    index.trimToValidSize()    truncated  }

 

转载地址:http://gjxx.baihongyu.com/

你可能感兴趣的文章
mysql中int、bigint、smallint 和 tinyint的区别、char和varchar的区别详细介绍
查看>>
mysql中json_extract的使用方法
查看>>
mysql中json_extract的使用方法
查看>>
mysql中kill掉所有锁表的进程
查看>>
mysql中like % %模糊查询
查看>>
MySql中mvcc学习记录
查看>>
mysql中null和空字符串的区别与问题!
查看>>
MySQL中ON DUPLICATE KEY UPDATE的介绍与使用、批量更新、存在即更新不存在则插入
查看>>
MYSQL中TINYINT的取值范围
查看>>
MySQL中UPDATE语句的神奇技巧,让你操作数据库如虎添翼!
查看>>
Mysql中varchar类型数字排序不对踩坑记录
查看>>
MySQL中一条SQL语句到底是如何执行的呢?
查看>>
MySQL中你必须知道的10件事,1.5万字!
查看>>
MySQL中使用IN()查询到底走不走索引?
查看>>
Mysql中使用存储过程插入decimal和时间数据递增的模拟数据
查看>>
MySql中关于geometry类型的数据_空的时候如何插入处理_需用null_空字符串插入会报错_Cannot get geometry object from dat---MySql工作笔记003
查看>>
mysql中出现Incorrect DECIMAL value: '0' for column '' at row -1错误解决方案
查看>>
mysql中出现Unit mysql.service could not be found 的解决方法
查看>>
mysql中出现update-alternatives: 错误: 候选项路径 /etc/mysql/mysql.cnf 不存在 dpkg: 处理软件包 mysql-server-8.0的解决方法(全)
查看>>
Mysql中各类锁的机制图文详细解析(全)
查看>>