首先我们先来了解一下kafka日志的结构:每个topic的partition对应一个broker上一个目录,目录中的文件以日志的大小(log.segment.bytes)和时间(log.roll.hours)来roll。我们看到的kafka v0.10.2的日志文件包括三个部分,分别是xxxxxxxxxxxxxxxxxxxx.index、xxxxxxxxxxxxxxxxxxxx.log和xxxxxxxxxxxxxxxxxxxx.timeindex,其中xxxxxxxxxxxxxxxxxxxx代表的是offset,20位,从0开始。Kafka没有采用uuid的形式,为每个message分配一个message.id,而是通过offset来标记message,offset并不是消息在文件中的物理编号,而是一个逻辑编号,通过追加方式,每次加1。那通过offset如何查找消息的呢?
通过offset查找文件位置,分为3步:
1)对xxxxxxxxxxxxxxxxxxxx.log进行排序,通过二分查找,得到所在的xxxxxxxxxxxxxxxxxxxx.log文件;
2) 对应的xxxxxxxxxxxxxxxxxxxx.index文件,通过二分查找,找到对应的条目,也就是offset到position的映射;
3)拿到这个position,就可直接定位xxxxxxxxxxxxxxxxxxxx.log的位置。然后从这个位置顺序扫描,得到实际的消息。
这里index文件的目的,就是为了加快查找速度。如果没有index文件,通过扫描log文件,从头扫描,也可以找到位置。
一条消息在磁盘的存储格式如下:
offset : 8 bytes
message length: 4 bytes (value: 4 + 1 + 1 + 8(if magic value > 0) + 4 + K + 4 + V)
crc : 4 bytes
magicvalue : 1 byte
attributes : 1 byte
timestamp : 8 bytes (Only exists when magic valueis greater than zero)
key length : 4 bytes
key : K bytes
valuelength : 4 bytes
value : V bytes
在Linux系统中,当我们把数据写入文件系统之后,其实数据在操作系统的pagecache里面,并没有刷到磁盘上。如果操作系统挂了,数据就丢失了。一方面,应用程序可以调用fsync这个系统调用来强制刷盘,另一方面,操作系统有后台线程,定时刷盘。频繁调用fsync会影响性能,需要在性能和可靠性之间进行权衡。实际上,官方不建议通过上述的三个参数来强制写盘,认为数据的可靠性通过replica来保证,而强制flush数据到磁盘会对整体性能产生影响。
Kafka的持久性并非要求同步数据到磁盘,因为问题节点都是从副本中恢复数据。这样刷盘依赖操作系统及Kafka的后台刷盘机制。这样的好处是:无需调优、高吞吐量、低延时和可全量恢复。
【关注】
如果觉得文章对你有用,请关注本微信公众号 - 全栈生涯
领取专属 10元无门槛券
私享最新 技术干货