index采用稀疏存储的方式,它不会为每一条message都建立索引,而是每隔一定的字节数建立一条索引,避免索引文件占用过多的空间。缺点是没有建立索引的offset不能一次定位到message的位置,需要做一次顺序扫描,但是扫描的范围很小。
索引包含两个部分(均为4个字节的数字),分别为相对offset和position。相对offset表示segment文件中的offset,position表示message在数据文件中的位置。
总结:Kafka的Message存储采用了分区(partition),磁盘顺序读写,分段(LogSegment)和稀疏索引这几个手段来达到高效性
Partition and Replica一个topic物理上分为多个partition,位于不同的broker上。如果没有 replica,一旦broker宕机,其上所有的patition将不可用。
每个partition可以有多个replica(对应server.properties/default.replication.factor),分配到不同的broker上,其中有一个leader负责读写,处理来自producer和consumer的请求;其它作为follower从leader pull消息,保持与leader的同步。
如何分配partition和replica到broker上将所有Broker(假设共n个Broker)和待分配的Partition排序
将第i个Partition分配到第(i mod n)个Broker上
将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上
根据上面的分配规则,若replica的数量大于broker的数量,必定会有两个相同的replica分配到同一个broker上,产生冗余。因此replica的数量应该小于或等于broker的数量。
leader选举kafka 在 zookeeper 中(/brokers/topics/[topic]/partitions/[partition]/state)动态维护了一个 ISR(in-sync replicas),ISR 里面的所有 replica 都"跟上"了 leader,controller将会从ISR里选一个做leader。具体流程如下:
1. controller 在 zookeeper 的 /brokers/ids/[brokerId] 节点注册 Watcher,当 broker 宕机时 zookeeper 会 fire watch 2. controller 从 /brokers/ids 节点读取可用broker 3. controller决定set_p,该集合包含宕机 broker 上的所有 partition 4. 对 set_p 中的每一个 partition 4.1 从/brokers/topics/[topic]/partitions/[partition]/state 节点读取 ISR 4.2 决定新 leader 4.3 将新 leader、ISR、controller_epoch 和 leader_epoch 等信息写入 state 节点 5. 通过 RPC 向相关 broker 发送 leaderAndISRRequest 命令当ISR为空时,会选一个 replica(不一定是 ISR 成员)作为leader;当所有的 replica 都歇菜了,会等任意一个 replica 复活,将其作为leader。
ISR(同步列表)中的follower都"跟上"了leader,"跟上"并不表示完全一致,它由 server.properties/replica.lag.time.max.ms 配置,表示leader等待follower同步消息的最大时间,如果超时,leader将follower移除ISR。
配置项 replica.lag.max.messages 已经移除
replica同步kafka通过"拉模式"同步消息,即follower从leader批量拉取数据来同步。具体的可靠性,是由生产者(根据配置项producer.properties/acks)来决定的。
In Kafka 0.9, request.required.acks=-1 which configration of producer is replaced by acks=all, but this old config is remained in docs.(在0.9版本,生产者配置项 request.required.acks=-1 被 acks=all 取代,但是老的配置项还保留在文档中。ps: 最新的文档2.2.x request.required.acks 已经不存在了)
acks description0 producer发送消息后直接返回,不会等待服务器确认
1 服务器将记录写进本地log后返回,不会等待follower同步消息。leader宕机后可能丢失一部分未同步的消息
-1/all 服务器将记录写进本地log后,等待所有ISR内的消息同步后返回。除非leader和所有的ISR都挂掉,否则消息不会丢失
在acks=-1的时候,如果ISR少于min.insync.replicas指定的数目,将会抛出NotEnoughReplicas或NotEnoughReplicasAfterAppend异常。
Prodecer如何发送消息Producer首先将消息封装进一个ProducerRecord实例中。
消息路由发送消息时如果指定了partition,则直接使用;
如果指定了key,则对key进行哈希,选出一个partition。这个hash(即分区机制)由producer.properties/partitioner.class指定的类实现,这个路由类需要实现Partitioner接口;
如果都未指定,通过round-robin来选partition。