kafka2.4之后,kafka提供了有限的读写分离,也就是说follower副本能够提供读服务
之前没有,因为读写分离适用于读负载很大,而写操作相对不频繁的场景.可kafka不属于这样的场景
同步机制:kafka采用pull 方式实现follwer的同步,因此Follwer与leader存在不一致性窗口,如果允许读follwer副本,就势必要处理消息滞后的问题
Kafka数据存储流程
Partition
一个有序的队列,以文件夹的形式存储在具体broker本机上
leo
表示每个partition的log最后一条message的位置
hw
表示partition各个replicas数据间同步且一致的offset位置,即表示allreplicas已经commit的位置
hw之前的数据才是commit后的,对消费者可见
ISR集合里面最小的leo
offset
Segment
ISR:partition leader会维持
Kafka高效文件存储设计特点:
kafka采用分片和索引机制,将每个partition分为多个segment
索引文件并不是为每条message建立索引,而是采用了稀疏存储的方式
即每隔一定字节的数据建立一条索引,避免索引文件占用过多空间和资源,从而可以将索引文件保存在内存中
缺点:没有建立索引的数据需要进行小范围的顺序扫描操作
Kafka把topic中一个partition大文件分成多个小文件段,通过多个小文件段,就容易定期清楚或删除已经消费完文件,
减少磁盘占用
通过索引信息可以快速定位message
producer生产数据要写入log文件中,写的过程是采用追加文件末尾的方式为顺序写
生产者发送到broker里面的策略和流程是怎样的?
1.如果指定partitionId 则PR被发送到指定partition
如果未指定id,但指定了key,PR会按照hash发送到对应partition
如果两者均未指定.PR会按照round-robin轮训模式发送到每个partition
2.kafka的客户端发送数据到服务器,先经过内存缓冲区默认16KB,通过kafkaProducer发送出去的消息都是先进入到客户端本地的内存缓冲区中,然后把很多消息收集到batch里面,再一次性发送到broker上去的,这样性能才可能提高。
3.发送的send方法是异步的,添加消息到缓冲区等待发送,并立即返回。
生产者将单个消息批量在一起发送来提高效率,一条消息发送之后,会阻塞当前线程,直至返回ack,发送消息后返回的一个future对象,调用get方法
消息发送主要有两个线程,main线程和sender线程
4.main线程发送消息到recordAcuccmulator返回
sender线程从recordAccmulator拉取信息发送到broker,发次数主要受batch,size和linger,ms两个参数影响
消费者消费机制和分区策略
消费者根据什么模式从broker获取数据?(消费者机制)
1.消费者采用pull拉取方式,从partition获取数据
pull模式可以根据consumer的消费能力进行自己调整,不同的消费者性能不一样,如果broker没有数据,comsumer可以设置超时时间,阻塞等待一段时间再返回.
如果是broker主动push,优点是可以快速处理消息,但是容易造成消费者处理不过来,造成消息堆积和延迟
2.消费者从哪个分区进行消费(分区策略)?
a.顶级接口org.apache.kafka.clients.comsumer.internals.AbstractPartitionAssignor
b.按照消费者组进行轮训分配,
同个消费者组监听不同主题一样,把所有partition和comsumer列出来,订阅主题不一样导致分配不均
弊端:如果同一消费者组内,所订阅的消息是不相同的,在执行分区分配的时候不会轮训分配,可能导致分区分配不均匀
c.rangeAssignor默认策略,按照主题进行分配,如果不平均分配则第一个消费者会分配的比较多,一个消费者监听不同主题也不影响
弊端;对于一个topic 消费者多消耗一个分区影响不大
topic越多消费的分区也越多,则性能有所下降
平衡:如何均衡的将topic下的所有partition分配给消费者,从而使得消息的消费速度达到最快
rebalance重平衡 重新进行partition的分配,从而使得partition分配重新达到平衡状态
发生rebalance的条件:
1.当消费者组内的消费者数量发生变化,就会产生重新分配partition
2.分区的数量发生变化时
自动提交offset问题?
1.没法控制消息是否正常被消费
2.适合非严谨的场景,比如日志收集发送
手工提交offset配置
1.初次启动消费者会去broker获取当前消费的offset的值.
2.同步commitSync阻塞当前线程,自动失败重试.
3.异步commitAsync不会阻塞当前线程,没有失败重试,回调callback函数获取提交信息,记录日志
kafka之间副本数据同步是怎样的?一致性怎么保证,数据怎样保证不丢失呢?
a.producer端:
保证生产者发送到指定的topic,topic下的每个partition收到发送数据后需要向producer发送ack确认收到,就会进行下一轮的发送否则重新发送数据
broker端(消息存储的可靠性):
1.kafka副本,一个topic下设置多个副本,副本数量小于broker数量.每个分区有一个leader和0到多个follwer,
replica分为leader replica 和follwer replica
2.副本间数据同步机制
当producer在向partition发送数据,根据ack机制,默认
ack=1,只会向leader中写入数据,并且接收,而且写入本地磁盘,不管其他follwer然后返回ack.然后leader中的数据会复制到其他replica中,follwer会周期性的从leader中pull数据。
存在的问题? leader接收到消息还没有同步给follwer此时leader所在broker挂了。
ack=0,producer发送一次就不再发送,不管是否发送成功,
存在的问题?如果中途broker挂了消息存在丢失
ack = -1或者all,producer只有收到分区内所有副本写入成功才会认为消息推送成功
leader会维持一个与其保持同步的replica集合,该集合就是ISR,leader副本也在isr里面
存在问题?
a .follwer同步完成后,broker发送ack之前,leader发生故障,
生产者端发生异常,会进行重新发送,造成数据重复.
b. ack=all 就可以代表数据一定不会丢失吗?
1.partition只有一个副本,也就是leader本身,任何follwer都没有
2.接收完消息后宕机,也会导致数据丢失,ack=all,必须跟isr列表至少有2个以后的副本配合使用
在设置ack=-1的同时,
也要min.insync.replicas这个参数设定isr的最小副本是多少,默认是1,如果isr中的副本数量小于配置值,客户端会返回异常.
每一个leader partition 都有一个isr,leader动态维护,要保证kafka不丢失message,就要保证isr这组集合存活,并且消息commit 成功,
当ISR中的partition follwer完成数据同步之后,就会给leader发送ack.如果follwer长时间未向leader同步数据,则该partition follwer将被踢出ISR
leader发生故障之后,就会从isr中选举新的leader
consumer端:
hw,保证消费数据的一致性和副本数据的一致性,指所有副本中最小的leo.
follwer故障:首先临时剔除ISR,待该follwer恢复后,读取本地的记录的hw,把该log文件高于hw部分截取掉,从hw开始向leader进行同步,等该follwer的leo大于等于该partition的hw,即follwer追上leader后,重新加入ISR.
leader故障:leader发生故障后,会从ISR中选出新的leader,为了保证多个副本数据的一致性,其余的follower会先将各自的log文件高于hw的部分截取掉,新leader不会截取,然后从新leader同步数据
ACK保障生产者投递可靠性
partition多副本保障了消息存储的可靠性
hw:是所有副本中最小的leo,保证消费数据的一致性和副本数据的一致性
日志清理策略:
背景:数据持久化到磁盘上,为了控制磁盘容量,需要对过去的消息进行清理。
1.内部有个定时任务定期检查删除日志,默认是5分钟。
2.基于时间删除
每个日志段文件都维护一个最大时间戳,每次新写入消息时,都会更新该字段,一个segment日志段写满后,不再接收消息,最大时间戳字段保持不变。
kafka通过将当前时间与该最大时间戳进行比较,判断是否过期
3.基于大小超过阈值
超过阈值的部分必须大于一个日志段的大小。
如果阈值设置的为1000mb,那么超过阈值的部分就是(500+10)mb
大于日志块的大小,此时会删除最老的那部分日志段
4.日志压缩
log.cleanup.policy=compact启用压缩策略
按照消息key进行整理,相同key不同value只保留最后一个
利用Page cache
用于缓存文件的页数据,页是逻辑上的概念,因此page cache 是与文件系统同级的,作用是加速数据的IO,写数据首先写入缓存,将写入的页进行标记为dirty,
之后向外部存储flush,读数据的时候先读取缓存,没有再读取外部存储。
每一个文件都是一棵树,树的每个节点都是一个页,根据文件内的偏移量确定所在页
kafka高性能zerocopy(SendFile)
Kafka高性能从如下几个点答:
1.存储模型,topic多分区,每个分区多segment段
2.索引文件查找,分段和稀疏存储
3.磁盘顺序写入效率高
4.异步发送,batich缓冲区批量发送
5.页缓存(不是很理解)
6.零拷贝 kafka采用sendFile