原文地址:https://kafka.apache.org/0101/documentation.html#compaction
日志壓縮確保Kafka會(huì)為一個(gè)Topic分區(qū)數(shù)據(jù)日志中保留至少message key的最后一個(gè)值趁冈。它解決了應(yīng)用crash或系統(tǒng)故障或應(yīng)用在操作期間重啟來重新加載緩存的場(chǎng)景喊括。讓我們深入到細(xì)節(jié)中解釋日志壓縮是如何工作的乾蓬。
到目前為止,我們只說明了在一斷時(shí)間或達(dá)到特定大小的時(shí)候丟棄舊日志的簡(jiǎn)單方法耳璧。這適用于像日志這樣每一條數(shù)據(jù)都是獨(dú)立數(shù)據(jù)的情況囱挑。但是重要類別的數(shù)據(jù)是根據(jù)key處理的數(shù)據(jù)(例如DB中表的變更數(shù)據(jù))凌唬。
讓我們來討論這樣一個(gè)具體的流的例子赁还。一個(gè)Topic包含了用戶email address信息妖泄;每一次用戶變更郵箱地址,我們都像這個(gè)topic發(fā)送一條消息艘策,使用用戶ID作為primay key〉负現(xiàn)在我們已經(jīng)為用戶ID為123的用戶發(fā)送了一些消息,每條消息包含了email address的變更:
123 => bill@microsoft.com
.
.
.
123 => bill@gatesfoundation.org
.
.
.
123 => bill@gmail.com
日志壓縮為我們提供了更精細(xì)的保留機(jī)制朋蔫,至少保存每個(gè)key最后一個(gè)變更(如123 => bill@gmail.com)罚渐。這樣做我們確保了這個(gè)日志包含了所有key最后一個(gè)值的快照。這樣Consumer可以重建狀態(tài)而不需要保留完成的變更日志驯妄。
讓我們列一些日志壓縮有用的場(chǎng)景荷并,然后看他是如何被使用的。
- 1.DB變更訂閱青扔。這是很常見的源织,一個(gè)數(shù)據(jù)在多個(gè)數(shù)據(jù)系統(tǒng)中,而且其中一個(gè)系統(tǒng)是數(shù)據(jù)庫類型的(如RDBMS或KV系統(tǒng))微猖。例如可能有一個(gè)數(shù)據(jù)庫谈息,一個(gè)戶緩存系統(tǒng)豹障,一個(gè)搜索集群噪径,一個(gè)Hadoop集群陶耍。DB的任何一個(gè)變更需要反映到緩存烙懦、搜索集群吕世,最終保存到Hadoop中贤旷。在這個(gè)場(chǎng)景中亲澡,你只需要實(shí)時(shí)系統(tǒng)最新的更新日志疼阔。但是如果需要重新加載緩存或恢復(fù)宕機(jī)的檢索節(jié)點(diǎn)盲憎,就需要完整的數(shù)據(jù)嗅骄。
- 2.事件源。這是一種應(yīng)用設(shè)計(jì)風(fēng)格饼疙,它將查詢處理和應(yīng)用程序設(shè)計(jì)結(jié)合到一起溺森,并使用日志作為程序的主要存儲(chǔ)。
- 3.高可用日志窑眯。一個(gè)本地集成程序可以通過變更日志來做到容錯(cuò)屏积,這樣另一個(gè)程序能夠在當(dāng)前程序故障時(shí)繼續(xù)處理。例如磅甩,像流數(shù)據(jù)查詢例子炊林,如計(jì)數(shù),匯總或其他的分組操作卷要。實(shí)時(shí)系統(tǒng)框架如Samza渣聚,就是為了達(dá)到這個(gè)目的使用這個(gè)特性的独榴。
在這些場(chǎng)景中,主要處理實(shí)時(shí)的變更奕枝,但有時(shí)需要重新加載或重新處理時(shí)棺榔,需要加載所有數(shù)據(jù)。日志壓縮允許使用相同的Topic來支持這些場(chǎng)景隘道,這種日志使用風(fēng)格在后續(xù)的內(nèi)容中會(huì)更詳細(xì)的描述症歇。
想法很簡(jiǎn)單,我們有無限的日志谭梗,以上每種情況記錄變更日志忘晤,我們從一開始就捕獲每一次變更。使用這個(gè)完整的日志激捏,我們可以通過回放日志來恢復(fù)到任何一個(gè)時(shí)間點(diǎn)的狀態(tài)德频。這種假設(shè)的情況下,完整的日志是不實(shí)際的缩幸,對(duì)于那些每一行記錄會(huì)變更多次的系統(tǒng),即使數(shù)據(jù)集很小竞思,日志也會(huì)無限的增長下去表谊。丟棄舊日志的簡(jiǎn)單操作可以限制空間的增長,但是無法重建狀態(tài)——因?yàn)榕f的日志被丟棄盖喷,可能一部分記錄的狀態(tài)會(huì)無法重建(這寫記錄所有的狀態(tài)變更都在就日志中)爆办。
日志壓縮機(jī)制是更細(xì)粒度的,每個(gè)記錄都保留的機(jī)制课梳,而不是基于時(shí)間的粗粒度距辆。這個(gè)想法是選擇性的刪除哪些有更新的變更的記錄的日志。這樣最終日志至少包含每個(gè)key的記錄的最后一個(gè)狀態(tài)暮刃。
這個(gè)策略可以為每個(gè)Topic設(shè)置跨算,這樣一個(gè)集群中,可以一部分Topic通過時(shí)間和大小保留日志椭懊,另外一些可以通過壓縮保留诸蚕。
這個(gè)功能的靈感來自于LinkedIn的最古老且最成功的基礎(chǔ)設(shè)置——一個(gè)稱為Databus的數(shù)據(jù)庫變更日志緩存系統(tǒng)。不想大多數(shù)的日志存儲(chǔ)系統(tǒng)氧猬,Kafka為了訂閱而量身打造背犯,用于線性的快速讀寫。和Databus不同盅抚,Kafka作為真實(shí)的存儲(chǔ)漠魏,壓縮日志是非常有用的,在上游數(shù)據(jù)源不能重放的情況下妄均。
Log Compaction Basics
這里是一個(gè)展示Kafka日志的邏輯結(jié)構(gòu)的圖(每條消息包含了一個(gè)offset):
Log head中包含傳統(tǒng)的Kafka日志柱锹。它包含了連續(xù)的offset和所有的消息哪自。日志壓縮增加了處理tail Log的選項(xiàng)。上圖展示了日志壓縮的的Log tail的情況奕纫。tail中的消息保存了初次寫入時(shí)的offset提陶。即使該offset的消息被壓縮,所有offset仍然在日志中是有效的匹层。在這個(gè)場(chǎng)景中隙笆,無法區(qū)分和下一個(gè)出現(xiàn)的更高offset的位置。如上面的例子中升筏,36撑柔、37、38是屬于相同位置的您访,從他們開始讀取日志都將從38開始铅忿。
壓縮允許刪除。一條消息伴隨著空的值被認(rèn)為從日志中刪除灵汪。這個(gè)刪除標(biāo)記將會(huì)引起所有之前擁有相同key的消息被移除(包括擁有key相同的新消息)檀训,但是刪除標(biāo)記比較特殊,它將在一定周期后被從日志中刪除來釋放空間享言。這個(gè)時(shí)間點(diǎn)被稱為“delete retention point”峻凫。
壓縮操作通過在后臺(tái)周期性的拷貝日志段來完成。清除操作不會(huì)阻塞讀取览露,并且可以被配置不超過一定IO吞吐來避免影響Producer和Consumer荧琼。實(shí)際的日志段壓縮過程有點(diǎn)像如下:
What guarantees does log compaction provide?
日志壓縮提供了如下的保證:
- 1.所有跟上消費(fèi)的Consumer能消費(fèi)到所有寫入的消息;這些消息有連續(xù)的序列號(hào)差牛。Topic的min.compaction.lag.ms可以用于保證消息寫入多久后才會(huì)被壓縮命锄。這限制了一條消息在Log Head中的最短存在時(shí)間。
- 2.消息的順序會(huì)被保留偏化。壓縮不會(huì)重排序消息脐恩,只是移除其中一部分。
- 3.消息的Offset不會(huì)變更夹孔。這是消息在日志中的永久標(biāo)志被盈。
- 4.任何從頭開始處理日志的Consumer至少會(huì)拿到每個(gè)key的最終狀態(tài)。另外搭伤,只要Consumer在小于Topic的delete.retention.ms設(shè)置(默認(rèn)24小時(shí))的時(shí)間段內(nèi)到達(dá)Log head只怎,將會(huì)看到所有刪除記錄的所有刪除標(biāo)記。換句話說怜俐,因?yàn)橐瞥齽h除標(biāo)記和讀取是同事發(fā)生的身堡,Consumer可能會(huì)因?yàn)槁浜蟪^delete.retention.ms而導(dǎo)致錯(cuò)過刪除標(biāo)記。
Log Compaction Details
日志壓縮由Log Cleaner執(zhí)行拍鲤,后臺(tái)線程池重新拷貝日志段贴谎,移除那些key存在于Log Head中的記錄汞扎。每個(gè)壓縮線程如下工作:
- 1.選擇Log Head相對(duì)于Log Head在日志中占更高比例的日志
- 2.創(chuàng)建Log Head中每個(gè)Key最后一個(gè)offset的摘要
- 3.從頭到尾的拷貝日志,并刪除之后日志終于到相同key的記錄擅这。新的澈魄、干凈的日志將會(huì)立即被交到到日志中,所以只需要一個(gè)額外的日志段空間
- 4.Log Head的摘要實(shí)際上是一個(gè)空間緊湊的哈希表仲翎。每個(gè)條目使用24個(gè)字節(jié)痹扇。所以如果有8G的整理緩沖區(qū), 則能迭代處理大約366G的日志頭部(假設(shè)消息大小為1k)。
Configuring The Log Cleaner
Log Cleaner默認(rèn)啟用溯香。這會(huì)啟動(dòng)清理的線程池鲫构。如果要開始特定Topic的清理功能,可以開啟特定的屬性:
log.cleanup.policy=compact
這個(gè)可以通過創(chuàng)建Topic時(shí)配置或者之后使用Topic命令實(shí)現(xiàn)玫坛。
Log Cleaner可以配置保留最小的不壓縮的日志頭结笨。可以通過配置壓縮的延遲時(shí)間:
log.cleaner.min.compaction.lag.ms
這可以用于保證消息比在被壓縮的消息大一段時(shí)間湿镀。如果沒有設(shè)置炕吸,除了最后一個(gè)日志外,所有的日志都會(huì)被壓縮勉痴。當(dāng)前寫入的自如端不會(huì)被壓縮算途,即使所有的消息都落后于比配置的最小壓縮時(shí)間。
更多的配置在這里