前言
這是一篇學(xué)習(xí)筆記。學(xué)習(xí)的材料來自Jay Kreps的一篇講Log的博文。原文很長,但是我堅(jiān)持看完了,收獲頗多,也深深為Jay哥的技術(shù)能力、架構(gòu)能力和對于分布式系統(tǒng)的理解之深刻所折服。同時(shí)也因?yàn)槟承├斫夂蚃ay哥觀點(diǎn)吻合而略沾沾自喜。
Jay Kreps是前Linkedin的Principal Staff Engineer,現(xiàn)任Confluent公司的聯(lián)合創(chuàng)始人和CEO,Kafka和Samza的主要作者。
所謂筆記,就是看了文章,提筆就記,因?yàn)镴ay哥本身本章組織的太好,而其本身的科學(xué)素養(yǎng)及哲學(xué)素養(yǎng)也很高,所以私以為出彩的東西就不省略了。
一、資料來源
The Log: What every software engineer should know about real-time data’s unifying abstraction
二、筆記
2.1 Log的價(jià)值
1) Log是如下系統(tǒng)的核心:
分布式圖數(shù)據(jù)庫
分布式搜索引擎
Hadoop
第一代和第二代K-V數(shù)據(jù)庫
2) Log可能跟計(jì)算機(jī)的歷史一樣長,并且是分布式數(shù)據(jù)系統(tǒng)和實(shí)時(shí)計(jì)算系統(tǒng)的核心。
3) Log的名字很多:
Commit log
Transaction log
Write-ahead log
4) 不理解Log,你就不可能充分理解
數(shù)據(jù)庫
NoSQL存儲
K-V存儲
復(fù)制
Paxos算法
Hadoop
Version Control
或者,任何軟件系統(tǒng)
2.2 什么是Log?
2.2.1 概述
記錄會附加到log的尾部。
從左到右讀取記錄。
每個(gè)entry都有唯一且有序的log entry 序號。
記錄的順序定義了這樣的一個(gè)概念:時(shí)間。
因?yàn)樵娇孔蟮挠涗浽皆纭?/p>
Entry的序號可以當(dāng)作一種時(shí)間戳,將記錄的順序當(dāng)作時(shí)間這一概念看起來很奇怪,但是很快你就會發(fā)現(xiàn),這樣做:可以方便地將“時(shí)間”與任一特定的物理時(shí)鐘解耦。
Log和常見的文件、表(table)沒有那么大的差別。
文件是一組字節(jié)
表是一組記錄
Log可以說是某種將記錄按時(shí)間排序的文件或者表
這樣說,可能你會覺得log如此簡單,還有討論的必要嗎?
其實(shí),log的核心意義在于:
Log記錄了何時(shí)發(fā)生了什么(they record what happened and when.)。
而這一條,通常是分布式系統(tǒng)最最最核心的東西。
注意,這里有必要澄清幾個(gè)概念:
本篇所討論的Log和程序員通常接觸的應(yīng)用日志(application logs)不同
應(yīng)用日志通常是一種非結(jié)構(gòu)化的,記錄錯(cuò)誤信息、調(diào)試信息,用于追蹤應(yīng)用的運(yùn)行的,給人看的日志,比如通過log4j或者 syslog來寫入本地文件的日志。
而本篇所討論的log是通過編程方式訪問的,不是給人看的,比如“journal”、“data logs”。
應(yīng)用日志是本篇所討論的log的一種特化。
2.2.2 數(shù)據(jù)庫中的Logs
Log的起源不得而知,就像發(fā)明二分查找的人,難以意識到這種發(fā)明是一種發(fā)明。
Log的出現(xiàn)和IBM的System R 一樣早。
在數(shù)據(jù)庫中,需要在數(shù)據(jù)庫崩潰時(shí),保持多種多樣的數(shù)據(jù)結(jié)構(gòu)和索引保持同步。
為保證原子性和持久性,數(shù)據(jù)庫需要在對數(shù)據(jù)結(jié)構(gòu)和索引進(jìn)行修改提交之前,記錄其要修改的內(nèi)容。
所以log記錄了何時(shí)發(fā)生了什么,而每一張表和索引本身,都是這種歷史信息的映射。
因?yàn)閘og是立即持久化的,所以當(dāng)crash發(fā)生時(shí),其成為恢復(fù)其它持久化結(jié)構(gòu)的可靠來源。
Log從保證ACID特性的一種實(shí)現(xiàn),發(fā)展成了一種數(shù)據(jù)庫之間數(shù)據(jù)復(fù)制的手段。
很顯然,數(shù)據(jù)庫中發(fā)生的一系列的數(shù)據(jù)變更,成為數(shù)據(jù)庫之間 保持同步最需要的信息。
Oracle、MySQL、PostgreSQL,都包含了log傳輸協(xié)議,將log的一部分發(fā)送到用于保持復(fù)制的從數(shù)據(jù)庫(Slave)。
Oracle的XStreams和GoldenState,將log當(dāng)作一種通用的數(shù)據(jù)訂閱機(jī)制,以提供給非Oracle的數(shù)據(jù)庫訂閱數(shù)據(jù)。
MySQL和PostgreSQL也提供了類似的組件,這些組件是數(shù)據(jù)系統(tǒng)架構(gòu)的核心。
面向機(jī)器的Log,不僅僅可被用在數(shù)據(jù)庫中,也可以用在:
消息系統(tǒng)
數(shù)據(jù)流(data flow)
實(shí)時(shí)計(jì)算
2.2.3 分布式系統(tǒng)中的logs
Log解決了兩個(gè)很重要的分布式數(shù)據(jù)系統(tǒng)中的問題:
1) 有序的數(shù)據(jù)變化
2) 數(shù)據(jù)分布式化
所謂的狀態(tài)機(jī)復(fù)制原理(State Machine Replication Principle):
如果兩個(gè)確定的處理過程,從相同的狀態(tài)開始,按照相同的順序,接收相同的輸入,那么它們將會產(chǎn)生相同的輸出,并以 相同的狀態(tài)結(jié)束。
所謂確定的(deterministic),是指處理過程是時(shí)間無關(guān)的,其處理結(jié)果亦不受額外輸入的影響。
可以通過非確定的例子來理解:
多線程的執(zhí)行順序不同導(dǎo)致不同的結(jié)果
執(zhí)行g(shù)etTimeOfDay()方法
其它的不能重復(fù)的處理過程
所謂狀態(tài),可以是機(jī)器上的任意數(shù)據(jù),無論在處理結(jié)束后,是在機(jī)器的內(nèi)存中還是磁盤上。
相同的輸入按照相同的順序,產(chǎn)生相同的結(jié)果,這一點(diǎn)值得引起你的注意,這也是為什么log會如此重要,這是一個(gè)直覺性的概念:如果你將同一個(gè)log輸入兩個(gè)確定性的程序,它們將產(chǎn)生相同的輸出。
在分布式系統(tǒng)的構(gòu)建中,意識到這一點(diǎn),可以使得:
讓所有的機(jī)器做同樣的事,規(guī)約為:
構(gòu)建分布式的、滿足一致性的log系統(tǒng),以為所有處理系統(tǒng)提供輸入。
Log系統(tǒng)的作用,就是將所有的輸入流之上的不確定性驅(qū)散,確保所有的處理相同輸入的復(fù)制節(jié)點(diǎn)保持同步。
這種方法的最妙之處在于,你可以將索引日志的時(shí)間戳,作為所有復(fù)制節(jié)點(diǎn)的時(shí)鐘來對待:
通過將復(fù)制節(jié)點(diǎn)所處理過的log中最大的時(shí)間戳,作為復(fù)制節(jié)點(diǎn)的唯一ID,這樣,時(shí)間戳結(jié)合log,就可以唯一地表達(dá)此節(jié)點(diǎn)的整個(gè)狀態(tài)。
應(yīng)用這種方法的方式也很多:
在log中記錄對一個(gè)服務(wù)的請求
在回復(fù)請求的前后,記錄服務(wù)狀態(tài)的變化
或者,服務(wù)所執(zhí)行的一系列轉(zhuǎn)換命令,等等。
理論上來講,我們可以記錄一系列的機(jī)器指令,或者所調(diào)用方法的名稱及參數(shù),只要數(shù)據(jù)處理進(jìn)程的行為相同,這些進(jìn)程就可以保證跨節(jié)點(diǎn)的一致性。
常玩兒數(shù)據(jù)庫的人,會將邏輯日志和物理日志區(qū)分對待:
物理日志:記錄了所有的行內(nèi)容的變化。
邏輯日志:不是記錄內(nèi)容的變化,而是Insert , update , delete等導(dǎo)致行內(nèi)容變化的SQL語句。
對分布式系統(tǒng),通常有兩種方式來處理復(fù)制和數(shù)據(jù)處理:
1) State machine model(active – active)
2) Primary-back model (active – passive)
如下圖所示:
為了理解上述兩種方式的不同,來看個(gè)簡單的例子:
現(xiàn)在,集群需要提供一個(gè)簡單的服務(wù),來做加法、乘法等算術(shù)運(yùn)算。初始,維護(hù)一個(gè)數(shù)字,比如0。
Active – active :在日志記錄這樣的一些操作,如“+1”、“*2”等,這樣,每個(gè)復(fù)制節(jié)點(diǎn)需要執(zhí)行這些操作,以保證最后的數(shù)據(jù)狀態(tài)是一致的。
Active – passive:一個(gè)單獨(dú)的master節(jié)點(diǎn),執(zhí)行“+1”、“*2”等操作,并且在日志中記錄操作的結(jié)果,如“1”、“3”、“6”等。
上面的例子也揭示了,為什么順序是復(fù)制節(jié)點(diǎn)之間保持一致性的關(guān)鍵因素,如果打亂了這些操作的順序,就會得到不同的運(yùn)算結(jié)果。
分布式log,可以當(dāng)做某些一致性算法的數(shù)據(jù)結(jié)構(gòu):
Paxos
ZAB
RAFT
Viewstamped Replication
一條log,表征了一系列的關(guān)于下一個(gè)值是什么的決定。
2.2.4 Changelog
從數(shù)據(jù)庫的角度來看,一組記錄數(shù)據(jù)變化的changelog和表,是對偶和互通的。
1) 依據(jù)記錄了數(shù)據(jù)變化的log,可以重構(gòu)某一狀態(tài)的表(也可以是非關(guān)系型存儲系統(tǒng)中有key的記錄)
2) 相反,表如果發(fā)生了變化,可以將變化計(jì)入log。
這正是你想要的準(zhǔn)實(shí)時(shí)復(fù)制的秘籍所在!
這一點(diǎn)和版本控制所做的事情極為類似:管理分布式的、并發(fā)的、對狀態(tài)進(jìn)行的修改。
版本控制工具,維護(hù)了反映修改的補(bǔ)丁,這其實(shí)就是log,你和一個(gè)被簽出(checked out)的分支快照進(jìn)行交互,這份快照就相當(dāng)于數(shù)據(jù)庫中的表。你會發(fā)現(xiàn),版本控制與分布式系統(tǒng)中,復(fù)制都是基于log的:當(dāng)你更新版本時(shí),你只是拉取了反映了版本變化的補(bǔ)丁,并應(yīng)用于當(dāng)前的分支快照。
2.3 數(shù)據(jù)集成(Data integration)
2.3.1 數(shù)據(jù)集成的含義
所謂數(shù)據(jù)集成,就是將一個(gè)組織中的所有服務(wù)和系統(tǒng)的數(shù)據(jù),變得可用。
實(shí)際上,對數(shù)據(jù)進(jìn)行有效利用,很符合馬斯洛的層次需求理論。
金字塔的最底層,是收集數(shù)據(jù),將其整合進(jìn)應(yīng)用系統(tǒng)中(無論是實(shí)時(shí)計(jì)算引擎,還是文本文件,還是python腳本)。
而這些數(shù)據(jù),需要經(jīng)過轉(zhuǎn)換,保持一個(gè)統(tǒng)一、規(guī)范、整潔的格式,以易于被讀取和處理。
當(dāng)上面的要求被滿足后,就可以開始考慮多種多樣的數(shù)據(jù)處理方式,比如map – reduce 或者實(shí)時(shí)查詢系統(tǒng)。
很顯然,如果沒有一個(gè)可靠的、完備的數(shù)據(jù)流,Hadoop就僅僅是一個(gè)昂貴的、難以整合的加熱器(集群很費(fèi)電么?)。
相反,如果能保證數(shù)據(jù)流可靠、可用且完備,就可以考慮更高級的玩法、更好的數(shù)據(jù)模型和一致的、更易被理解的語義。
接著,注意力就可以轉(zhuǎn)移到可視化、報(bào)表、算法和預(yù)測上來(挖啊機(jī)啊深度?。?。
2.3.2 數(shù)據(jù)集成的兩個(gè)復(fù)雜性
事件
事件數(shù)據(jù),記錄了事件是怎么發(fā)生的,而不僅僅是發(fā)生了什么,這一類log通常被當(dāng)做應(yīng)用日志,因?yàn)橐话闶怯蓱?yīng)用系統(tǒng)寫入的。但這一點(diǎn),其實(shí)混淆了log的功能。
Google的財(cái)富,其實(shí),是由一個(gè)建立在(用戶)點(diǎn)擊流和好惡印象(體驗(yàn))之上的相關(guān)性pipeline產(chǎn)生的,而點(diǎn)擊流和印象,就是事件。
各種各樣的專業(yè)數(shù)據(jù)系統(tǒng)的爆發(fā)
這些系統(tǒng)存在的原因:
聯(lián)機(jī)分析(OLAP)
搜索
簡單的在線存儲
批處理
圖譜分析
等等(如spark)
顯然,要將數(shù)據(jù)整合進(jìn)這樣的系統(tǒng)中,對于數(shù)據(jù)集成來講,極為困難。
2.3.3 基于日志結(jié)構(gòu)的數(shù)據(jù)流
每種邏輯意義上的數(shù)據(jù)源,都可以依據(jù)log進(jìn)行建模。
數(shù)據(jù)源可以是記錄了事件(點(diǎn)擊和PV)的應(yīng)用程序,可以是接受更改的數(shù)據(jù)庫表。
每個(gè)訂閱者,都盡可能快地從這些數(shù)據(jù)源產(chǎn)生的log中獲取新的記錄,應(yīng)用于本地的存儲系統(tǒng),并且提升其在log中的讀取偏移(offset)。訂閱者可以是任何數(shù)據(jù)系統(tǒng),比如緩存、Hadoop、另一個(gè)站點(diǎn)的數(shù)據(jù)庫,或者搜索引擎。
Log,實(shí)際上提供了一種邏輯時(shí)鐘,針對數(shù)據(jù)變化,可以測量不同的訂閱者所處的狀態(tài),因?yàn)檫@些訂閱者在log中的讀取偏移不同且相互獨(dú)立,這種偏移就像一個(gè)時(shí)間意義上的“時(shí)刻”一樣。
考慮這樣一個(gè)例子,一個(gè)數(shù)據(jù)庫,和一些緩存服務(wù)器:
Log提供了這樣一種能力,可以使得所有的緩存服務(wù)器得到同步,并推出它們所處的“時(shí)刻”。
假設(shè)我們寫入了一個(gè)編號為X的log,要從某個(gè)緩存服務(wù)器讀取數(shù)據(jù),為了不讀到老數(shù)據(jù),只需要保證:在緩存服務(wù)器將數(shù)據(jù)(同步)復(fù)制到X這個(gè)位置前,我們不從這個(gè)緩存中讀取任何東西即可。
此外,log還提供了作為緩沖區(qū)的能力,以支持生產(chǎn)者和消費(fèi)者的行為以異步的方式進(jìn)行。
最關(guān)鍵的一個(gè)支持異步的原因,是訂閱系統(tǒng)可能會發(fā)生崩潰、因維護(hù)而下線,接著恢復(fù)上線,而在這種情況下,每個(gè)訂閱者都以自己的步調(diào)消費(fèi)數(shù)據(jù)。
一個(gè)批處理系統(tǒng),比如Hadoop,或者一個(gè)數(shù)據(jù)倉庫,是以小時(shí)或天為單位消費(fèi)數(shù)據(jù),而一個(gè)實(shí)時(shí)系統(tǒng),通常在秒級消費(fèi)數(shù)據(jù)。
而數(shù)據(jù)源或者log,對消費(fèi)數(shù)據(jù)的訂閱者一無所知,所以,需要在pipeline中做到無縫的添加訂閱者和移除訂閱者。
更重要的是,訂閱者,只需要知道log,而不需要對其所消費(fèi)的數(shù)據(jù)的來源有任何了解,無論這個(gè)數(shù)據(jù)源是RDBMS、Hadoop,還是一個(gè)最新流行的K-V數(shù)據(jù)庫,等等。
之所以討論log,而不是消息系統(tǒng),是因?yàn)椴煌南⑾到y(tǒng)所保證的特性不同,并且用消息系統(tǒng)這個(gè)詞,難以全面和精確表達(dá)某種語義,因?yàn)橄⑾到y(tǒng),更重要的在于重定向消息。
但是,可以將log理解為這樣一種消息系統(tǒng),其提供了持久性保證及強(qiáng)有序的語義,在通訊系統(tǒng)中,這稱作原子廣播。
2.4 在Linkedin
Linkedin目前的主要系統(tǒng)包括(注:2013年):
Search
Social Graph
Voldemort (K-V存儲)
Espresso (文檔存儲)
Recommendation engine
OLAP query engine
Hadoop
Terradata
Ingraphs (監(jiān)控圖譜及metrics服務(wù))
每個(gè)系統(tǒng),都在其專業(yè)的領(lǐng)域提供專門的高級功能。
(這一段太長太長了,Jay兄十分能侃啊,所以挑重點(diǎn)的來記吧?。?/p>
1) 之所以引入數(shù)據(jù)流這個(gè)概念,是因?yàn)橐趏racle數(shù)據(jù)庫的表之上,建立一個(gè)抽象的緩存層,為搜索引擎的索引構(gòu)建和社交圖譜更新,提供拓展能力。
2) 為了更好的處理linkedin的一些推薦算法,開始搭Hadoop集群,但團(tuán)隊(duì)在此塊的經(jīng)驗(yàn)尚淺,所以走了很多彎路。
3) 開始時(shí),簡單粗暴地認(rèn)為只要將數(shù)據(jù)從oracle數(shù)據(jù)倉庫中拉出來,丟進(jìn)hadoop就可以了。結(jié)果發(fā)現(xiàn):第一,將數(shù)據(jù)從oracle數(shù)據(jù)倉庫快速導(dǎo)出是個(gè)噩夢;第二,也是更糟糕的一點(diǎn),數(shù)據(jù)倉庫中某些數(shù)據(jù)的處理不對,導(dǎo)致了hadoop的批處理任務(wù)不能按預(yù)期輸出結(jié)果,且通過hadoop批處理執(zhí)行任務(wù),通常不可逆,特別是在出了報(bào)表之后。
4) 最后,團(tuán)隊(duì)拋棄了從數(shù)據(jù)倉庫中出數(shù)據(jù)的方式,直接以數(shù)據(jù)庫和logs為數(shù)據(jù)源。接著,造出了一個(gè)輪子:K-V 存儲(Voldemort)。
5) 即使是數(shù)據(jù)拷貝這樣不高大上的活兒,也占據(jù)了團(tuán)隊(duì)大量的時(shí)間去處理,更糟的是,一旦數(shù)據(jù)處理的pipeline中有個(gè)點(diǎn)出錯(cuò),hadoop立馬變得廢柴,因?yàn)樵倥1频乃惴ㄅ茉阱e(cuò)誤的數(shù)據(jù)上,只有一個(gè)后果,就是產(chǎn)生更多的錯(cuò)誤數(shù)據(jù)。
6) 即使團(tuán)隊(duì)構(gòu)建的東西抽象層次很高,針對每種數(shù)據(jù)源還是需要特定的配置,而這也是很多錯(cuò)誤和失敗的根源。
7) 一大批程序員想跟進(jìn),每個(gè)程序員都有一大批的想法,集成這個(gè)系統(tǒng),添加這個(gè)功能,整合這個(gè)特色,或者想要自定義的數(shù)據(jù)源。
8) Jay哥開始意識到:
第一, 雖然他們構(gòu)建的pipelines還很糙,但是卻極其有價(jià)值。即使是解決了數(shù)據(jù)在新的系統(tǒng)(如hadoop)中可用的問題,也解鎖了一大批可能性。以前難做的計(jì)算開始變?yōu)榭赡?。新的產(chǎn)品和分析,僅需要解鎖其它系統(tǒng)中的數(shù)據(jù),并且進(jìn)行整合,就可以容易地做出來。
第二, 很明顯,可靠地?cái)?shù)據(jù)裝載需要更堅(jiān)實(shí)的支撐,如果能夠捕獲所有的結(jié)構(gòu),就可以讓hadoop數(shù)據(jù)裝載完全自動化,不需要加入新的數(shù)據(jù)源或人工修改數(shù)據(jù)的模式。數(shù)據(jù)會神奇地出現(xiàn)在HDFS中,而新的數(shù)據(jù)源加入后,Hive的表會用合適的列自動化地、自適應(yīng)地生成。
第三,數(shù)據(jù)覆蓋度遠(yuǎn)遠(yuǎn)不足。因?yàn)橐幚砗芏嘈碌臄?shù)據(jù)源,很難。
9) 為了解決新數(shù)據(jù)源加入后的數(shù)據(jù)裝載問題,團(tuán)隊(duì)開始了這樣的嘗試:
很快,他們發(fā)現(xiàn)這樣搞行不通,因?yàn)榘l(fā)布和訂閱、生產(chǎn)和消費(fèi),數(shù)據(jù)流通常還是雙向的,這成了一個(gè)O(n^2)的問題。
所以,他們需要的是這樣的模型:
需要將每個(gè)消費(fèi)者從數(shù)據(jù)源隔離,理想的情況下,這些消費(fèi)者只和一個(gè)data repository進(jìn)行交互,而這個(gè)repository可以提供它們訪問任意數(shù)據(jù)的能力。
10)消息系統(tǒng) + log = Kafka,kafka橫空出世。
2.5 Log和ETL、數(shù)據(jù)倉庫的關(guān)系
2.5.1 數(shù)據(jù)倉庫
1) 一個(gè)裝有干凈的、結(jié)構(gòu)化的、集成的數(shù)據(jù)repository,用于分析。
2) 雖然想法很美好,但是獲取數(shù)據(jù)的方式有點(diǎn)過時(shí)了:周期性地從數(shù)據(jù)庫獲取數(shù)據(jù),將其轉(zhuǎn)換為某種可讀性更佳的格式。
3) 之前的數(shù)據(jù)倉庫問題在于:將干凈的數(shù)據(jù)和數(shù)據(jù)倉庫高度耦合。
數(shù)據(jù)倉庫,應(yīng)該是一組查詢功能的集合,這些功能服務(wù)于報(bào)表、搜索、ad hot 分析,包含了計(jì)數(shù)(counting)、聚合(aggregation)、過濾(filtering)等操作,所以更應(yīng)該是一個(gè)批處理系統(tǒng)。
但是將干凈的數(shù)據(jù)和這樣的一種批處理系統(tǒng)高度耦合在一起,意味著這些數(shù)據(jù)不能被實(shí)時(shí)系統(tǒng)消費(fèi),比如搜索引擎的索引構(gòu)建、實(shí)時(shí)計(jì)算和實(shí)時(shí)監(jiān)控系統(tǒng),等等。
2.5.2 ETL
Jay哥認(rèn)為,ETL無非做兩件事:
1) 對數(shù)據(jù)進(jìn)行抽取和清洗,將數(shù)據(jù)從特定的系統(tǒng)中解鎖
2) 重構(gòu)數(shù)據(jù),使其能通過數(shù)據(jù)倉庫進(jìn)行查詢。比如將數(shù)據(jù)類型變?yōu)檫m配某個(gè)關(guān)系型數(shù)據(jù)庫的類型,將模式轉(zhuǎn)換為星型或者雪花模式,或者將其分解為某種面向列的存儲格式。
但是,將這兩件事耦合在一起,問題很大,因?yàn)榧珊蟮?、干凈的?shù)據(jù),本應(yīng)能被其它實(shí)時(shí)系統(tǒng)、索引構(gòu)建系統(tǒng)、低延時(shí)的處理系統(tǒng)消費(fèi)。
數(shù)據(jù)倉庫團(tuán)隊(duì),負(fù)責(zé)收集和清洗數(shù)據(jù),但是,這些數(shù)據(jù)的生產(chǎn)者往往因?yàn)椴幻鞔_數(shù)據(jù)倉庫團(tuán)隊(duì)的數(shù)據(jù)處理需求,導(dǎo)致輸出很難被抽取和清洗的數(shù)據(jù)。
同時(shí),因?yàn)楹诵臉I(yè)務(wù)團(tuán)隊(duì)對和公司的其它團(tuán)隊(duì)保持步調(diào)一致這件事兒不敏感,所以真正能處理的數(shù)據(jù)覆蓋度很低,數(shù)據(jù)流很脆弱,很難快速應(yīng)對變化。
所以,更好的方式是:
如果想在一個(gè)干凈的數(shù)據(jù)集上做點(diǎn)搜索、實(shí)時(shí)監(jiān)控趨勢圖、實(shí)時(shí)報(bào)警的事兒,以原有的數(shù)據(jù)倉庫或者h(yuǎn)adoop集群來作為基礎(chǔ)設(shè)施,都是不合適的。更糟的是,ETL所構(gòu)建的針對數(shù)據(jù)倉庫的數(shù)據(jù)加載系統(tǒng),對其它(實(shí)時(shí))系統(tǒng)點(diǎn)兒用沒有。
最好的模型,就是在數(shù)據(jù)發(fā)布者發(fā)布數(shù)據(jù)之前,就已經(jīng)完成了數(shù)據(jù)的清洗過程,因?yàn)橹挥邪l(fā)布者最清楚它們的數(shù)據(jù)是什么樣的。而所有在這個(gè)階段所做的操作,都應(yīng)該滿足無損和可逆。
所有豐富語義、或添加值的實(shí)時(shí)轉(zhuǎn)換,都應(yīng)在原始的log發(fā)布后處理(post-processing),包括為事件數(shù)據(jù)建立會話,或者添加某些感興趣的字段。原始的log依舊可被單獨(dú)使用,但是此類實(shí)時(shí)應(yīng)用也派生了新的參數(shù)化的log。
最后,只有對應(yīng)于具體的目標(biāo)系統(tǒng)的數(shù)據(jù)聚合操作,應(yīng)作為數(shù)據(jù)裝載的一部分,比如轉(zhuǎn)換為星型或雪花型模式,以在數(shù)據(jù)倉庫中進(jìn)行分析和出報(bào)表。因?yàn)檫@個(gè)階段,就像傳統(tǒng)的ETL所做的那樣,因?yàn)橛辛朔浅8蓛艉鸵?guī)范的數(shù)據(jù)流,(有了log后)現(xiàn)在變得非常簡單。
2.6 Log文件和事件
以log為核心的架構(gòu),還有個(gè)額外的好處,就是易于實(shí)現(xiàn)無耦合的、事件驅(qū)動的系統(tǒng)。
傳統(tǒng)的 捕獲用戶活動和系統(tǒng)變化的方式,是將此類信息寫入文本日志,然后抽取到數(shù)據(jù)倉庫或者h(yuǎn)adoop集群中進(jìn)行聚合和處理,這個(gè)問題和前面所述的數(shù)據(jù)倉庫和ETL問題類似:數(shù)據(jù)與數(shù)據(jù)倉庫的高度耦合。
在Linkedin,其基于kafka構(gòu)建了事件數(shù)據(jù)處理系統(tǒng)。為各種各樣的action定義了成百上千種事件類型,從PV、用戶對于廣告的趕腳(ad impressions)、搜索,到服務(wù)的調(diào)用和應(yīng)用的異常,等等。
為了體會上述事件驅(qū)動系統(tǒng)的好處,看一個(gè)簡單的關(guān)于事件的例子:
在工作機(jī)會頁面上,提供一個(gè)機(jī)會。這個(gè)頁面應(yīng)該只負(fù)責(zé)如何展示機(jī)會,而不應(yīng)該過多地包含其它邏輯。但是,你會發(fā)現(xiàn),在一個(gè)具有相當(dāng)規(guī)模的網(wǎng)站中,做這件事,很容易就會讓越來越多的與展示機(jī)會無關(guān)的邏輯牽扯進(jìn)來。
比如,我們希望集成以下系統(tǒng)功能:
1) 我們需要將數(shù)據(jù)發(fā)送到hadoop和數(shù)據(jù)倉庫做離線處理。
2) 我們需要統(tǒng)計(jì)頁面瀏覽次數(shù),以確保某些瀏覽不是為了抓取網(wǎng)頁內(nèi)容什么的。
3) 我們需要聚合對此頁面的瀏覽信息,在機(jī)會發(fā)布者的分析頁面上呈現(xiàn)。
4) 我們需要記錄某用戶對此頁面的瀏覽記錄,以確保我們對此用戶提供了有價(jià)值的、體驗(yàn)良好的任何適宜此用戶的工作機(jī)會,而不是對此用戶一遍又一遍地重復(fù)展示某個(gè)機(jī)會(想想老婆不在家才能玩的游戲吧,那紅綠藍(lán)閃爍的特效,配合那勁爆的DJ風(fēng)舞曲,或者那搖擺聚焦的事業(yè)峰和齊X小短裙的girls,然后點(diǎn)進(jìn)去才發(fā)現(xiàn)是標(biāo)題黨的ad吧?。?。
5) 我們的推薦系統(tǒng)需要記錄對此頁面的瀏覽記錄,以正確地追蹤此工作機(jī)會的流行度。
很快,僅僅展示機(jī)會的頁面邏輯,就會變得復(fù)雜。當(dāng)我們在移動端也增加了此機(jī)會的展示時(shí),不得不把邏輯也遷移過去,這又加劇了復(fù)雜程度。還沒完,糾結(jié)的東西是,負(fù)責(zé)處理此頁面的工程師,需要有其它系統(tǒng)的知識,以確保上述的那些功能能正確的集成在一起。
這只是個(gè)極其簡單的例子,在實(shí)踐中,情況只會更加復(fù)雜。
事件驅(qū)動可以讓這件事變得簡單。
負(fù)責(zé)呈現(xiàn)機(jī)會的頁面,只需要呈現(xiàn)機(jī)會并記錄一些和呈現(xiàn)相關(guān)的因素,比如工作機(jī)會的相關(guān)屬性,誰瀏覽了這個(gè)頁面,以及其它的有用的與呈現(xiàn)相關(guān)的信息。頁面不需要保持對其它系統(tǒng)的知識和了解,比如推薦系統(tǒng)、安全系統(tǒng)、機(jī)會發(fā)布者的分析系統(tǒng),還有數(shù)據(jù)倉庫,所有的這些系統(tǒng)只需要作為訂閱者,訂閱這個(gè)事件,然后獨(dú)立地進(jìn)行它們各自的處理即可,而呈現(xiàn)機(jī)會的頁面不需要因?yàn)樾碌挠嗛喺呋蛳M(fèi)者的加入而做出修改。
2.7 構(gòu)建可擴(kuò)展的log
分離發(fā)布者和訂閱者不新鮮,但是要保證多個(gè)訂閱者能夠?qū)崟r(shí)處理消息,并且同時(shí)保證擴(kuò)展能力,對于log系統(tǒng)來說,是一件比較困難的事。
如果log的構(gòu)建不具備快速、低開銷和可擴(kuò)展能力,那么建立在此log系統(tǒng)之上的一切美好都免談。
很多人可能認(rèn)為log系統(tǒng)在分布式系統(tǒng)中是個(gè)很慢、重型開銷的活兒,并且僅用來處理一些類似于ZooKeeper更適合處理的元數(shù)據(jù)等信息。
但是Linkedin現(xiàn)在(注:2013年),在kafka中每天處理600億條不同的消息寫入(如果算數(shù)據(jù)中心的鏡像的話,那就是幾千億條寫入)。
Jay哥他們怎么做到的呢?
1) 對log進(jìn)行分割(partitioning the log)
2) 通過批量讀寫優(yōu)化吞吐量
3) 避免不必要的數(shù)據(jù)拷貝
通過將log切為多個(gè)partition來提供擴(kuò)展能力:
1) 每個(gè)partition都是有序的log,但是partitions之間沒有全局的順序。
2) 將消息寫入哪個(gè)partition完全由寫入者控制,通過依照某種類型的key(如user_id)進(jìn)行分割。
3) 分割使得log的附加操作,可以不用在分片(sharding)之間進(jìn)行協(xié)調(diào)就進(jìn)行,同時(shí),保證系統(tǒng)的吞吐量和kafka集群的規(guī)模呈線性關(guān)系。
4) 雖然沒有提供全局順序(實(shí)際上消費(fèi)者或者訂閱者成千上萬,討論它們的全局順序一般沒有啥價(jià)值),但是kafka提供了這樣一種保證:發(fā)送者按照什么順序?qū)⑾l(fā)給某個(gè)partition,從這個(gè)partition遞交出去的消息就是什么順序(什么順序進(jìn),什么順序出)。
5) 每個(gè)partition都按照配置好的數(shù)目進(jìn)行復(fù)制,如果一個(gè)leader節(jié)點(diǎn)掛了,其它的節(jié)點(diǎn)會成為新的leader。
6) 一條log,同文件系統(tǒng)一樣,線性的讀寫模式可被優(yōu)化,將小的讀寫log可以組成更大的、高吞吐量的操作。Kafka在這件事上做的很猛。批處理用在了各種場景之下,比如客戶端將數(shù)據(jù)發(fā)送到服務(wù)端、將數(shù)據(jù)寫入磁盤、服務(wù)器之間的數(shù)據(jù)復(fù)制、將數(shù)據(jù)傳送給消費(fèi)者,以及確認(rèn)提交數(shù)據(jù)等場景。
7) 最后,kafka在內(nèi)存log、磁盤log、網(wǎng)絡(luò)中發(fā)送的log上,采用了很簡單的二進(jìn)制格式,以利于利用各種優(yōu)化技術(shù),比如零拷貝數(shù)據(jù)傳輸技術(shù)(zero-copy data transfer)。
諸多的優(yōu)化技術(shù),匯聚起來,可以讓你即使在內(nèi)存爆滿的情形下,也能按照磁盤或網(wǎng)絡(luò)能提供的最大能力進(jìn)行數(shù)據(jù)讀寫。
2.8 Logs和實(shí)時(shí)處理
你以為Jay哥提供了這么個(gè)美麗的方法把數(shù)據(jù)復(fù)制來復(fù)制去就完了?
你!錯(cuò)!了!
Log是流的另一種說法,logs是流處理的核心。
2.8.1 什么是流處理
Jay哥認(rèn)為:
1)流處理是連續(xù)數(shù)據(jù)處理的基礎(chǔ)設(shè)施。
2)流處理的計(jì)算模型,就如同MapReduce或其它分布式處理框架一樣,只是需要保證低延遲。
3)批處理式的收集數(shù)據(jù)模式,導(dǎo)致了批處理式的數(shù)據(jù)處理模式。
4)連續(xù)的收集數(shù)據(jù)模式,導(dǎo)致了連續(xù)的數(shù)據(jù)處理模式。
5)Jay哥講了個(gè)美國人口普查的方式來解釋批處理。
在linkedin,無論是活動數(shù)據(jù)還是數(shù)據(jù)庫的變化,都是連續(xù)的。
批處理按天處理數(shù)據(jù),和連續(xù)計(jì)算將窗口設(shè)為一天雷同。
所以,流處理是這樣一種過程:
6)在處理數(shù)據(jù)時(shí),帶了一個(gè)時(shí)間的概念,不需要對數(shù)據(jù)保持一個(gè)靜態(tài)的快照,所以可以在用戶自定義的頻率之下,輸出結(jié)果,而不必等數(shù)據(jù)集到達(dá)某種“結(jié)束”的狀態(tài)。
7)從這個(gè)意義上講,流處理是批處理的一種泛化,并且考慮到實(shí)時(shí)數(shù)據(jù)的流行程度,這是一種極其重要的泛化。
8)許多商業(yè)公司無法建立流處理引擎,往往因?yàn)闊o法建立流數(shù)據(jù)收集引擎。
9)流處理跨越了實(shí)時(shí)響應(yīng)式服務(wù)和離線批處理的基礎(chǔ)設(shè)施之間的鴻溝。
10)Log系統(tǒng),解決了很多流處理模式中的關(guān)鍵問題,其中最大的一個(gè)問題就是如何在實(shí)時(shí)的多個(gè)訂閱者模式下,提供可用數(shù)據(jù)的問題(流數(shù)據(jù)收集)。
2.9 數(shù)據(jù)流圖譜
流處理中最有趣的地方在于,其拓展了什么是數(shù)據(jù)源(feeds)這一概念。
無論是原始數(shù)據(jù)的logs、feeds,還是事件、一行一行的數(shù)據(jù)記錄,都來自應(yīng)用程序的活動。
但是,流處理還可以讓我們處理來自其它feeds的數(shù)據(jù),這些數(shù)據(jù)和原始數(shù)據(jù),在消費(fèi)者看來,并無二致,而這些派生的feeds可以包含任意程度的復(fù)雜性。
一個(gè)流處理任務(wù),應(yīng)該是這樣的:從logs讀取數(shù)據(jù),將輸出寫入logs或者其它系統(tǒng)。
作為輸入和輸出的logs,連通這些處理本身,和其它的處理過程,構(gòu)成了一個(gè)圖。
事實(shí)上,以log為核心的系統(tǒng),允許你將公司或機(jī)構(gòu)中的數(shù)據(jù)捕獲、轉(zhuǎn)換以及數(shù)據(jù)流,看作是一系列的logs及在其上進(jìn)行寫入的處理過程的結(jié)合。
一個(gè)流處理程序,其實(shí)不必很高大上:可以是一個(gè)處理過程或者一組處理過程,但是,為了便于管理處理所用的代碼,可以提供一些額外的基礎(chǔ)設(shè)施和支持。
引入logs有兩個(gè)目的:
1) 保證了數(shù)據(jù)集可以支持多個(gè)訂閱者模式,及有序。
2) 可以作為應(yīng)用的緩沖區(qū)。這點(diǎn)很重要,在非同步的數(shù)據(jù)處理進(jìn)程中,如果上游的生產(chǎn)者出數(shù)據(jù)的速度更快,消費(fèi)者的速度跟不上,這種情況下,要么使處理進(jìn)程阻塞,要么引入緩沖區(qū),要么丟棄數(shù)據(jù)。
丟棄數(shù)據(jù)似乎不是個(gè)好的選擇,而阻塞處理進(jìn)程,會使得所有的數(shù)據(jù)流的處理圖譜中的處理進(jìn)程卡住。而log,是一種很大,特大,非常大的緩沖區(qū),它允許處理進(jìn)程的重啟,使得某個(gè)進(jìn)程失敗后,不影響流處理圖譜中的其它進(jìn)程。這對于一個(gè)龐大的機(jī)構(gòu)去擴(kuò)展數(shù)據(jù)流是非常關(guān)鍵的,因?yàn)椴煌膱F(tuán)隊(duì)有不同的處理任務(wù),顯然不能因?yàn)槟硞€(gè)任務(wù)發(fā)生錯(cuò)誤,整個(gè)流處理進(jìn)程都被卡住。
Storm和Samza就是這樣的流處理引擎,并且都能用kafka或其它類似的系統(tǒng)作為它們的log系統(tǒng)。
(注:Jay哥相當(dāng)猛,前有kafka,后有samza。)
2.10 有狀態(tài)的實(shí)時(shí)處理
很多流處理引擎是無狀態(tài)的、一次一記錄的形式,但很多用例都需要在流處理的某個(gè)大小的時(shí)間窗口內(nèi)進(jìn)行復(fù)雜的counts , aggregations和joins操作。
比如,點(diǎn)擊流中,join用戶信息。
那么,這種用例,就需要狀態(tài)的支持。在處理數(shù)據(jù)的地方,需要維護(hù)某個(gè)數(shù)據(jù)的狀態(tài)。
問題在于,如何在處理者可能掛掉的情況下保持正確的狀態(tài)?
將狀態(tài)維護(hù)在內(nèi)存中可能是最簡單的,但抵不住crash。
如果僅在某個(gè)時(shí)間窗口內(nèi)維護(hù)狀態(tài),當(dāng)掛掉或者失敗發(fā)生,那么處理可以直接回退到窗口的起點(diǎn)來重放,但是,如果這個(gè)窗口有1小時(shí)那么長,這可能行不通。
還有個(gè)簡單的辦法,就是把狀態(tài)存在某個(gè)遠(yuǎn)程的存儲系統(tǒng)或數(shù)據(jù)庫中,但是這會損失數(shù)據(jù)的局部性并產(chǎn)生很多的網(wǎng)絡(luò)間數(shù)據(jù)往返(network round-trip)。
回憶下,上文中曾提到的數(shù)據(jù)庫中的表和log的對偶性。
一個(gè)流處理組件,可以使用本地的存儲或索引來維護(hù)狀態(tài):
Bdb
Leveldb
Lucene
Fastbit
通過記錄關(guān)于本地索引的changelog,用于在crash后恢復(fù)狀態(tài)。這種機(jī)制,其實(shí)也揭示了一種一般化的,可以存儲為任意索引類型的,與輸入流同時(shí)被分割(co-partitioned)的狀態(tài)。
當(dāng)處理進(jìn)程崩潰,其可以從changelog中恢復(fù)索引,log充當(dāng)了將本地狀態(tài)轉(zhuǎn)化為某種基于時(shí)間備份的增量記錄的角色。
這種機(jī)制還提供了一種很優(yōu)雅的能力:處理過程本身的狀態(tài)也可以作為log被記錄下來,顯然,其它的處理過程可以訂閱這個(gè)狀態(tài)。
結(jié)合數(shù)據(jù)庫中的log技術(shù),針對數(shù)據(jù)集成這一場景,往往可以做出很強(qiáng)大的事:
將log從數(shù)據(jù)庫中抽取出來,并在各種各樣的流處理系統(tǒng)中進(jìn)行索引,那么,與不同的事件流進(jìn)行join就成為可能。
2.11 Log 合并
顯然,用log記錄全時(shí)全量的狀態(tài)變更信息,不太可能。
Kafka使用了log合并或者log垃圾回收技術(shù):
1) 對于事件數(shù)據(jù),kafka只保留一個(gè)時(shí)間窗口(可在時(shí)間上配置為幾天,或者按空間來配置)
2) 對于keyed update,kafka采用壓縮技術(shù)。此類log,可以用來在另外的系統(tǒng)中通過重放技術(shù)來重建源系統(tǒng)的狀態(tài)。
如果保持全時(shí)全量的logs,隨著時(shí)間增長,數(shù)據(jù)將會變得越來越大,重放的過程也會越來越長。
Kafka不是簡單地丟棄老的日志信息,而是采用合并的方式,丟棄廢棄的記錄,比如,某個(gè)消息的主鍵最近被更新了。
2.12 系統(tǒng)構(gòu)建
2.12.1 分布式系統(tǒng)
Log,在分布式數(shù)據(jù)庫的數(shù)據(jù)流系統(tǒng)和數(shù)據(jù)集成中所扮演的角色是一致的:
抽象數(shù)據(jù)流
保持?jǐn)?shù)據(jù)一致性
提供數(shù)據(jù)恢復(fù)能力
你可以將整個(gè)機(jī)構(gòu)中的應(yīng)用系統(tǒng)和數(shù)據(jù)流,看作是一個(gè)單獨(dú)的分布式數(shù)據(jù)庫。
將面向查詢的獨(dú)立系統(tǒng),比如Redis , SOLR , Hive tables 等等,看作是一種特別的、數(shù)據(jù)之上的索引。
將Storm、Samza等流處理系統(tǒng),看做一種精心設(shè)計(jì)過的觸發(fā)器或者物化視圖機(jī)制。
各式各樣的數(shù)據(jù)系統(tǒng),爆發(fā)性的出現(xiàn),其實(shí),這種復(fù)雜性早已存在。
在關(guān)系型數(shù)據(jù)庫的輝煌時(shí)期(heyday),某個(gè)公司或者機(jī)構(gòu)光關(guān)系型數(shù)據(jù)庫就有很多種。
顯然,不可能將所有的東西都丟進(jìn)一個(gè)Hadoop集群中,期望其解決所有的問題。所以,如何構(gòu)建一個(gè)好的系統(tǒng),可能會像下面這樣:
構(gòu)建一個(gè)分布式系統(tǒng),每個(gè)組件都是一些很小的集群,每個(gè)集群不一定能完整提供安全性、性能隔離、或者良好的擴(kuò)展性,但是,每個(gè)問題都能得到(專業(yè)地)解決。
Jay哥覺得,之所以各式各樣的系統(tǒng)爆發(fā)性地出現(xiàn),就是因?yàn)橐獦?gòu)建一個(gè)強(qiáng)大的分布式系統(tǒng)十分困難。而如果將用例限制到一些簡單的,比如查詢這樣的場景下,每個(gè)系統(tǒng)都有足夠的能力去解決問題,但是要把這些系統(tǒng)整合起來,很難。
Jay哥覺得在未來構(gòu)建系統(tǒng)這事兒有三種可能:
1) 保持現(xiàn)狀。這種情況下,數(shù)據(jù)集成依然是最頭大的問題,所以一個(gè)外部的log系統(tǒng)就很重要(kafka?。?/p>
2) 出現(xiàn)一個(gè)強(qiáng)大的(如同輝煌時(shí)期的關(guān)系型數(shù)據(jù)庫)能解決所有問題的系統(tǒng),這似乎有點(diǎn)不可能發(fā)生。
3) 新生代的系統(tǒng)大部分都開源,這揭示了第三種可能:數(shù)據(jù)基礎(chǔ)設(shè)施可被離散為一組服務(wù)、以及面向應(yīng)用的系統(tǒng)API,各類服務(wù)各司其事,每個(gè)都不完整,卻能專業(yè)滴解決專門的問題,其實(shí)通過現(xiàn)存的java技術(shù)棧就能看出端倪:
ZooKeeper:解決分布式系統(tǒng)的同步、協(xié)作問題(也可能受益于更高抽象層次的組件如helix、curator).
Mesos、YARN:解決虛擬化和資源管理問題。
嵌入式的組件Lucene、LevelDB:解決索引問題。
Netty、Jetty及更高抽象層次的Finagle、rest.li解決遠(yuǎn)程通訊問題。
Avro、Protocol Buffers、Thrift及umpteen zlin:解決序列化問題。
Kafka、bookeeper:提供backing log能力。
從某種角度來看,構(gòu)建這樣的分布式系統(tǒng),就像某個(gè)版本的樂高積木一樣。這顯然跟更關(guān)心API的終端用戶沒有太大關(guān)系,但是這揭示了構(gòu)建一個(gè)強(qiáng)大系統(tǒng)并保持簡單性的一條道路:
顯然,如果構(gòu)建一個(gè)分布式系統(tǒng)的時(shí)間從幾年降到幾周,那么構(gòu)建一個(gè)獨(dú)立的龐大系統(tǒng)的復(fù)雜性就會消失,而這種情況的出現(xiàn),一定是因?yàn)槌霈F(xiàn)了更可靠、更靈活的“積木”。
2.12.2 Log在系統(tǒng)構(gòu)建中的地位
如果一個(gè)系統(tǒng),有了外部log系統(tǒng)的支持,那么每個(gè)獨(dú)立的系統(tǒng)就可以通過共享log來降低其自身的復(fù)雜性,Jay哥認(rèn)為log的作用是:
1) 處理數(shù)據(jù)一致性問題。無論是立即一致性還是最終一致性,都可以通過序列化對于節(jié)點(diǎn)的并發(fā)操作來達(dá)到。
2) 在節(jié)點(diǎn)間提供數(shù)據(jù)復(fù)制。
3) 提供“提交”的語義。比如,在你認(rèn)為你的寫操作不會丟失的情況下進(jìn)行操作確認(rèn)。
4) 提供外部系統(tǒng)可訂閱的數(shù)據(jù)源(feeds)。
5) 當(dāng)節(jié)點(diǎn)因失敗而丟失數(shù)據(jù)時(shí),提供恢復(fù)的能力,或者重新構(gòu)建新的復(fù)制節(jié)點(diǎn)。
6) 處理節(jié)點(diǎn)間的負(fù)載均衡。
以上,大概是一個(gè)完整的分布式系統(tǒng)中應(yīng)提供的大部分功能了(Jay哥確實(shí)愛Log?。O碌木褪强蛻舳说腁PI和諸如一些構(gòu)建索引的事了,比如全文索引需要獲取所有的partitions,而針對主鍵的查詢,只需要在某個(gè)partition中獲取數(shù)據(jù)。
(那把剩下的事情也交代下吧,Jay哥威武!)
系統(tǒng)可被分為兩個(gè)邏輯組件(這強(qiáng)大的理解和功力):
1) Log層
2) 服務(wù)層
Log層,以序列化的、有序的方式捕獲狀態(tài)的變化,而服務(wù)層,則存儲外部查詢需要的索引,比如一個(gè)K-V存儲可能需要B-tree、sstable索引,而一個(gè)搜索服務(wù)需要倒排索引。
寫操作既可以直接入log層,也可以通過服務(wù)層做代理。寫入log會產(chǎn)生一個(gè)邏輯上的時(shí)間戳(log的索引),比如一個(gè)數(shù)字ID,如果系統(tǒng)partition化了,那么,服務(wù)層和log層會擁有相同的partitions(但其各自的機(jī)器數(shù)可能不同)。
服務(wù)層訂閱到log層,并且以最快的速度、按log存儲的順序追log,將數(shù)據(jù)和狀態(tài)變化同步進(jìn)自己的本地索引中。
客戶端將會得到read-your-write的語義:
通過對任一一個(gè)節(jié)點(diǎn),在查詢時(shí)攜帶其寫入時(shí)的時(shí)間戳,服務(wù)層的節(jié)點(diǎn)收到此查詢,通過和其本地索引比較時(shí)間戳,如果必要,為了防止返回過期的老數(shù)據(jù),推遲請求的執(zhí)行,直到此服務(wù)節(jié)點(diǎn)的索引同步跟上了時(shí)間戳。
服務(wù)層的節(jié)點(diǎn),也許需要、也許不需要知道leader的概念。在很多簡單的用例中,服務(wù)層可不構(gòu)建leader節(jié)點(diǎn),因?yàn)閘og就是事實(shí)的來源。
還有一個(gè)問題,如何處理節(jié)點(diǎn)失敗后的恢復(fù)問題??梢赃@樣做,在log中保留一個(gè)固定大小的時(shí)間窗口,同時(shí)對數(shù)據(jù)維護(hù)快照。也可以讓log保留數(shù)據(jù)的全量備份并使用log合并技術(shù)完成log自身的垃圾回收。這種方法,將服務(wù)層的眾多復(fù)雜性移至log層,因?yàn)榉?wù)層是系統(tǒng)相關(guān)(system-specific)的,而log層確可以通用。
基于log系統(tǒng),可以提供一組完備的、供開發(fā)使用的、可作為其它系統(tǒng)的ETL數(shù)據(jù)源、并供其它系統(tǒng)訂閱的API。
Full Stack ?。?/p>
顯然,一個(gè)以log為核心的分布式系統(tǒng),其本身立即成為了可對其它系統(tǒng)提供數(shù)據(jù)裝載支持及數(shù)據(jù)流處理的角色。同樣的,一個(gè)流處理系統(tǒng),也可以同時(shí)消費(fèi)多個(gè)數(shù)據(jù)流,并通過對這些數(shù)據(jù)流進(jìn)行索引然后輸出的另一個(gè)系統(tǒng),來對外提供服務(wù)。
基于log層和服務(wù)層來構(gòu)建系統(tǒng),使得查詢相關(guān)的因素與系統(tǒng)的可用性、一致性等因素解耦。
也許很多人認(rèn)為在log中維護(hù)數(shù)據(jù)的單獨(dú)備份,特別是做全量數(shù)據(jù)拷貝太浪費(fèi)、太奢侈,但事實(shí)并非如此:
1) linkedin(注:2013年)的kafka生產(chǎn)集群維護(hù)了每數(shù)據(jù)中心75TB的數(shù)據(jù),而應(yīng)用集群需要的存儲空間和存儲條件(SSD+更多的內(nèi)存)比kafka集群要高。
2) 全文搜索的索引,最好全部裝入內(nèi)存,而logs因?yàn)槎际蔷€性讀寫,所以可以利用廉價(jià)的大容量磁盤。
3) 因?yàn)閗afka集群實(shí)際運(yùn)作在多個(gè)訂閱者的模式之下,多個(gè)系統(tǒng)消費(fèi)數(shù)據(jù),所以log集群的開銷被攤還了。
4) 所有以上原因,導(dǎo)致基于外部log系統(tǒng)(kafka或者類似系統(tǒng))的開銷變得非常小。
2.13 結(jié)語
Jay哥在最后,不僅厚道地留下了很多學(xué)術(shù)、工程上的有價(jià)值的論文和參考鏈接,還很謙遜地留下了這句話:
If you made it this far you know most of what I know about logs.
終。
聯(lián)系客服