kafka是什么?
kafka比较官方的定义是一种高吞吐量的分布式发布订阅消息系统,主要用来做消息的异步处理、系统解耦、削峰控流处理;
kafka系统架构图
上图所示,就是kafka的整体架构图,主要来说分为四个部分,下面主要对这四个部分的功能做一个简单介绍
- topic:消息存放的目录即主题
- producer:消息生产者,主要是连接kafka的broker,发送消息
- broker:Kafka的服务实例就是一个broker
- consumer:主要是对消息进行消费,订阅具体的分组,消费队列里的消息
kafka Topic
Topic& partition
消息发送时都被发送到一个topic,其本质就是一个目录,而topic由是由一些Partition Logs(分区日志)组成,其组织结构如下图所示:
- 我们可以看到,每个Partition中的消息都是有序的,生产的消息被不断追加到Partition log上,其中的每一个消息都被赋予了一个唯一的offset值。
- Kafka集群会保存所有的消息,不管消息有没有被消费;我们可以设定消息的过期时间,只有过期的数据才会被自动清除以释放磁盘空间。比如我们设置消息过期时间为2天,那么这2天内的所有消息都会被保存到集群中,数据只有超过了两天才会被清除。
- Kafka需要维持的元数据只有一个–消费消息在Partition中的offset值,Consumer每消费一个消息,offset就会加1。其实消息的状态完全是由Consumer控制的,Consumer可以跟踪和重设这个offset值,这样的话Consumer就可以读取任意位置的消息。
parition & leader
- kafka中的数据是持久化的并且能够容错的。Kafka允许用户为每个topic设置副本数量,副本数量决定了有几个broker来存放写入的数据。如果你的副本数量设置为3,那么一份数据就会被存放在3台不同的机器上,那么就允许有2个机器失败。一般推荐副本数量至少为2,这样就可以保证增减、重启机器时不会影响到数据消费。如果对数据持久化有更高的要求,可以把副本数量设置为3或者更多。
- Producer在生产数据时,会按照一定规则(direct,round-robin,key modhash,custom)把消息发布到topic的各个partition中。上面将的副本都是以partition为单位的,不过只有一个partition的副本会被选举成leader作为读写用。
- 关于如何设置partition值需要考虑的因素。一个partition只能被一个消费者消费(一个消费者可以同时消费多个partition),因此,如果设置的partition的数量小于consumer的数量,就会有消费者消费不到数据。所以,推荐partition的数量一定要大于同时运行的consumer的数量。
producer的生产流程
- 1、ProducerInterceptors是一个拦截器,对发送的数据进行拦截
- 2、Serializer 对消息的key和value进行序列化
- 3、通过使用分区器作用在每一条消息上,实现数据分发进行入到topic不同的分区中
- 4、RecordAccumulator收集消息,实现批量发送
- 5、Sender线程从RecordAccumulator获取消息
- 6、构建ClientRequest对象
- 7、将ClientRequest交给 NetWorkClient准备发送
- 8、NetWorkClient 将请求放入到KafkaChannel的缓存(利用了RPC 和JAVA的非阻塞线程NIO)
- 9、发送请求到kafka集群
- 10、调用回调函数,接受到响应(ack应答-1,0,1 是leader和副本的应答数量)
consumer消费者
consumer 和sever的通信
- consumer在调用poll()操作的时候会触发offset的提交动作,也就是只有到下一次poll()的时候本次消费的offset才会被提交(当然,这是针对使用自动提交的设置的时候),同时还会检查是否需要rebalance等操作(这个等下面讲)。
- 当然,consumer和server之间肯定要有一个心跳检测,来让server知道consumer还是活着的,以免consumer被从group当中踢出去。
- kafka在consumer端引入了一个heartbeat 线程来辅助维持consumer的心跳,这个线程只做心跳维护和一些状态的同步,比如当前group处于rebalance状态heartbeat线程再发送心跳请求时会返回正在rebalance的状态,这个时候hearbeat线程会在consumer端和consumer共享的变量设置标志位,标识正在进行rebalance,这样的话,consumer在进行下一次poll()的时候会检查这个标志位,并作出一些动作。
consumer的offset管理
每个consumer group中的consumer的因为要消费对应的数据需要记录offset,同时server端也要记录这些offset以便于consumer端在短暂重启以后还能保持继续消费。在server端会有一个叫 Coordinator的组件来负责管理consumer group,和内置的topic(__consumer_offsets)来保存消费者组的offset
__consumer_offsets
- __consumer_offsets是 Kafka 内部使用的一个 topic,专门用来存储 group 消费的情况,默认情况下有50个 partition,每个 partition 三副本。
- 每个consumer group 的元数据存储到那个partition有一些规则,通过这个 abs(GroupId.hashCode()) % NumPartitions 来计算出一个值,(其中,NumPartitions 是 __consumer_offsets 的 partition 数,默认是50个),这个值代表了 __consumer_offsets 的一个 partition,而这个 partition 的 leader 即为这个 Group 要交互的 GroupCoordinator 所在的节点。
consumer group的rebalance
- 针对一个consumer group ,kafka server端的coordinator会在有新的consuemr加入或者现有的consumer 退出的时候触发rebalance,在rebalance阶段,所有现有的consumer都要重新加入这个group。重新加入分为两个阶段,1.join-group 请求以加入 group,2.然后再发送 sync-group 请求以获取被分配的partition等信息。
- coordinator端接收到第一个join-group请求以后,会进入rebalance阶段,然后等待所有的consuemr(指在此之前通过heartbeat监测到的存活的)发起join-group。等所有的consumer都发起了join-group以后,coordinator会从中选取一个consuemr作为leader来进行分配,coordinator把所有consumer的元数据信息都发送给这个leader,由他来判断如何分配。当leader将分配结果通过sync-group发送到coordiantor的时候,coordinator会在其他的consumer的sync-group中将分配结果下发。然后就完成了rebalance。
- kafka默认提供了两种分配partition策略:range 和 round-robin。当然 Kafka 采用了可插拔式的分配策略,你可以创建自己的分配器以实现不同的分配策略。实际上,由于目前 range 和 round-robin 两种分配器都有一些弊端
zookeeper在kafka中的作用
- broker
- 状态
zookeeper 记录了所有 broker 的存活状态,broker 会向 zookeeper 发送心跳请求来上报自己的状态。zookeeper 维护了一个正在运行并且属于集群的 broker 列表。 - 控制器选举
kafka 集群中有多个 broker,其中有一个会被选举为控制器。控制器负责管理整个集群所有分区和副本的状态,例如某个分区的 leader 故障了,控制器会选举新的 leader。从多个 broker 中选出控制器,这个工作就是 zookeeper 负责的。 - 限额权限
kafka 允许一些 client 有不同的生产和消费的限额。这些限额配置信息是保存在 zookeeper 里面的。 - 所有 topic 的访问控制信息也是由 zookeeper 维护的。
- 记录 ISR
ISR(in-sync replica) 是 partition 的一组同步集合,就是所有 follower 里面同步最积极的那部分。
一条消息只有被 ISR 中的成员都接收到,才被视为“已同步”状态。只有处于 ISR 集合中的副本才有资格被选举为 leader。zookeeper 记录着 ISR 的信息,而且是实时更新的,只要发现其中有成员不正常,马上移除。 - node 和 topic 注册
zookeeper 保存了所有 node 和 topic 的注册信息,可以方便的找到每个 broker 持有哪些 topic。node 和 topic 在 zookeeper 中是以临时节点的形式存在的,只要与 zookeeper 的 session 一关闭,他们的信息就没有了。 - topic 配置
zookeeper 保存了 topic 相关配置,例如 topic 列表、每个 topic 的 partition 数量、副本的位置等等。
- consumer
- offset
kafka 老版本中,consumer 的消费偏移量是默认存储在 zookeeper 中的。
新版本中,这个工作由 kafka 自己做了,kafka 专门做了一个 offset manager。 - 注册
和 broker 一样,consumer 也需要注册。consumer 会自动注册,注册的方式也是创建一个临时节点,consumer down 了之后就会自动销毁。 - 分区注册
kafka 的每个 partition 只能被消费组中的一个 consumer 消费,kafka 必须知道所有 partition 与 consumer 的关系。
kafka中controller的作用
一 选举Leader和ISR
控制器从ZK的/brokers/topics加载一个topic所有分区的所有副本,从分区副本列表中选出一个作为该分区的leader,并将该分区对应所有副本置于ISR列表,其他分区类似;其他topic的所有分区也类似。
二 同步元数据信息包括broker和分区的元数据信息
控制器架子ZK的/brokers/ids以及上一个步骤得到的topic下各分区leader和ISR将这些元数据信息同步到集群每个broker。而且通过下面所阐述的监控机制当有broker或者分区发生变更时及时更新到集群保证集群每一台broker缓存的是最新元数据。
三 broker增删监听与处理
- broker加入的监听和处理
- broker崩溃的监听与处理
四 topic变化监听与处理
- topic创建的监听与处理
控制器启动时就起一个监视器监视ZK/brokers/topics/子节点。当通过脚本或者请求创建一个topic后,该topic对应的所有分区及其副本都会写入该目录下的一个子节点。控制器的监视器发现这种变化后,控制器开始执行topic创建的相关流程包括leader选举和ISR并同步元数据信息到集群;且新增一个监视器监视ZK/brokers/topics/<新增topic子节点内容>防止该topic内容变化。
- topic删除的监听与处理
五 分区变化监听与变化处理
- 分区重分配监听与处理
分区重分配通过KAFKA管理员脚本执行完成一个topic下分区的副本重新分配broker。
- 分区扩展监听与处理
当创建一个topic后,控制器会增加一个监视器监视ZK/brokers/topics/<新增topic子节点内容>防止该topic内容变化。当通过脚本执行分扩展后会在该目录增加新的分区目录。控制器的监视器发现这种变化后,控制器开始执行分区扩展相应流程如选举leader和ISR并同步。
六 broker退出
相比较broker机器直接宕机或强制kill,通过脚本或kill -9 关闭一个broker我们称为broker优雅退出。即将关闭的broker向控制器发送退出请求后一直阻塞。
控制器接收到请求后,执行leader重选举和ISR后响应broker。broker接收后退出。
七.controller fail-over挂掉
- 集群在开始时集群中第一个broker通过在ZK/controller注册子节点brokerId使得自己成为该集群的控制器,其他broker虽然没有争取到控制器资格,但是都会起一个监视器监视ZK/controller以及向/controller_EPOCH注册子节点。
- 如果控制器所在broker退出、崩溃或与ZK会话失效则ZK会删除/controller内该子节点,各个broker的监视器发现这种变化后,每个broker开始竞争直到有一个竞争成为新的控制器,并向/controller注册子节点,以及向/controller_EPOCH注册子节点
Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么
- 分区中的所有副本统称为AR(Assigned Repllicas)
- 所有与leader副本保持一定程度同步的副本(包括Leader)组成ISR(In-Sync Replicas)
- 与leader副本同步滞后过多的副本(不包括leader)副本,组成OSR(Out-Sync Relipcas)
ISR集合是AR集合中的一个子集。消息会先发送到leader副本,然后follower副本才能从leader副本中拉取消息进行同步,同步期间内follower副本相对于leader副本而言会有一定程度的滞后。前面所说的“一定程度”是指可以忍受的滞后范围,这个范围可以通过参数进行配置。,由此可见:AR=ISR+OSR。在正常情况下,所有的follower副本都应该与leader副本保持一定程度的同步,即AR=ISR,OSR集合为空。
Leader副本负责维护和跟踪ISR集合中所有的follower副本的滞后状态,当follower副本落后太多或者失效时,leader副本会吧它从ISR集合中剔除。如果OSR集合中follower副本“追上”了Leader副本,之后再ISR集合中的副本才有资格被选举为leader,而在OSR集合中的副本则没有机会(这个原则可以通过修改对应的参数配置来改变)
ISR的伸缩:
Kafka在启动的时候会开启两个与ISR相关的定时任务,名称分别为“isr-expiration"和”isr-change-propagation".。isr-expiration任务会周期性的检测每个分区是否需要缩减其ISR集合。这个周期和“replica.lag.time.max.ms”参数有关。大小是这个参数一半。默认值为5000ms,当检测到ISR中有是失效的副本的时候,就会缩减ISR集合。如果某个分区的ISR集合发生变更, 则会将变更后的数据记录到ZooKerper对应/brokers/topics//partition//state节点中。
除此之外,当ISR集合发生变更的时候还会将变更后的记录缓存到isrChangeSet中,isr-change-propagation任务会周期性(固定值为2500ms)地检查isrChangeSet,如果发现isrChangeSet中有ISR集合的变更记录,那么它会在Zookeeper的/isr_change_notification的路径下创建一个以isr_change开头的持久顺序节点(比如/isr_change_notification/isr_change_0000000000),并将isrChangeSet中的信息保存到这个节点中。kafka控制器为/isr_change_notification添加了一个Watcher,当这个节点中有子节点发生变化的时候会触发Watcher动作,以此通知控制器更新相关的元数据信息并向它管理的broker节点发送更新元数据信息的请求。最后删除/isr_change_notification的路径下已经处理过的节点。频繁的触发Watcher会影响kafka控制器,zookeeper甚至其他的broker性能。为了避免这种情况,kafka添加了指定的条件,当检测到分区ISR集合发生变化的时候,还需要检查一下两个条件:
(1).上一次ISR集合发生变化距离现在已经超过5秒,
(2).上一次写入zookeeper的时候距离现在已经超过60秒。
满足以上两个条件之一者可以将ISR写入集合的变化的目标节点。
有缩减就会有补充,那么kafka何时扩充ISR的?
随着follower副本不断进行消息同步,follower副本LEO也会逐渐后移,并且最终赶上leader副本,此时follower副本就有资格进入ISR集合,追赶上leader副本的判定准侧是此副本的LEO是否小于leader副本HW,这里并不是和leader副本LEO相比。ISR扩充之后同样会更新ZooKeeper中的/broker/topics//partition//state节点和isrChangeSet,之后的步骤就和ISR收缩的时的相同。
当ISR集合发生增减时,或者ISR集合中任一副本LEO发生变化时,都会影响整个分区的HW。
Kafka中的HW、LEO、LSO、LW等分别代表什么?
- (High Watermark)俗称高水位,它标识了一个特定的消息偏移量(offset),消费者只能拉取到这个offset之前的消息。
下图表示一个日志文件,这个日志文件中只有9条消息,第一条消息的offset(LogStartOffset)为0,最有一条消息的offset为8,offset为9的消息使用虚线表示的,代表下一条待写入的消息。日志文件的 HW为6,表示消费者只能拉取offset在 0 到 5之间的消息,offset为6的消息对消费者而言是不可见的。
- (Log End Offset),标识当前日志文件中下一条待写入的消息的offset。上图中offset为9的位置即为当前日志文件的 LEO,LEO 的大小相当于当前日志分区中最后一条消息的offset值加1.分区 ISR 集合中的每个副本都会维护自身的 LEO ,而 ISR 集合中最小的 LEO 即为分区的 HW,对消费者而言只能消费 HW 之前的消息。
- Last Stable Offset 对未完成的事务而言,LSO 的值等于事务中第一条消息的位置(firstUnstableOffset),对已完成的事务而言,它的值同 HW 相同
- LW:Low Watermark 低水位, 代表 AR 集合中最小的 logStartOffset 值
Kafka的日志目录结构
- segment file 组成 由3部分组成,分别为 index file和 data file,timeindex 索引文件,文件是一一对应的,后缀”.index”和”.log”分别表示索引文件和数据文件;".timeindex" 索引文件,基于时间的索引文件;目前支持的时间戳类型有两种: CreateTime 和 LogAppendTime 前者表示 producer 创建这条消息的时间;后者表示 broker 接收到这条消息的时间(严格来说,是 leader broker 将这条消息写入到 log 的时间)
- segment file 命名规则:partition 的第一个 segment 从0开始,后续每个 segment 文件名为上一个 segment 文件最后一条消息的 offset, ofsset 的数值最大为64位(long 类型),20位数字字符长度,没有数字用0填充。
关于 segment file 中 index 索引文件与 data 文件对应关系图
- segment的索引文件中存储着大量的元数据,数据文件中存储着大量消息,索引文件中的元数据指向对应数据文件中的 message 的物理偏移地址。以索引文件中的6,1407为例,在数据文件中表示第6个 message(在全局 partition 表示第368775个 message),以及该消息的物理偏移地址为1407,索引文件中的索引是稀疏索引.
- Kafka message 结构如下图:
-
索引文件包含两个部分(均为4个字节),分别为 offset 和 position。
-
通过 offset 查找 message 分为两个步骤:
通过二分查找文件列表,快速定位到具体的.index和.log文件; 即找到小于或等于给定 offset 的最大的 offset 文件。
通过 segment file 查找具体的 message,找到具体的文件后,先定位到 .index 文件中的 元数据物理位置 position, 即 .log 文件中的物理偏移地址。 然后在通过顺序查找到具体的 offset。
Kafka 高效文件存储设计特点:
- 将 topic 中一个 partition 大文件分割成多个小文件段,这样更容易定期清除和删除已经消费完的文件,减少磁盘占用。
- 通过索引可以快速定位到 message 和 确定 response 的最大大小。
- 通过 index 元数据全部映射到 memory,可以避免 segment file的 IO 磁盘操作。
- 通过索引文件稀疏存储,可以大幅降低 index 文件元数据占用空间大小。
Kafka中是怎么体现消息顺序性的?
- Kafka只能保证分区内消息顺序有序,无法保证全局有序
- 生产者:通过分区的leader副本负责数据顺序写入,来保证消息顺序性
- 消费者:同一个分区内的消息只能被一个group里的一个消费者消费,保证分区内消费有序
- 为什么做不到全局有序:
- 因为消息会发送到不一样的分区,分区之间发送的顺序是无法保证的
- 如何做到并发且全局有序?
-
【整体思路】想办法让需要保证顺序的数据发送到同一个分区中,并增加生产者/消费者的并发度
-
【方案1】topic设置一个分区,发送端和消费端开启多线程生产和消费
- 【优点】实现简单
- 【弊端】有热点瓶颈问题,服务端压力大
-
【方案2】topic设置多个分区,自定义发送端的分区策略,数据发送到同一个分区中,消费端开启多线程消费
- 【优点】扩展多个分区分摊了非同类数据写入同个分区的压力
- 【弊端】相同业务的数据在同一个分区依然有热点瓶颈的问题
-
【方案3】topic设置多个分区,自定义发送端的分区策略,数据发送不同分区,消费时按发送分区的顺序消费,发送和消费端都启动多线程来提高并发度
- 【步骤】
自义分区器,使得消息按分区号大小顺序依次发送相同数量大小的数据发送端和消费端启动多个消费线程进行生产和消费线程之间按分区号大小顺序消费数据
- 【弊端】消费性能极大下降,无法真正并发
-
KafkaConsumer是非线程安全的,那么怎么样实现多线程消费?
使用两种多线程模式消费数据
KafkaProducer是线程安全的,然而 KafkaConsumer却是非线程安全的。 Kafka Consumer中定义了一个 acquire(方法,用来检测当前是否只有一个线程在操作,若有其他线程正在操作则会抛出 Concurrentmodifcationexception异常:
java.util.ConcurrentModificationException: KafkaConsumer is not safe for multi-threaded access.
第一种多线程消费实现方式
KafkaConsumer非线程安全并不意味着我们在消费消息的时候只能以单线程的方式执行。如果生产者发送消息的速度大于消费者处理消息的速度,那么就会有越来越多的消息得不到及时的消费,造成了一定的延迟。除此之外,由于Kafka中消息保留机制的作用,有些消息有可能在被消费之前就被清理了,从而造成消息的丢失。我们可以通过多线程的方式来实现消息消费,多线程的目的就是为了提高整体的消费能力。多线程的实现方式有多种,第一种也是最常见的方式:线程封闭,即为每个线程实例化一个KafkaConsumer对象
第二种基于数据处理的多线程消费实现
如果处理数据的地方对消息的处理非常迅速,那么pollo拉取的频次也会更高,进而整体消费的性能也会提升;相反,如果在这里对消息的处理缓慢,比如进行一个事务性操作,或者等待一个RPC的同步响应,那么poll(拉取的频次也会随之下降,进而造成整体消费性能的下降。一般而言, pol()拉取消息的速度是相当快的,而整体消费的瓶颈也正是在处理消息这一块,如果我们通过一定的方式来改进这一部分,那么我们就能带动整体消费性能的提升,因此将处理消息模块改成多线程的实现方式。
Kafka中的幂等性
Producer在生产发送消息时,难免会重复发送消息。Producer进行retry时会产生重试机制,发生消息重复发送。而引入幂等性后,重复发送只会生成一条有效的消息。
Kafka的幂等性是如何实现的?
Kafka为了实现幂等性,它在底层设计架构中引入了ProducerID和SequenceNumber。那这两个概念的用途是什么呢?
- ProducerID:在每个新的Producer初始化时,会被分配一个唯一的ProducerID,这个ProducerID对客户端使用者是不可见的。
- SequenceNumber:对于每个ProducerID,Producer发送数据的每个Topic和Partition都对应一个从0开始单调递增的SequenceNumber值。
Kafka中的事务
与幂等性有关的另外一个特性就是事务。Kafka中的事务与数据库的事务类似,Kafka中的事务属性是指一系列的Producer生产消息和消费消息提交Offsets的操作在一个事务中,即原子性操作。对应的结果是同时成功或者同时失败。
这里需要与数据库中事务进行区别,操作数据库中的事务指一系列的增删查改,对Kafka来说,操作事务是指一系列的生产和消费等原子性操作。
在事务属性引入之前,先引入Producer的幂等性,它的作用为:
- Producer多次发送消息可以封装成一个原子性操作,即同时成功,或者同时失败;
- 消费者&生产者模式下,因为Consumer在CommitOffsets出现问题时,导致重复消费消息时,Producer重复生产消息。需要将这个模式下Consumer的Commit Offsets操作和Producer一系列生产消息的操作封装成一个原子性操作。
产生的场景有:
- 比如,在Consumer中Commit Offsets时,当Consumer在消费完成时Commit的Offsets为100(假设最近一次Commit的Offsets为50),那么执行触发Balance时,其他Consumer就会重复消费消息(消费的Offsets介于50~100之间的消息)。
日志留存策略
日志留存策略,就是Kafka保存topic数据的规则
一、留存策略类型
目前,与日志留存方式相关的策略类型主要有两种:
- delete:一般是使用按照时间保留的策略,当不活跃的segment的时间戳是大于设置的时间的时候,当前segment就会被删除
- compact: 日志不会被删除,会被去重清理,这种模式要求每个record都必须有key,然后kafka会按照一定的时机清理segment中的key,对于同一个key只保留罪行的那个key.同样的,compact也只针对不活跃的segment
二、留存机制及其工作原理
- 在开始详细介绍各种留存机制之前,先简要说下Kafka是如何处理日志留存的。每个Kafka broker启动时,都会在后台开启一个定时任务,定期地去检查并执行所有topic日志留存,这个定时任务触发的时间周期由broker端参数log.retention.check.interval.ms控制,默认是5分钟,即每台broker每5分钟都会尝试去检查一下是否有可以删除的日志。因此如果你要缩短这个间隔,只需要调小log.retention.check.interval.ms即可。
- 鉴于日志留存和日志删除实际上是一个问题的两个方面,因而我们下面讨论的是关于Kafka根据什么规则来删除日志。但有一点要强调一下,待删除的标的是日志段,即LogSegment,也就是以.log结尾的一个个文件,而非整个文件夹。另外还有一点也很重要,当前日志段(active logsegment)是永远不会被删除的,不管用户配置了哪种留存机制。
Kafka底层存储(页缓存、内核层、块层、设备层)
保存在磁盘,基于磁盘的顺序写。 采用页缓存、零拷贝技术。
零拷贝如图:
Kafka的延时操作的原理
延迟操作包括延迟拉取、延迟数据删除、延迟生产等。 每个延迟操作会被放到一个延迟操作管理器当中,每一个延迟操作管理器会配备一个定时器,定时器底层数据是基于时间轮实现。
为什么Kafka不支持读写分离?
-
在 Kafka 中,生产者写入消息、消费者读取消息的操作都是与 leader 副本进行交互的,从 而实现的是一种主写主读的生产消费模型。数据库、Redis 等都具备主写主读的功能,与此同时还支持主写从读的功能,主写从读也就是读写分离,为了与主写主读对应,这里就以主写从读来称呼。Kafka 并不支持主写从读,这是为什么呢?
-
从代码层面上来说,虽然增加了代码复杂度,但在 Kafka 中这种功能完全可以支持。对于 这个问题,我们可以从“收益点”这个角度来做具体分析。主写从读可以让从节点去分担主节 点的负载压力,预防主节点负载过重而从节点却空闲的情况发生。但是主写从读也有 2 个很明 显的缺点:
- (1)数据一致性问题。数据从主节点转到从节点必然会有一个延时的时间窗口,这个时间 窗口会导致主从节点之间的数据不一致。某一时刻,在主节点和从节点中 A 数据的值都为 X, 之后将主节点中 A 的值修改为 Y,那么在这个变更通知到从节点之前,应用读取从节点中的 A 数据的值并不为最新的 Y,由此便产生了数据不一致的问题。
- (2)延时问题。类似 Redis 这种组件,数据从写入主节点到同步至从节点中的过程需要经 历网络→主节点内存→网络→从节点内存这几个阶段,整个过程会耗费一定的时间。而在 Kafka 中,主从同步会比 Redis 更加耗时,它需要经历网络→主节点内存→主节点磁盘→网络→从节 点内存→从节点磁盘这几个阶段。对延时敏感的应用而言,主写从读的功能并不太适用。
怎么计算Lag?(注意read_uncommitted和read_committed状态下的不同)
Lag=LEO-CURRENT-OFFSET。计算出来的值即为消费延迟情况。
kafka的高可靠性,数据一致性
- 生产者生产数据有ack 应答机制,可以保证数据不丢失
- broker保存数据有分区下的副本,持久的存储数据
- HW高水位线保证数据的一致性