Confluent聯(lián)合創(chuàng)始人兼CEO Jay Kreps發(fā)表了一篇博文,給出了Kafka的真正定位——它不只是個消息系統(tǒng),它還是個存儲系統(tǒng),而它的終極目標是要讓流式處理成為現(xiàn)代企業(yè)的主流開發(fā)范式。以下內(nèi)容翻譯自作者的博文,查看原文It’s Okay To Store Data In Apache Kafka。
人們總是問是否可以把Kafka作為長期的數(shù)據(jù)存儲來使用,很顯然,如果把數(shù)據(jù)保留策略設置為“永久”或者啟用主題的日志壓縮功能,那么數(shù)據(jù)就可以被永久保存下來。但我覺得人們其實真正想知道的是,這樣做是不是很瘋狂。
簡而言之,這樣做不算瘋狂。實際上,人們一直都在這么做,而且Kafka的設計意圖之一就是要將它作為數(shù)據(jù)存儲系統(tǒng)。不過問題是,為什么我們要把Kafka作為數(shù)據(jù)存儲呢?
你可能在構(gòu)建一個基于事件溯源的應用程序,需要一個數(shù)據(jù)存儲來保存變更日志。理論上,你可以使用任何一種存儲系統(tǒng)。Kafka已經(jīng)解決了不可變(immutable)日志和基于這些日志生成“物化視圖”的問題,既然這樣,為什么不直接使用Kafka呢?紐約時報已經(jīng)在他們的CMS系統(tǒng)里使用Kafka來保存他們的文章。你可能在應用程序里使用了緩存,并從Kafka上獲取數(shù)據(jù)來更新緩存。你可以將Kafka的主題設置為壓縮型日志,應用程序每次在重啟時就可以從零偏移量位置重新刷新緩存。你的流式作業(yè)數(shù)據(jù)流來自Kafka,在流式作業(yè)的邏輯發(fā)生變更后,需要重新計算結(jié)果。最簡單的辦法就是將偏移量重置為零,讓新代碼重新計算結(jié)果。Kafka經(jīng)常被用于捕獲和分發(fā)數(shù)據(jù)庫的變更事件(通常被稱為CDC,Change Data Capture)。應用程序可能只需要最新的數(shù)據(jù)庫變更,但卻要處理完整的數(shù)據(jù)快照,而這是相當耗時的操作。如果啟用主題的日志壓縮功能,就可以讓應用程序直接從零偏移量位置重新加載數(shù)據(jù)。像這樣在Kafka里存儲數(shù)據(jù)并不是什么瘋狂事,Kafka本來就是設計用來存儲數(shù)據(jù)的。數(shù)據(jù)經(jīng)過校驗后被持久化在磁盤上,并通過復制副本提升容錯能力。再多的數(shù)據(jù)都不會拖慢Kafka,在生產(chǎn)環(huán)境中,有些Kafka集群甚至已經(jīng)保存超過1 TB的數(shù)據(jù)。
那么人們?yōu)槭裁磿κ褂肒afka來存儲數(shù)據(jù)心存疑問呢?
我想,人們更多的是把Kafka當成了消息隊列系統(tǒng)。消息隊列有一些不成文的規(guī)則,比如“不要在消息隊列里保存消息”。傳統(tǒng)的消息系統(tǒng)之所以不能用來保存消息,是因為:
消息被讀取后就會被刪除伸縮性差缺乏健壯的復制機制(如果broker崩潰,數(shù)據(jù)也就丟失了)傳統(tǒng)的消息系統(tǒng)在設計上存在很多不足。從根本上講,任何一個異步消息系統(tǒng)都會保存消息,只是時間很短,有時候只有幾秒鐘,直到消息被消費為止。假設有一個服務向消息隊列發(fā)送消息,并希望有一種機制可以保證其他服務能夠收到這個消息,那么消息就需要被保存在某個地方,直到其他服務讀取它。如果消息系統(tǒng)不擅長存儲消息,也就談不上給消息“排隊”了。你可能覺得無所謂,因為你并不打算長時間地保留消息。但不管怎樣,如果消息系統(tǒng)持續(xù)地處理負載,總會有一些未被消費的消息需要保存下來。一旦消息系統(tǒng)發(fā)生崩潰,如果沒有有效的容錯存儲機制,數(shù)據(jù)就會丟失。消息存儲是消息系統(tǒng)的基礎(chǔ),但人們總是忽略這一點。
實際上,Kafka并非傳統(tǒng)意義上的消息隊列,它與RabbitMQ等消息系統(tǒng)并不一樣。它更像是一個分布式的文件系統(tǒng)或數(shù)據(jù)庫。Kafka與傳統(tǒng)消息系統(tǒng)之間有三個關(guān)鍵區(qū)別。
Kafka持久化日志,這些日志可以被重復讀取和無限期保留Kafka是一個分布式系統(tǒng):它以集群的方式運行,可以靈活伸縮,在內(nèi)部通過復制數(shù)據(jù)提升容錯能力和高可用性Kafka支持實時的流式處理以上三點足以將Kafka與傳統(tǒng)的消息隊列區(qū)別開,我們甚至可以把它看成是流式處理平臺。
我們可以這樣來看待消息系統(tǒng)、存儲系統(tǒng)和Kafka之間的關(guān)系。消息系統(tǒng)傳播的是“未來”的消息:你連接到broker上,并等待新消息的到來。存儲系統(tǒng)保存的是過去寫入的數(shù)據(jù):你查詢或讀取的結(jié)果是基于過去所做的更新。而流式處理可以把這二者結(jié)合起來,既可以處理過去的數(shù)據(jù),也可以處理未來的消息。這也就是為什么Kafka的核心就是一個持續(xù)的、基于時間排序的日志。它是一種結(jié)構(gòu)化的“文件”,而且從邏輯上看,它沒有終點,會一直持續(xù)下去。應用程序不需要區(qū)分已有的舊數(shù)據(jù)和即將生成的新數(shù)據(jù),它們都存在于一條持續(xù)的流中。Kafka提供了統(tǒng)一的協(xié)議和API來保存過去的數(shù)據(jù)和傳播未來的消息,Kafka因此成為一種非常好的流式處理平臺。
日志就像是分布式文件系統(tǒng)中的一個文件,在這個系統(tǒng)里,日志被復制到多臺機器上,被持久化到磁盤,并支持高吞吐的線性讀取和寫入。當然,日志也像是一個消息系統(tǒng),支持高吞吐的并發(fā)寫入和低延遲的多消費者。
從實現(xiàn)方面來看,日志非常適合用來作為數(shù)據(jù)存儲。Kafka本身就是使用復制日志作為存儲,所以你也不例外!在Kafka內(nèi)部,偏移量被保存在一個壓縮主題上,Kafka Streams API使用壓縮主題來記錄應用程序的處理狀態(tài)。
當然,把Kafka作為存儲系統(tǒng)來用并不會給你帶來新的門檻。存儲系統(tǒng)包攬了正確性、運行時間和數(shù)據(jù)完整性等方面的工作。如果一個系統(tǒng)成為數(shù)據(jù)的標準來源,人們就會對它的正確性和運維標準提出很高的要求。我們花了大量的精力在提升Kafka的正確性上,我們每天在數(shù)百臺機器上運行數(shù)個小時的分布式測試以及數(shù)千個常規(guī)性的單元測試,但我們覺得還有很多事情要做。除了測試之外,我們還需要知道如何做好運維工作,以及了解系統(tǒng)的局限性。
有時候,人們也會問我,這是不是就意味著Kafka可以取代其他存儲引擎。答案當然是否定的。
首先,數(shù)據(jù)庫提供大量的查詢,而Kafka并不打算在日志上增加隨機訪問的特性。Kafka保存數(shù)據(jù)可以被復制到其他數(shù)據(jù)庫、緩存、流式處理器、搜索引擎、圖存儲引擎和數(shù)據(jù)胡(data lake)上,這些存儲引擎都各自的優(yōu)缺點,我們也無法做出一個可以打敗其他所有引擎的系統(tǒng)。
如果說Kafka并不想取代這些系統(tǒng),那它存在的意義是什么?你可以把數(shù)據(jù)中心看成是一個大型的數(shù)據(jù)庫,Kafka是這個系統(tǒng)里的提交日志,而其他存儲引擎則是索引或視圖。Kafka是構(gòu)建數(shù)據(jù)庫的基礎(chǔ),至于查詢方面的工作可以交給索引和視圖。
Kafka Streams API提供了交互式的查詢功能?;贙afka Streams開發(fā)的應用就是一個Kafka消費者,只不過它們可以維護計算狀態(tài),而且這些狀態(tài)可以直接保存到外部的存儲系統(tǒng),這種物化視圖讓Kafka具備了低延遲的查詢能力。Kafka集群保存日志,Streams API保存物化視圖并處理查詢請求。后來我們引入了KSQL——Kafka的流式SQL引擎。有了KSQL,用戶可以直接使用SQL語句從Kafka上獲得物化視圖。
我們不打算為Kafka提供查詢API的另一個原因是因為我們有其他更重要的事情要做。我們希望流式處理成為主流的開發(fā)模式,讓流式平臺成為現(xiàn)代數(shù)字業(yè)務的中心系統(tǒng)。我們希望能夠達成這個讓人激動不已的目標,而不只是創(chuàng)建一種新的數(shù)據(jù)庫系統(tǒng)。我們相信,在現(xiàn)代企業(yè)里,流式平臺將會成為移動和處理數(shù)據(jù)的黑馬。要實現(xiàn)這個目標,我們還有很多事情要做。