1. 日志索引

偏移量索引文件用来建立消息偏移量到物理地址之间的映射关系,方便快速定位消息所在的物理文件位置;时间戳索引文件则根据指定的时间戳来查找对应的偏移量信息。

Kafka 中的索引文件以稀疏索引( sparse index )的方式构造消息的索引,它并不保证每个消息在索引文件中都有对应的索引项。每当写入一定量(由 broker 端参数 log.index.interval.bytes 指定,默认值为4096 ,即 4KB )的消息时,偏移量索引文件和时间戳索引文件分别增加一个偏移量索引项和时间戳索引项,增大或减小 log.index.interval.bytes 的值,对应地可以增加或缩小索引项的密度。

稀疏索引通过 MappedByteBuffer 将索引文件映射到内存中,以加快索引的查询速度。偏移量索引文件中的偏移量是单调递增的,查询指定偏移量时,使用二分查找法来快速定位偏移量的位置,如果指定的偏移量不在索引文件中,则会返回小于指定偏移量的最大偏移量。时间戳索引文件中的时间戳也保持严格的单调递增,查询指定时间戳时,也根据二分查找法来查找不大于该时间戳的最大偏移量,至于要找到对应的物理文件位置还需要根据偏移量索引文件来进行再次定位。稀疏索引的方式是在磁盘空间、内存空间、查找时间等多方面之间的一个折中。

日志分段文件切分包含了以下几个条件就会切分,满足其中一个即可。

  • 当前日志分段文件的大小超过了 broker 端参数 log.segment.bytes 配置的值。 log.segment.bytes 参数的默认值为 1073741824 ,即 lGB。
  • 当前日志分段中消息的最大时间戳与当前系统的时间戳的差值大于 log.roll.ms或log.roll.hours 参数配置的值。如果同时配置了 log.roll.ms或log.roll.hours 参数, 那么 log.roll.ms 的优先级高 默认情况下,只配置了 log.roll.hours 参数,其值为 168, 即7天。
  • 偏移量索引文件或时间戳索引文件的大小达到 broker 端参数 log.index.size .max.bytes 配置的值。 log.index.size.max.bytes 的默认值为 10485760 ,即 10MB。
  • 追加的消息的偏移量与当前日志分段的偏移量之间的差值大于 Integer.MAX_VALUE, 即要追加的消息的偏移量不能转变为相对偏移量( offset - baseOffset > Int ger.MAX_VALUE )。

对非当前活跃的日志分段而言,其对应的索引文件内容己经固定而不需要再写入索引项, 所以会被设定为只读 而对当前活跃的日志分段 activeSegment )而言,索引文件还会追加更多的索引项,所以被设定为可读写。在索引文件切分的时候,Kafka会关闭当前正在写入的索引文件并置为只读模式,同时以可读写的模式创建新的索引文件,索引文件的大小由broker端参数log.index.size.max.bytes 配置。 Kafka 在创建索引文件的时候会为其预分配 log.index size.max.bytes 大小的空间,注意这一点与日志分段文件不同,只有当索引文件进行切分的时候, Kafka 才会把该索引文件裁剪到实际的数据大小。也就是说,与当前活 跃的日志分段对应的索引文件的大小固定为 log.index.size.max.bytes ,而其余日志分段对应的索引文件的大小为实际的占用空间。

2. 日志清理

通过broker端参数log.cleanup.policy来设置日志清理策略,此参数默认是delete,如果需要采用日志压缩的清理策略,就需要将log.cleanup.policy设置为compact,并且还需要将log.cleaner.enable设定为true。

日志删除:按照一定的保留策略直接删除不符合条件的日志分段

日志压缩:针对每个消息的key进行整合,对于有相同key的不同

3. 磁盘存储

Kafka使用文件系统(文件系统)来存储和缓存消息;

实验表明6块7200r/min的RAID5阵列组成的磁盘簇的顺序写入速度可以达到600M/s,但是随机写入速度只有100K/s,两者的性能相差6000倍。

OS针对顺序读写可以做深层次优化,比如预读(提前将一个比较大的磁盘块读入内存)、后写(将很多小的逻辑写操作合并起来组成一个大的物理写操作)。

Kafka 在设计时采用文件追加的方式来写入消息,也就是只能在日志文件的尾部追加新的消息,而且不允许修改已经写入的消息,这种方式决定了kafka属于顺序写。

3.1 页缓存

页缓存是操作系统实现的一种主要的磁盘缓存。简单来讲就是将磁盘中的数据缓存到内存中,把对磁盘的访问变为对内存的方位。

具体来说:当一个进程准备读取磁盘上的文件内容的时候,操作系统会先查看待读取的数据所在的页是否在页缓存中,如果命中则直接返回数据,从而避免对物理磁盘的I/O操作;如果没有命中,则操作系统会向磁盘发起读取请求并将读取的数据页存入页缓存,之后再将数据返回;当一个进程需要将数据写入磁盘,那么操作系统也会检测数据对应的页是否在页缓存中,如果不存在,则会先在页缓存中添加相应的页,最后再将数据写入对应的页。被修改后的页变成了脏页,操作系统会在合适的时间把脏页中的数据写入磁盘,以保持数据的一致性。

脏页的刷写,有操作系统控制,一般不建议修改;

对于一个进程而言,它会在操作系统内存缓存处理所需的数据,然而这些数据有可能还缓存在操作系统的页缓存中,因此同一份数据可能被缓存了两次,除非使用Direct I/O方式,页缓存很难被禁止。

kafka直接使用页缓存的好处:1、省去了进程内部的缓存消耗;2、不担心GC带来的性能问题而且可以使用的额内存不再受jvm内存的限制;3、即使进程重启页缓存也不会消失。

关于SWAP分区,作用是把当前非活跃的进程调入swap分区,以此将内存空出来给到活跃的进程。但是对于大量使用页缓存的kafka应用来说,应当尽量避免这种内存交换,否则将会对各方面的内存产生很大的负面影响。建议设置为1,这样可以即保留swap机制又可以最大程度限制它对kafka的影响。


本站由 卡卡龙 使用 Stellar 1.29.1主题创建

本站访问量 次. 本文阅读量 次.