Kafka-之数据日志存储
1 kafka的日志布局
kafka的数据存储是基于文件系统的,kafka的数据以日志的形式存储在磁盘上,具体的日志布局可以看下图。
很明显,kafka以topic来进行数据划分,我们可以通过在server.properties
文件指定log.dirs
来指定数据日志存储
#指定日志存储路径
log.dirs=/tmp/kafka-logs1,tmp/kafka-logs2,....
虽说kafka按照topic进行数据划分,但是在物理存储上还是按照topic-partition进行目录划分,假如这里有个topicA有2个partition,那么它的存储结构如下:
cd /tmp/kafka-logs1/
topicA-0/
#在每个分区下的数据又是按日志分段进行存储的,每个日志分段LogSegment都有自己的索引文件和辅助文件
00000000000000000000.index # offset索引文件
00000000000000000000.log # 实际数据日志
00000000000000000000.timeindex # 时间戳索引文件
00000000000000000000.txnindex # unknown
00000000000000000000.snapshot # 快照记录文件
topicA-1/
.............
kafka的消息都是顺序写入磁盘的logSegment文件的,只有最后一个logSegment文件才能执行写入操作,在其之前的所有xxx.log都不能写入,通常我们将最后一个LogSegment称为
activeSegment
,之后追加的消息将写入activeSegment
,当其大小达到一定程度时就会重新生成一个新的activeSegment的.log
文件。为了方便日志分段文件检索,每个LogSegment都有对应的标识(baseoffset)和索引文件。
- .index(offset索引文件,用来检索offset)
- .timeindex(时间戳索引,用来检索时间戳)
- baseoffset(该值代表每个segment文件的数据的起始offset,如00000000000000133.log,那么该分段的起始offset是133,如果上一个分段文件为00000000000000000.log,那么上个分段文件中存储了133条消息)
还可能出现.delete,.cleaned,.swap,.txnindex,leader_ecpoch_checkpoint等文件。
从更加宏观的角度来讲,kafka的日志目录结构如下:
2 kafka日志格式演变
从kafka-0.8x到目前的2.x版本已经经历了3个大版本:V0,V1,V2
- V0:0.8x-0.10.0,不包括0.10.0
- V1:0.10.0到0.11.0版本之前,不包括0.11.0
- V2:0.11.0到如今
2.1 V0的消息格式
上图左边的就是V0版本的Record格式,通常通过LOG_OVERHEAD
与RECORD_OVERHEAD
同时来描述一个消息,右边的Message Set是消息容器,用于存储消息。
- crc32: crc32校验值,作用范围magic~value之间 (4B)
- magic: 消息版本号,这里是0 (1B)
- attributes:消息属性,0-3代表压缩格式(1B)
- 0代表NONE
- 1代表GZIP
- 2代表SNAPPY
- 3代表LZO4(0.9x引入)
- key length代表key的长度(4B),如果为-1,代表key为null,是没有设置key的
- key代表消息的key
- value length代表value的长度(4B)
- value代表消息
在V0版本中消息的最小长度为 4 + 1 + 1 + 4 + 4 = 14B(byte),如果小于这个值那么,那么这个消息就是一条破损的消息而不被接收,我们可以使用kafka内部的工具类查看置顶的segment文件的RECORD_OVERHEAD_V0(消息的最小值)信息
./kafka-run-class.sh kafka.tools.DumpLogSegments --files /tmp/kafka-logs/topic1-0/00000000000033568928.log
>>>>>>
#这里将会显示starting offset、offset、position、isvalid、payloadsize、magic、compressCodec、crc、keysize信息,当前演示版本为kafka-0.8.2.1
Starting offset : 33568928
offset: 33568928 position : 0 isvalid: true payloadsize: 5 magic: 0
compresscodec: NoCompressionCodec crc: 592888119 keysize : 3
2.2 V1的消息格式
除了新引入了一个timestamp,magic=1,其它的都是与V0相同,timestamp的大小为8B,所以V1版本的最小消息(RECORD_OVERHEAD_V1)为14+8=22B(byte)。
- 该timestamp字段由broker端的参数
log.message.timestamp.type
指定,默认是生产者创建消息的时间createTime; - 我们可以通过在创建ProducerRecord的时候进行指定,new ProducerRecord(,timestamp);
- 如果没有显式手动指定timestamp,那么就会选择time.milliseconds();
- V1与V0一样,都是通过LOG_OVERHEAD与RECORD_OVERHEAD来描述一个message。
2.3 V2的消息格式
V2版本的消息集从Message Set变成了Record Batch,这是一个处理批量消息读写的NIO 通道channel,在消息压缩的过中,Record Batch Header部分是不会被压缩的(也就是下图中first offset到records count部分),被压缩的是records字段中的所有内容,每个Records中包含一条或多条Record。
- Record Batch对应Producer client中的Producerbatch
- Record对应Producer Record中的ProducerRecord
V2版本的Record格式去除了crc,将其移动到了RECORDBATCH,另外增加了length、timestamp delta 、offset delta;key、key length、value、value length还是与V0,V1保持一致,具体的新增的字段如下。
- length:消息的总长度;
- attributes:字段在V2中虽然没被使用,但是任然为其保留1B的空间,保证可扩展;
- timestamp delta:时间戳增量,是当前消息的时间戳与RecordBatch的起始时间戳的差值,通常存储一个时间戳需要8个字节,这里存储的差值进一步减少了存储的消耗;
- offset delta:位移增量,如果数据条目特别多,那么存储offset也是需要较多的存储,这里存储的是当前消息的offset与RecordBatch起始offset的差值,进一步节省存储空间;
- headers:用来存储应用级别的扩展。
除了对RECORD本身做了修改,在V2版本还对RECORDBATCH进行了彻底的修改,除了对上面的crc校验,同时还新增了如下字段。
- first offset:这个RECORBATCH的起始位移
- length:从partition leader epoch字段到末尾的总长度,并不是消息的总长度
- partition leader epoch:可以看作leader的版本号,或者看作分区leader的更新次数
- magic: 消息格式的版本号
- attributes:消息属性,0~3位依旧是压缩类型
- last offset delta :RECORDBATCH中的最后一个RECORD的offset与first offset的差值(增量)
- first timestamp:第一条消息的时间戳
- producer id:生产者id:PID,用来与first sequence一起保证单个生产者-分区-单个session的幂等性
- producer epoch:与producer id一样,用来支持事物与幂等(递增序列)
- first sequence:与producer id,producer epoch一样,用来保证事物与幂等(递增序列)
- records count:该RECORDBATCH中RECORD的条目数
2.4 消息压缩
消息越多压缩的效果越明显,数据比较少的时候选择压缩反而不太好,通常kafka的消息在生产者这里就开始选择压缩,在Broker上也是以压缩的形式进行存储的,只有在消费数据处理数据的术后才会将消息进行解压,这样就保证了端到端的压缩。在kafka的参数中,压缩格式通过compress.type
来指定,默认为producer
,表示保留生产者使用的压缩方式,还可以选择lz4,snappy,gzip
等配置,分别表示使用LZ4,SNAPPY,GZIP
的压缩算法
注:压缩率 = 压缩后大小 /压缩前大小 * 100%,压缩率通常是越小越好
,而口语中常说压缩率越高越好。
kafka中有 2个概念,不能混淆
- compress message 使用压缩算法将消息进行压缩,减少内存,网络io,与磁盘的消耗
- compact message 将日志中的消息进行清理,类似于hbase的compact操作
消息的压缩是针对整个消息集RECORDBATCH(MESSAGE SET),将整个消息集压缩成wrapper message 和inner message,如下图
该途中offset为RECORDBATCH的first offset,内层消息中的offset为每个 record的offset与外层offset的增量,即(x - first offset)。