原文地址:https://kafka.apache.org/0101/documentation.html#persistence
不要對文件系統(tǒng)感到恐懼
kafka 嚴(yán)重依賴于底層的文件系統(tǒng)來保存和緩存消息記錄缔杉。一種普遍的觀念是磁盤很慢锤躁,大家都會(huì)懷疑kafka 的存儲(chǔ)結(jié)構(gòu)是否能提供有競爭力的存儲(chǔ)性能。實(shí)際上磁盤比人們現(xiàn)象中的還快或详,這就看你怎么用了系羞。一個(gè)合理設(shè)計(jì)的磁盤存儲(chǔ)結(jié)構(gòu),往往可以和網(wǎng)絡(luò)一樣快霸琴。
關(guān)于磁盤性能的關(guān)鍵事實(shí)是椒振,硬盤驅(qū)動(dòng)器的吞吐量在過去十年時(shí),磁道的尋址延遲就已經(jīng)達(dá)到了極限了梧乘。使用 jaod 方式配置6個(gè)7200rpm SATA RAID-5 組的磁盤陣列大概是 600MB/sec澎迎,但是隨機(jī)寫性能只有100k/sec,差距是6000X萬倍选调,線性讀寫在使用上是最容易預(yù)測的方式夹供,所以大部分操作系統(tǒng)都對這方面做了很多優(yōu)化措施。現(xiàn)在的操作系統(tǒng)仁堪,都有提前讀和緩存寫的技術(shù)哮洽,從大的數(shù)據(jù)塊中批量讀取數(shù)據(jù),并匯總小的邏輯寫請求后弦聂,使用一次大的物理寫請求代替鸟辅。更多關(guān)于這方面的研究可以查看 ACM Queue article 這里,它指出這樣的一個(gè)事實(shí)莺葫,順序?qū)懺谀承┣闆r下比隨機(jī)的內(nèi)存讀取還要快剔桨。
為了彌補(bǔ)這種性能上的差距,現(xiàn)代操作系統(tǒng)更多使用內(nèi)存來為磁盤做緩存♂闳冢現(xiàn)代的操作系統(tǒng)更樂意使用所有的空閑內(nèi)存為磁盤做緩存洒缀,在內(nèi)存的回收上只需要花費(fèi)極小的代價(jià)。所有的磁盤讀寫都通過統(tǒng)一的緩存。如果沒有使用直接 I/O 這個(gè)開關(guān)树绩,這種特性不會(huì)很容易被屏蔽掉萨脑。因此,即使一個(gè)進(jìn)程內(nèi)部獨(dú)立維持一個(gè)數(shù)據(jù)緩存饺饭,那么數(shù)據(jù)也有可能在系統(tǒng)頁中再被緩存一次渤早,所有的數(shù)據(jù)都會(huì)被存儲(chǔ)兩次。
此外瘫俊,我們基于 jvm 上面構(gòu)建應(yīng)用鹊杖,有花費(fèi)時(shí)間在 java 內(nèi)存上的人都知道兩件事:
- 1.內(nèi)存中存有大量的對象需要消耗很高,經(jīng)常是雙倍于存儲(chǔ)到磁盤時(shí)大小(可能更多)
- 2.java的垃圾收集器在內(nèi)存數(shù)據(jù)增加時(shí)變得既煩瑣又慢
考慮到這些因素扛芽,使用文件系統(tǒng)并使用頁緩存機(jī)制比自己去進(jìn)行內(nèi)存緩存或使用其他存儲(chǔ)結(jié)構(gòu)更為有效——我們訪問內(nèi)存的時(shí)候已經(jīng)起碼至少訪問了兩次緩存骂蓖,很有可能在寫字節(jié)的時(shí)候也是兩次存儲(chǔ)而非單次。這樣做的話川尖,在一個(gè)緩存達(dá)到32GB的機(jī)器上登下,可以減少GC的代價(jià),這樣也可以減少代碼在維護(hù)緩存和系統(tǒng)文件間的一致性叮喳,比再嘗試新的方法有更高的正確性被芳。如果你對磁盤的使用充分利用到線性讀,那么預(yù)取機(jī)制將會(huì)很有效的在每次磁盤讀取時(shí)實(shí)現(xiàn)填充好緩存空間馍悟。
這意味設(shè)計(jì)非常簡單畔濒,系統(tǒng)不是更多把數(shù)據(jù)保存到內(nèi)存空間,在內(nèi)存空間耗盡時(shí)才趕緊寫入到文件系統(tǒng)中锣咒,相反的篓冲,所有的數(shù)據(jù)都被馬上寫入到文件系統(tǒng)的日志文件中,但沒有必要馬上進(jìn)行 flush 磁盤操作宠哄。只是把數(shù)據(jù)傳輸?shù)较到y(tǒng)內(nèi)核的頁面空間中去了。
這種頁面緩存風(fēng)格設(shè)計(jì)可以參考這里 : article
常量耗時(shí)需求
在消息系統(tǒng)中嗤攻,大部分持久化的數(shù)據(jù)結(jié)構(gòu)通常使用一個(gè)消費(fèi)者隊(duì)列一個(gè) btree 結(jié)構(gòu)毛嫉,或其他隨機(jī)讀取的數(shù)據(jù)結(jié)構(gòu)用于維持消息的元數(shù)據(jù)信息。btree 結(jié)構(gòu)是最通用的數(shù)據(jù)結(jié)構(gòu)類型妇菱,它在消息系統(tǒng)中承粤,能夠支持廣泛的事物或非事物的語義。雖然 btree 操作的代價(jià)是 O(log N)闯团,但是實(shí)際使用時(shí)消耗的代價(jià)卻很高辛臊。通常O(log N) 被認(rèn)為是消耗常量時(shí)間,但是這個(gè)對硬盤操作卻不是這樣房交,硬盤尋址需要使用10ms的耗時(shí)彻舰,每次請求只能做一次硬盤尋址,不能并發(fā)執(zhí)行。所以即使少數(shù)的幾次硬盤尋址也會(huì)有很高的負(fù)載刃唤,因?yàn)榇鎯?chǔ)系統(tǒng)混合和快速緩存操作和慢速的物理磁盤操作隔心,btree 樹的性能一般逼近與緩存到硬盤里面的數(shù)據(jù)大小,當(dāng)數(shù)據(jù)量加倍時(shí)尚胞,效率可能下降一半或更慢硬霍。
直觀地,一個(gè)持久化隊(duì)列可以使用簡單的讀和追加數(shù)據(jù)到文件的日志方式進(jìn)行實(shí)現(xiàn)笼裳,這種結(jié)構(gòu)有一個(gè)好處是唯卖,所有操作都是O(1)性能的,而且讀和寫入數(shù)據(jù)不會(huì)相互阻塞躬柬,這樣性能和數(shù)據(jù)的大小完全無關(guān)拜轨,一臺(tái)服務(wù)器可以完全充分利用了廉價(jià)、低速的1+TB SATA 硬盤楔脯,雖然它們的尋道性能不高撩轰,但是他們以3分之一的價(jià)格和3倍的容量接受大量的讀寫請求。
能夠以微小地性能代價(jià)存取數(shù)據(jù)到無限的硬盤中昧廷,這意味著我們可以提供一些其他消息系統(tǒng)沒有的特性堪嫂。例如,,在kafka中木柬,不需要在消費(fèi)者消費(fèi)了數(shù)據(jù)后馬上把消息從隊(duì)列中刪除掉皆串,相反的我們可以保留一段很長的時(shí)間,例如一個(gè)禮拜眉枕。這對消費(fèi)者來說提供了很大的靈活性恶复,下面我們就會(huì)講到。