Kafka 存储机制

个人总结,以官网为准
如有错误,欢迎指出

存储方式

在底层的硬盘上,kafka会在对应的配置目录下,创建topic-partitionId的目录,如下。如果是多broker的情况下,会使用partitionId % broker数量的值决定在哪个broker上。

副本分配算法如下:

  • 将所有N Broker和待分配的i个Partition排序.
  • 将第i个Partition分配到第(i mod n)个Broker上.
  • 将第i个Partition的第j个副本分配到第((i + j) mod n)个Broker上.

在这里插入图片描述

分区内部

每一个分区中的数据被切割为一个个的segmentFile,每个segmentFile由一个index和log文件组成。

文件的命名为偏移量.log/index,初始的第一个文件开头为全是0(19个零).index/log.后面的每一个文件命名的偏移量是上一个文件最后一条消息的偏移量。也就是本文件的第一条消息的偏移量应该是文件名的偏移量加一。每个segment大小默认配置为500M,一旦满了就新建文件。这样有利于定时的清除消息,而不用拷贝或者移动消息,直接删除。(当然消息必须是顺序存储的,kafka也是这么做的)

在这里插入图片描述

怎么查找消息

在这里插入图片描述

比如查找368773消息

  • 首先在索引文件中找到大概在哪个index文件中,(切记文件名偏移量加一,才是每个文件的起始点)
  • -然后在index文件中找到一个区间或者一个准确的值,因为index文件是稀疏的存储了消息在本文件中偏移量(不是全局的偏移量)和这条消息的物理偏移量。比如某条消息全局偏移量是10,但它可能刚好在某一个文件的第一位。
  • 最后再在这个区间的物理偏移量直接进行查找
存储机制中影响读写快慢的优势
顺序写

网上有一种说法,顺序读写磁盘是比内存还快的。这种说法不知道对不对,但是也侧面说明了kafka顺序读写的效率之快。顺序读的前提要顺序写,那么怎么实现的呢?

之前一直在思考一个问题:

怎么呢保证顺序写呢?一直向文件末尾添加消息就是顺序写吗?

这一定是错的,计算机是多任务并行的,默认的写磁盘是随机的,一旦在你的线程失去cpu使用权后,其他的程序就会插队写入数据。那么顺序的写入就会变成了,局部顺序,全局随机。

直到我看到一篇文章讲解了,java是怎么实现顺序读写的。

public static long fileWrite(String filePath, String content, int index) {
     File file = new File(filePath);
     RandomAccessFile randomAccessTargetFile;
     MappedByteBuffer map;
     try {
          randomAccessTargetFile = new RandomAccessFile(file, "rw");
          FileChannel targetFileChannel = randomAccessTargetFile.getChannel();
          map = targetFileChannel.map(FileChannel.MapMode.READ_WRITE, 0, (long) 1024 * 1024 * 1024);
          map.position(index);
          map.put(content.getBytes());
          return map.position();
     } catch (IOException e) {
          e.printStackTrace();
     } finally {
     }
     return 0L;
}

从上面的代码中,看出每一次写入后都会返回一个position,也会在使用时传入一个index.

本人理解,顺序写入实际靠记住每一个的写入位置后,下一次从这个位置开始。

零copy技术(加速读)

这种技术是加速了数据传输的直接性,减少了数据拷贝的次数。它并不是真的是零次拷贝。

在这里插入图片描述

传统的数据传输方式

  • 首先从磁盘拷贝到页缓存(内核态)
  • 然后再拷贝应用程序缓存区(用户态)
  • 再然后拷贝到Socket缓存区
  • 最后通过网卡传输出去

其实蓝色的部分是不需要的,如果取消掉这一部分的开销。那么就更快的传输速度。

DMA技术,它可以实现硬件的直接交互,不经过CPU.上面的过程是经过了CPU的。

从而将过程优化为下图

在这里插入图片描述

页缓存

为了优化读写性能,Kafka利用了操作系统本身的Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。这样做的好处有:

(1)避免Object消耗:如果是使用Java堆,Java对象的内存消耗比较大,通常是所存储数据的两倍甚至更多。
(2)避免GC问题:随着JVM中数据不断增多,垃圾回收将会变得复杂与缓慢,使用系统缓存就不会存在GC问题。

用过 Java 的人一般都知道两点事实:对象的内存开销非常大,通常会是真实数据大小的几倍甚至更多,空间使用率低下;Java 的垃圾回收会随着堆内数据的增多而变得越来越慢。基于这些因素,使用文件系统并依赖于页缓存的做法明显要优于维护一个进程内缓存或其他结构,至少我们可以省去了一份进程内部的缓存消耗,同时还可以通过结构紧凑的字节码来替代使用对象的方式以节省更多的空间。

此外,即使 Kafka 服务重启,页缓存还是会保持有效,然而进程内的缓存却需要重建。这样也极大地简化了代码逻辑,因为维护页缓存和文件之间的一致性交由操作系统来负责,这样会比进程内维护更加安全有效。

Kafka的Log Retention的理解

即日志删除

  • 基于时间
    • 从segment的跳跃表中移除删除的segment
    • 将文件后缀改写.deleted
    • delete-finle命名的延迟任务去删除标识好的文件
  • 基于日志大小
    • 这里的大小指的是总的大小
  • 基于日志偏移量
    • 某个日志分段的下一个日志的偏移量小于LogStartOffset

也可以配置为合并

日志合并的,取相同key的最新的一条

需要将 log.cleanup.policy 设置为“compact”,并且还需要将 log.cleaner.enable

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值