1 名詞解釋
- Topic:可以認為一個一類消息,生產(chǎn)者和消費者通過topic進行數(shù)據(jù)傳輸
- partition:Kafka 中消息是以 topic 進行分類的微宝,生產(chǎn)者通過 topic 向 Kafka broker 發(fā)送消息,消費者通過 topic 讀取數(shù)據(jù)行瑞。如果 partition 規(guī)則設置的合理叹阔,所有消息可以均勻分布到不同的 partition 里截酷,這樣就實現(xiàn)了水平擴展(解決了性能問題)蔓同。同時生產(chǎn)者可以通過設置key指定分片饶辙,實現(xiàn)局部有序
2 存儲原理
隨著生產(chǎn)者不斷生產(chǎn)消息,partition在實際保存的時候會不斷擴張斑粱,對于消息文件的維護和過期數(shù)據(jù)的清理帶來影響弃揽,所以partition又細分成segment,方便過期數(shù)據(jù)的清理珊佣。
segment 文件由兩部分組成蹋宦,分別為“.index”文件(索引文件,存儲大量的元數(shù)據(jù))和“.log”文件(存儲大量的消息)咒锻,分別表示為 segment 索引文件和數(shù)據(jù)文件。這兩個文件的命令規(guī)則為:partition 全局的第一個 segment 從 0 開始守屉,后續(xù)每個 segment 文件名為上一個 segment 文件最后一條消息的 offset 值惑艇,數(shù)值大小為 64 位,20 位數(shù)字字符長度拇泛,沒有數(shù)字用 0 填充滨巴,如下:
00000000000000000000.index
00000000000000000000.log
00000000000000170410.index
00000000000000170410.log
00000000000000239430.index
00000000000000239430.log
以上面的 segment 文件為例,展示出 segment:00000000000000170410 的“.index”文件和“.log”文件的對應的關系俺叭,如下圖:
如上圖恭取,索引文件中的元數(shù)據(jù)指向對應數(shù)據(jù)文件中 message 的物理偏移地址。其中以“.index”索引文件中的元數(shù)據(jù) [3, 348] 為例熄守,在“.log”數(shù)據(jù)文件表示第 3 個消息蜈垮,即在全局 partition 中表示 170410+3=170413 個消息耗跛,該消息的物理偏移地址為 348。
那么如何從 partition 中通過 offset 查找 message 呢攒发?以上圖為例调塌,讀取 offset=170418 的消息,首先查找 segment 文件惠猿,其中 00000000000000000000.index 為最開始的文件羔砾,第二個文件為 00000000000000170410.index(起始偏移為 170410+1=170411),而第三個文件為 00000000000000239430.index(起始偏移為 239430+1=239431)偶妖,所以這個 offset=170418 就落到了第二個文件之中姜凄。其他后續(xù)文件可以依次類推,以其實偏移量命名并排列這些文件趾访,然后根據(jù)二分查找法就可以快速定位到具體文件位置檀葛。其次根據(jù) 00000000000000170410.index 文件中的 [8,1325] 定位到 00000000000000170410.log 文件中的 1325 的位置進行讀取。
3 ISR
ISR (In-Sync Replicas)腹缩,指副本同步隊列屿聋,由 leader 維護 ,通過ISR維護正常同步的副本集合(包括leader和follower)藏鹊,在ISR列表中的副本的延遲條數(shù)replica.lag.time.max.ms在設置的范圍內润讥,超過閾值會把 follower 剔除出 ISR。
Kafka 的 ISR 的管理最終都會反饋到 Zookeeper 節(jié)點上盘寡,通過leader單獨的線程定期檢測 follower楚殿,并將新的 ISR 的信息返回到 Zookeeper 的相關節(jié)點中。
為什么需要ISR竿痰?
Leader宕機后通常需要重新選舉Leader脆粥,同時為了保證數(shù)據(jù)不丟失,新Leader必須包含完整的數(shù)據(jù)影涉。同步復制要求所有能工作的follower副本都復制完变隔,這條消息才會被確認已成功提交,這種復制方式極大的影響了性能蟹倾。kafka使用這種ISR的方式匣缘,只維護了部分符合要求的副本,有效的權衡了數(shù)據(jù)可靠性和性能之間的關系鲜棠。
為什么不使用類似ZK的“少數(shù)服從多數(shù)”原則肌厨?
如果可以容忍N個節(jié)點掛掉,那么就需要部署2N+1個節(jié)點豁陆;柑爸。也就是說,在生產(chǎn)環(huán)境下為了保證較高的容錯率盒音,必須要有大量的副本表鳍,而大量的副本又會在大數(shù)據(jù)量下的協(xié)商會導致性能的急劇下降(也是kafka為什么不在zk里保存offset的原因)馅而。
4 HW 和 LEO
LEO(Log End Offset),標識當前日志文件中下一條待寫入的消息的offset进胯;
HW (High Watermark)用爪,取一個 partition 對應的 ISR 中最小的 LEO 作為 HW,consumer 最多只能消費到 HW 所在的位置胁镐。
假如leader副本的LEO為5偎血,follower1的LEO為5,follower2的LEO 為4盯漂,那么當前分區(qū)的HW取最小值4颇玷,此時消費者可以消費到offset0至3之間的消息;當所有副本都成功寫入消息3和消息4之后就缆,整個分區(qū)的HW和LEO都變?yōu)?帖渠,因此消費者可以消費到offset為4的消息了。
為什么需要HW和LEO竭宰?
保證副本數(shù)據(jù)一致性空郊,因為HW代表了ISR所有副本中都已經(jīng)同步的數(shù)據(jù),而log是直接做追加操作切揭,要先截斷到HW位置狞甚,保證自己沒有多余的數(shù)據(jù),然后在追加
5 數(shù)據(jù)可靠性保證
當 producer 向 leader 發(fā)送數(shù)據(jù)時廓旬,可以通過 request.required.acks 參數(shù)來設置數(shù)據(jù)可靠性的級別:
- 0: 意味著 producer 無需等待來自 broker 的確認而繼續(xù)發(fā)送下一批消息哼审。這種情況下數(shù)據(jù)傳輸效率最高,但是數(shù)據(jù)可靠性確是最低的孕豹。
- 1:這意味著 producer 在 ISR 中的 leader 已成功收到的數(shù)據(jù)并得到確認后發(fā)送下一條 message涩盾。如果 leader 宕機了,則會丟失數(shù)據(jù)励背。
- -1: producer 需要等待 ISR 中的所有 follower 都確認接收到數(shù)據(jù)后才算一次發(fā)送完成春霍,可靠性最高。(生產(chǎn)環(huán)境下如果不能容忍數(shù)據(jù)丟失椅野,應該設置為-1终畅,并且配合min.insync.replicas>=2來保證完全不丟失,如果min.insync.replicas設置為1竟闪,當 ISR 中只有 leader 時,這樣就變成了 acks=1 的情況杖狼。)
kafka會不會因為操作系統(tǒng)pagecache導致數(shù)據(jù)丟失
為了減少落盤次數(shù)炼蛤,提高效率,kafka的broker會先存儲到頁緩存(Page cache)中蝶涩,按照時間或者其他條件進行刷盤(從page cache到file)理朋;如果生產(chǎn)速度和消費速度速度相差不大絮识,是可以依賴page cache直接完成數(shù)據(jù)交互的
但是當leader寫入pagecache后宕機,數(shù)據(jù)并沒有來得及落盤嗽上,數(shù)據(jù)會丟失次舌;如果想保證數(shù)據(jù)不丟失,可以依賴request.required.acks = -1來保證副本都已經(jīng)同步數(shù)據(jù)了兽愤,相當于kafka并不需要擔心pagecache引起的數(shù)據(jù)丟失彼念,而是使用follower副本避免數(shù)據(jù)丟失問題
6 消費者offset移交
consumer提交offset有兩種方式:
- 自動提交,讓消費者自動提交偏移量浅萧。 enable.auto.comnit 被設為 true逐沙,消費者會自動把從 poll()方法接收到的最大偏移量提交上去。 提交時間間隔由 auto.commit.interval.ms 控制洼畅,默認值是 5s吩案。因為太省事了,以至于喪失了很大的靈活性和可控性帝簇,完全沒法把控 Consumer 端的位移管理徘郭。
- 手動提交,包含同步提交 丧肴、異步提交 残揉、異步+同步 組合的方式提交三種方式,在保證消費端消費完成后手動調用提交闪湾,可以保證消息不丟失(前提是客戶端沒有新開線程去處理)
offset提交到哪里冲甘?
這個提交過程不僅要實現(xiàn)高持久性,還要支持高頻的寫操作途样,顯然江醇,Kafka 的主題設計天然就滿足這兩個條件,因此kafka通過內置的_consumer_offset何暇,用于Offset數(shù)據(jù)的提交(由于ZK并不適合大數(shù)據(jù)量寫陶夜,所以改為topic保存)。
如果位移主題是 Kafka 自動創(chuàng)建的裆站,那么該主題的分區(qū)數(shù)是 50条辟,副本數(shù)是 3。并通過Math.abs(“groupid”.hashCode())%groupMetadataTopicPartitionCount 定位到分區(qū)位置宏胯。
隨著消費者的不斷消費羽嫡,會向位移主題中不斷寫入消息,但是顯然 Kafka 只需要保留這類消息中的最新一條就可以了肩袍,之前的消息都是可以刪除的杭棵。這就要求 Kafka 必須要有針對位移主題消息特點的消息刪除策略,否則這種消息會越來越多氛赐,最終撐爆整個磁盤魂爪。
Kafka 使用Compact 策略來刪除位移主題中的過期消息先舷,避免該主題無限期膨脹。那么應該如何定義 Compact 策略中的過期呢滓侍?對于同一個 Key 的兩條消息 M1 和 M2蒋川,如果 M1 的發(fā)送時間早于 M2,那么 M1 就是過期消息撩笆。Kafka 提供了專門的后臺線程定期地巡檢待 Compact 的主題捺球,看看是否存在滿足條件的可刪除數(shù)據(jù)浇衬。