[TOC]
我先解釋一下我說的是『數據集成』(`data integration`)是什么,還有為什么我覺得它很重要,然后我們再來看看它是如何和日志建立關系的。
**數據集成 是指 使一個組織的所有數據 對 這個組織的所有的服務和系統 可用。**
『數據集成』還不是一個常見的用語,但是我找不到一個更好的。大家更熟知的術語[`ETL`](http://en.wikipedia.org/wiki/Extract,_transform,_load)?(譯注:`ETL`是`Extraction-Transformation-Loading`的縮寫,即數據提取、轉換和加載) 通常只是覆蓋了數據集成的一個有限子集 —— 主要在關系型數據倉庫的場景。但我描述的東西很大程度上可以理解為,將`ETL`推廣至覆蓋實時系統和處理流程。
[](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/cabling.jpg)
你一定不會聽到數據集成就興趣盎然地屏住呼吸,并且天花亂墜的想到***大數據***的概念, 但盡管如此,我相信這個陳詞濫調的『讓數據可用』的問題是組織可以關注的更有價值的事情之一。
對數據的高效使用遵循一種[馬斯洛的需要層次理論](http://en.wikipedia.org/wiki/Maslow%27s_hierarchy_of_needs)。 金字塔的基礎部分包含捕獲所有相關數據,能夠將它們全部放到適當的處理環境中(可以是一個華麗的實時查詢系統,或僅僅是文本文件和`python`腳本構成的環境)。 這些數據需要以統一的方式建模,以方便讀取和處理。 一旦這些以統一的方式捕獲數據的基本需求得到滿足,那么在基礎設施上以不同方法處理這些數據就變得理所當然 ——`MapReduce`、實時查詢系統等等。
顯而易見但值得注意的一點:如果沒有可靠的、完整的數據流,`Hadoop`集群只不過是個非常昂貴而且安裝麻煩的供暖器。 一旦有了數據和處理(`data and processing`),人們的關注點就會轉移到良好的數據模型和一致且易于理解的語義這些更精致的問題上來。 最后,關注點會轉移到更高級處理上 —— 更好的可視化、生成報表以及處理和預測算法。
以我的經驗,大多數組織在這個數據金字塔的底部存在巨大的漏洞 —— 缺乏可靠的完整的數據流 —— 卻想直接跳到高級數據模型技術上。這樣做完全是本未倒置。
所以問題是,我們如何在組織中構建貫穿所有數據系統的可靠數據流?
## [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2-data-integration.md#數據集成兩個難題)數據集成:兩個難題
有兩個趨勢使數據集成變得更加困難。
### [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2-data-integration.md#事件數據管道)事件數據管道
第一個趨勢是增長的事件數據(`event data`)。事件數據記錄的是發生的事情,而不是已存在的事情。 在`Web`系統中,這就意味著用戶活動日志,還有為了可靠地操作和監控數據中心機器的價值而記錄的機器級別的事件和統計數字。 人們傾向于稱它們為『日志數據(`log data`)』,因為它們經常被寫到應用日志中,但這樣的說法混淆了形式與功能。 這些數據是現代`Web`的核心:歸根結底,`Google`的財富來自于建立在點擊和展示(`clicks and impressions`)上的相關性管道(`relevance pipeline`),而這些點擊和展示正是事件。
這樣的事情并不是僅限于`Web`公司,只是`Web`公司已經完全數字化,所以更容易完成。財務數據長久以來一直是以事件為中心的。?[`RFID`](http://en.wikipedia.org/wiki/RFID)(無線射頻識別)使得能對物理設備做這樣的跟蹤。 隨著傳統的業務和活動的[數字化(`digitization`)](http://online.wsj.com/article/SB10001424053111903480904576512250915629460.html), 我認為這個趨勢仍將繼續。
這種類型的事件數據記錄了發生的事情,往往比傳統數據庫應用要大好幾個數量級。這對于處理提出了重大的挑戰。
### [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2-data-integration.md#專用的數據系統specialized-data-systems的爆發)專用的數據系統(`specialized data systems`)的爆發
第二個趨勢來自于專用的數據系統的[爆發](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.68.9136),這些數據系統在最近五年開始流行并且可以免費獲得。 專門用于[`OLAP`](https://github.com/metamx/druid/wiki)、[搜索](http://www.elasticsearch.org/)、[簡單](http://www.rethinkdb.com/)?[在線](http://www.slideshare.net/amywtang/espresso-20952131)?[存儲](http://hadoop.apache.org/)、?[批處理](http://hadoop.apache.org/)、[圖分析(`graph analysis`)](http://graphlab.org/)?[等](http://redis.io/)?[等](http://spark.incubator.apache.org/)?的數據系統已經出現。
更加多樣化的數據同時變成更加大量,而且這些數據期望放到更多的系統中,這些需求同時要解決,導致了一個巨大的數據集成問題。
## [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2-data-integration.md#日志結構化的log-structured數據流)日志結構化的(`log-structured`)數據流
處理系統之間的數據流,日志是最自然的數據結構。解決方法很簡單:
**提取所有組織的數據,并放到一個用于實時訂閱的中心日志中。**
每個邏輯數據源都可以建模為它自己的日志。 一個數據源可以看作 一個輸出事件日志的應用(如點擊或頁面的瀏覽),或是 一個接受修改的數據庫表。 每個訂閱消息的系統都盡可能快的從日志讀取信息,將每條新的記錄應用到自己的存儲中,同時向前滾動日志文件中的自己的位置。 訂閱方可以是任意一種數據系統 —— 緩存、`Hadoop`、另一個網站中的另一個數據庫、一個搜索系統,等等。
[](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/log_subscription.png)
舉個例子,日志概念為每個變更提供了邏輯時鐘,所有的訂閱方都可以比較這個邏輯時鐘。 這極大簡化了如何去推斷不同的訂閱系統的狀態彼此是否一致的,因為每個系統都持有了一個讀到哪兒的『時間點』。
為了讓討論更具體些,我們考慮一個簡單的案例,有一個數據庫和一組緩存服務器集群。 日志提供了一個方法可以同步更新到所有這些系統,并推斷出每個系統的所處在的時間點。 我們假設做了一個寫操作,對應日志記錄`X`,然后要從緩存做一次讀操作。 如果我們想保證看到的不是過時的數據,我們只需保證,不要去讀取那些復制操作還沒有跟上`X`的緩存即可。
日志也起到緩沖的作用,使數據的生產異步于數據的消費。有許多原因使得這一點很重要,特別是在多個訂閱方消費數據的速度各不相同的時候。 這意味著一個數據訂閱系統可以宕機或是下線維護,在重新上線后再趕上來:訂閱方可以按照自己的節奏來消費數據。 批處理系統如`Hadoop`或者是一個數據倉庫,或許只能每小時或者每天消費一次數據,而實時查詢系統可能需要及時到秒。 無論是起始的數據源還是日志都感知感知各種各樣的目標數據系統,所以消費方系統的添加和刪除無需去改變傳輸管道。
特別重要的是:目標系統只知道日志,而不知道來源系統的任何細節。 無論是數據來自于一個`RDBMS`、一種新型的鍵值存儲,還是由一個不包含任何類型實時查詢的系統所生成的,消費方系統都無需關心。 這似乎是一個小問題,但實際上卻是至關重要的。
> [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/19202238_eoij.jpg)
> [『每個工作的數據管道要設計得像是一個日志;每個損壞的數據管道都以其自己的方式損壞。』
> ——?*Count Leo Tolstoy*?(由作者翻譯)](http://en.wikipedia.org/wiki/Anna_Karenina_principle)
這里我使用術語『日志』取代了『消息系統』或者『發布-訂閱』,因為在語義上明確得多,并且準確得多地描述了在實際實現中支持數據復制時你所要做的事。 我發現『發布訂閱』只是表達出了消息的間接尋址(`indirect addressing of messages`) —— 如果你去比較兩個發布-訂閱的消息系統的話,會發現他們承諾的是完全不同的東西,而且大多數模型在這一領域沒什么用。 你可以認為日志是一種有持久性保證和強有序(`strong ordering`)語義的消息系統。 在分布式系統中,這個通信模型有時有個(有些可怕的)名字叫做[原子廣播(`atomic broadcast`)](http://en.wikipedia.org/wiki/Atomic_broadcast)。
值得強調的是,日志仍然只是基礎設施,并不是精通數據流這個故事的結束: 故事的剩余部分圍繞著元數據(`metadata`)、`schemas`、兼容性以及處理數據結構及其演化的所有細節來展開。 但是,除非有一種可靠的通用的方式來處理數據流的機制,否則語義細節總是次要的。
## [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2-data-integration.md#在linkedin)在`LinkedIn`
隨著`LinkedIn`從集中式關系數據庫過渡到一套分布式系統,我注意到數據集成的問題在迅速地演變。
[](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/linkedin.png)
目前我們主要的數據系統包括:
* [搜索](http://data.linkedin.com/projects/search)
* [`Social Graph`](http://engineering.linkedin.com/real-time-distributed-graph/using-set-cover-algorithm-optimize-query-latency-large-scale-distributed)
* [`Voldemort`](http://project-voldemort.com/)?(鍵值存儲)
* [`Espresso`](http://data.linkedin.com/projects/espresso)?(文檔存儲)
* [推薦引擎](http://www.quora.com/LinkedIn-Recommendations/How-does-LinkedIns-recommendation-system-work)
* `OLAP`查詢引擎
* [`Hadoop`](http://hadoop.apache.org/)
* [`Terradata`](http://www.teradata.com/)
* [`Ingraphs`](http://engineering.linkedin.com/52/autometrics-self-service-metrics-collection)?(監控圖表和指標服務)
每一個都是專用的分布式系統,在各自的專門領域提供高級的功能。
使用日志作為數據流的這個想法,甚至在我到這里之前,就已經在`LinkedIn`的各個地方開始浮現了。 我們開發的最早的一個基礎設施是一個稱為[`databus`](https://github.com/linkedin/databus)的服務, 它在我們早期的`Oracle`表上提供了一種日志緩存的抽象,用于可伸縮地訂閱數據庫修改,給我們的`social graph`和搜索索引輸入數據。
我先簡單介紹一些歷史以提供討論的上下文。在發布我們自己鍵值存儲之后,大約是2008年我開始參與這個項目。 我接著的一個項目是把一個運行的`Hadoop`部署用起來,遷移我們的一些推薦處理上來。 由于缺乏這方面的經驗,我們只計劃了幾周時間完成數據的導入導出,剩下的時間則用來實現復雜的預測算法。 就這樣我們開始了長途跋涉。
我們本來計劃是僅僅將數據從現存的`Oracle`數據倉庫中剖離。 但是我們首先發現將數據從`Oracle`中迅速取出簡直是一個黑魔法(`dark art`)。 更糟的是,數據倉庫的處理過程并不適合于 我們為`Hadoop`設計的生產批處理過程 —— 大部分處理都是不可逆的,并且與要生成的具體報表相關。 最終我們采取的辦法是,避免使用數據倉庫,直接訪問源數據庫和日志文件。 最后,我們實現了一個管道,用于完成[加載數據到我們的鍵值存儲](http://data.linkedin.com/blog/2009/06/building-a-terabyte-scale-data-cycle-at-linkedin-with-hadoop-and-project-voldemort)并生成結果。
這種普通常見的數據拷貝最終成為原來開發項目的主要內容之一。 糟糕的是,只要在任何時間任意管道有一個問題,`Hadoop`系統基本上就是廢的 —— 在錯誤的數據基礎上運行復雜的算法只會產生更多的錯誤數據。
雖然我們已經使用了一種很通用的構建方式,但是每個數據源都需要自定義的安裝配置。這也被證明是大量錯誤與失敗的根源。 我們用`Hadoop`實現的網站功能開始流行起來,而我們發現自己有一大把需要協作的工程師。 每個用戶都有他們想要集成的一大把的系統,并且想要導入的一大把新數據源。
有些東西在我面前開始漸漸清晰起來。
首先,我們已建成的通道雖然有一些雜亂,但實際上是極有價值的。 僅在一個新的處理系統(`Hadoop`)中讓數據可用于處理 就開啟了大量的可能性。 基于這些數據過去很難實現的計算如今已變為可能。 許多新的產品和分析技術都來源于把多個數據片塊放在一起,這些數據過去被鎖定在特定的系統中。
> [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/sisyphus.jpg)
> 古希臘時代的`ETL`。并沒有太多變化。
第二,可靠的數據加載需要數據通道的深度支持,這點已經變得很清晰了。 如果我們可以捕獲所有我們需要的結構,就可以使得`Hadoop`數據全自動地加載, 這樣不需要額外的手動操作就可以添加新的數據源或者處理`schema`變更 —— 數據就會自動的出現在`HDFS`,并且`Hive`表就會自動的為新數據源生成恰當的列。
第三,我們的數據覆蓋率仍然很低。 如果看一下`LinkedIn`所有數據在`Hadoop`中可用的比率,仍然很不完整。 相比接入并運轉一個新數據源所要做的努力,完整接入一個數據源更不容易。
我們曾經推行的方式是為每個數據源和目標構建自定義的數據加載,很顯然這是不可行的。 我們有幾十個數據系統和數據倉庫。把這些系統和倉庫聯系起來,就會導致任意兩兩系統間構建自定義的管道,如下所示:
[](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/datapipeline_complex.png)
需要注意的是數據是雙向流動的:例如許多系統(數據庫、`Hadoop`)同時是數據傳輸的來源和目的。 這就意味著我們我們最后要為每個系統建立兩個通道:一個用于數據輸入,一個用于數據輸出。
這顯然需要一大群人,而且也不具有可操作性。隨著我們接近完全連接,最終我們會有差不多`O(N^2)`條管道。
要避免上面的問題,我們需要像這樣的通用方式:
[](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/datapipeline_simple.png)
我們需要盡可能的將每個消費者與數據源隔離。理想情形下,它們應該只與一個單獨的數據源集成,就能訪問到所有數據。
這個思想是增加一個新的數據系統 —— 它可以作為數據來源或者數據目的地 —— 集成工作只需連接這個新系統到一個單獨的管道,而無需連接到每個數據消費方。
這樣的經歷使得我專注于創建[`Kafka`](http://kafka.apache.org/),把 我們所知的消息系統的特點 與 在數據庫和分布式系統內核常用的日志概念 結合起來。 我們首先需要一個實體作為所有的活動數據的中心管道,并逐步的擴展到其他很多的使用方式,包括`Hadoop`之外的數據、數據監控等等。
在相當長的時間內,`Kafka`是獨一無二的(有人會說是怪異) —— 作為一個底層設施,它既不是數據庫,也不是日志文件收集系統,更不是傳統的消息系統。 但是最近`Amazon`提供了非常非常類似`Kafka`的服務,稱之為[`Kinesis`](http://aws.amazon.com/kinesis)。 相似度包括了分片(`partition`)處理的方式,數據的持有方式,甚至包括有點特別的`Kafka API`分類(分成高端和低端消費者)。 我很開心看到這些,這表明了你已經創建了很好的底層設施抽象,`AWS`已經把它作為服務提供! 他們對此的想法看起來與我所描述的完全吻合: 管道聯通了所有的分布式系統,諸如`DynamoDB`,`RedShift`,`S3`等,同時作為使用`EC2`進行分布式流處理的基礎。
## [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2-data-integration.md#etl與數據倉庫的關系)`ETL`與數據倉庫的關系
我們再來聊聊數據倉庫。數據倉庫旨在包含支撐數據分析的規整的集成的結構化數據。 這是一個非常好的理念。對不了解數據倉庫概念的人來說,數據倉庫的用法是: 周期性的從源數據庫抽取數據,把它們轉化為可理解的形式,然后把它導入中心數據倉庫。 對于數據集中分析和處理,擁有高度集中的位置存放全部數據的規整副本對于數據密集的分析和處理是非常寶貴的資產。 在更高層面上,無論你使用傳統的數據倉庫`Oracle`還是`Teradata`或`Hadoop`, 這個做法不會有太多變化,可能[調整](http://searchdatamanagement.techtarget.com/definition/Extract-Load-Transform-ELT)一下抽取和加載數據的順序。
數據倉庫是極其重要的資產,它包含了的和規整的數據,但是實現此目標的機制有點過時了。
[](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/oracle.jpg)
對于以數據為中心的組織,關鍵問題是把規整的集成的數據聯結到數據倉庫。 數據倉庫是個批處理查詢基礎設施:它們適用于各類報表和臨時性分析,特別是當查詢包含了簡單的計數、聚合和過濾。 但是如果批處理系統是唯一一個包含規整的完整的數據的倉庫, 這就意味著,如果一個系統需要 實時數據輸入的實時系統(如實時處理、實時搜索索引、實時監控等系統),這些數據是不可用的。
依我之見,`ETL`包括兩件事。 首先,它是數據抽取和清理的處理 —— 本質上就是釋放被鎖在組織的各類系統中的數據,去除特定于系統的約束。 第二,依照數據倉庫的查詢重構數據,例如使其符合關系數據庫類型系統, 強制使用星型`schema`(`star schema`)、雪花型`schema`(`snowflake schema`),可能會打散數據成高性能的[列](http://parquet.io/)?[格式](http://docs.hortonworks.com/HDPDocuments/HDP2/HDP-2.0.0.2/ds_Hive/orcfile.html)(`column format`),等等。同時做好這兩件事是有困難的。 這些集成倉庫的規整的數據除了要索引到實時存儲系統中,也應當可用于實時或是低時延處理中。
在我看來,正是因為這個原因有了額外好處:使得數據倉庫`ETL`大大提升了***組織級***的可伸縮性(`scalable`)。 典型的問題是數據倉庫團隊要負責收集和整理組織中各個團隊所生成的全部數據。 兩邊的收益是不對稱的:數據的生產者常常并不知曉在數據倉庫中數據的使用情況, 結果產生的數據,為了轉成為可用的形式,抽取過程很難或是很繁重,轉換過程很難統一規模化。 當然,中心團隊的規模不可能跟上組織中其它團隊增長, 結果數據的覆蓋率總是參差不齊的,數據流是脆弱的,跟進變更是緩慢的。
較好的做法是有一個中央管道即日志,用定義良好的`API`來添加數據。 集成這個管道和提供良好的結構化的輸入數據所需的職責由提供數據的生產者承擔。 這意味著作為系統設計和實現一部分的生產者,在交付到中心通道時, 必須考慮其輸出和輸入的數據要有良好結構形式的問題。 新的存儲系統的加入對于數據倉庫團隊是無關緊要的,因為他們現在只有一個中心結點去集成。 (***譯注***:原來要集成的是其它各個相關的系統,工作是被簡化了的) 數據倉庫團隊需只處理更簡單的問題,從中心日志中加載結構化的輸入數據、完成特定于他們系統的數據轉換。
[](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/pipeline_ownership.png)
從上面討論可以看出,當考慮采納傳統的數據倉庫之外額外的數據系統時,組織級的伸縮性(`organizational scalability`)顯得尤為重要。 例如,想為組織的所有的數據集提供搜索能力。 或者想為數據流的監控的次級監控(`sub-second monitoring`)添加實時數據趨勢和告警。 無論是哪個情況,傳統的數據倉庫的基礎設施,甚至是`Hadoop`集群都將不再適合。 更糟的是,用于支持數據加載的`ETL`處理管道可能輸入不了數據到其它系統, 和帶動不了要動用數據倉庫這樣的大企業下的那些基礎設備。 這樣的做法應該是不可行的,可能可以解釋為什么多數組織對他們的所有數據很難輕松具備這樣的能力。 反之,如果組織能導出標準的結構良好的數據, 那么任何新的系統要使用所有數據僅僅需要提供一個用于集成的管道接到中央管道上即可。
關于數據規整化和轉換在哪里進行,這種架構也引出了的不同觀點:
1. 在添加數據到公司全局日志之前,由數據的生產者完成。
2. 由在日志上的一個實時轉換器完成,轉換器生成一個新的轉換過的日志。
3. 作為加載過程的一部分,由目標系統完成。
最好的模型是數據發布到日志之前由數據生產者完成數據規整化。 這樣可以確保數據是處于規范形式(`canonical form`)的, 并且不需要保留數據 從原來生產系統的特定代碼或是原來存儲系統的維護方式所帶來的任何遺留屬性。 這些細節最好由產成數據的團隊來處理,因為他們最了解他們自己的數據。 這個階段所使用的任何邏輯都應該是無損的和可逆的。
可以實時完成的任何類型有附加值的轉換操作都應該作為原始日志數據的后處理環節完成。 這類操作包括了事件數據的會話管理,或者附加上大家感興趣的派生字段。 原始日志仍然是可用的,但這樣的實時處理生產了包含增強數據(`augmented data`)的派生日志。
最后,只有針對目標系統的聚合操作才應該加到加載過程中。 比如可能包括在數據倉庫中為分析和報表而做的把數據轉化成特定的星型或者雪花狀`schema`。 因為在這個階段(一般比較自然地對應到傳統的`ETL`處理階段),現在處理的是一組規整得多和統一得多的流, 處理過程已經大大簡化了。
## [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2-data-integration.md#日志文件與事件)日志文件與事件
我們再來聊聊這種架構的附帶的優勢:支持解耦的事件驅動的系統。
在`Web`行業取得活動數據的典型方法是把打日志到文本文件中, 然后這些文本文件分解進入數據倉庫或者`Hadoop`用于聚合和查詢。 這做的問題和所有批處理的`ETL`做法一樣:數據流耦合了數據倉庫系統的能力和處理計劃(`processing schedule`)。
在`LinkedIn`,是以在中心日志完成處理的方式構建事件數據。?`Kafka`做為中心的有多個訂閱方的事件日志,定義數百種事件類型, 每種類型都會捕獲一個特定動作類型的獨特屬性。 這樣的方式覆蓋從頁面瀏覽、廣告展示、搜索到服務調用、應用異常的方方面面。
為了進一步理解這一優勢,設想一個簡單的場景 —— 顯示在工作職位頁面提交的職位信息。 職位頁面應當只包括顯示職位所需的邏輯。 然而,在足夠動態站點中,這很容易就變成與職位顯示無關的額外邏輯的點綴。 例如,我們將對如下的系統進行集成:
1. 發送數據到`Hadoop`和數據倉庫中,以做離線數據處理
2. 瀏覽計數,確保查看者不是一個內容爬蟲
3. 聚合瀏覽信息,在職位提交者的分析頁面顯示
4. 記錄瀏覽信息,確保合適地設置了用戶的推薦職位的展示上限(不想重復地展示同樣內容給用戶)
5. 推薦系統可能需要記錄瀏覽,以正確的跟蹤職位的流行程度
6. 等等
用不了多久,簡單的職位顯示變得相當的復雜。 與此同時,還要增加職位顯示的其它終端 —— 移動終端應用等等 —— 這樣的邏輯必須繼續實現,復雜程度被不斷地提升。 更糟的是,我們需要交互的系統是多方需求交織纏繞在一起的 —— 負責顯示職位的工程師需要知道多個其它系統和功能,才可以確保集成的正確。 這里僅是簡單描述了問題,實際應用系統只會更加復雜。
『事件驅動』風格提供了簡化這類問題的方案。 職位顯示頁面現在只負責顯示職位并記錄顯示職位的信息,如職位相關屬性、頁面瀏覽者及其它有價值的信息。 其它所有關心這個信息的系統諸如推薦系統、安全系統、職位提交分析系統和數據倉庫,只需訂閱上面的輸出數據進行各自的處理。 顯示代碼并不需要關注其它的系統,也不需要因為增加了數據的消費者而做改變。
## [](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2-data-integration.md#構建可伸縮的日志)構建可伸縮的日志
當然,把發布者與訂閱者分離不再是什么新鮮事了。 但是如果要給一個需要按用戶擴展的(`consumer-scale`)網站提供多個訂閱者的實時提交日志, 那么可伸縮性就會成為你所面臨的首要挑戰。 如果我們不能創建快速、低成本和可伸縮的日志以滿足實際大規模的使用,把日志用作統一集成機制只不過是個美好的幻想。
人們普遍認為分布式日志是緩慢的、重量級的抽象(并且通常只把它與『元數據』類的使用方式聯系在一起,可能用`Zookeeper`才合適)。 但有了一個專注于大數據流的深思熟慮的實現可以打破上面的想法。 在`LinkedIn`,目前每天通過`Kafka`寫入超過600億條不同的消息。 (如果算上[數據中心之間鏡像](http://kafka.apache.org/documentation.html#datacenters)的消息,那么這個數字會是數千億。)
為了支持這樣的規模,我們在`Kafka`中使用了一些技巧:
1. 日志分片
2. 通過批量讀出和寫入來優化吞吐量
3. 規避無用的數據拷貝
為了確保水平可擴展性,我們把日志進行切片:
[](https://github.com/oldratlee/translations/blob/master/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/images/partitioned_log.png)
每個分片的日志是有序的,但是分片之間沒有全局的次序(這個有別于在你的消息中可能包含的掛鐘時間)。 由寫入者決定消息發送到特定的日志分片,大部分使用者以某種鍵值(如用戶`id`)來進行分片。 追加日志時,分片方式在片段之間可以不需要協調,并且可以使系統的吞吐量與`Kafka`集群大小線性增長。
每個分片通過可配置數字指定數據復制的復本個數,每個復本都有一個分片日志完全一致的一份拷貝。 任何時候都有一個復本作為`leader`,如果`leader`出錯了,會有一個復本接替成為`leader`。
缺少跨分片的全局順序是個局限,但是我們沒有發現它成為大問題。 事實上,與日志的交互一般來源于成百上千個不同的處理流程,所以為所有處理提供全局順序沒有意義。 轉而需要的是,我們提供的每個分片有序的保證,和`Kafka`提供的 同一發送者發送給同一分區的消息以相同的順序交付到接收者 的保證。
日志,和文件系統一樣,對于順序讀寫可以方便地優化。日志可以把小的讀寫合成大的高吞吐量的操作。?`Kafka`非常積極做這方面的優化。客戶端向服務器端的數據發送、磁盤寫入、服務器之間復制、到消費者數據傳遞和數據提交確認 都會做批處理。
最后,`Kafka`使用簡單的二進制格式維護內存日志、磁盤日志和傳送網絡數據。這使得我們可以使用包括『[0拷貝的數據傳輸](https://www.ibm.com/developerworks/library/j-zerocopy)』在內的大量的優化機制。
這些優化的積累效應是往往以磁盤和網絡的速度在讀寫數據,即使維護的數據集大大超出內存大小。
這些自賣自夸的介紹不意味著是關于`Kafka`的主要內容,我就不再深入細節了。?`LinkedIn`方案的更細節說明在[這兒](http://sites.computer.org/debull/A12june/pipeline.pdf),`Kafka`設計的詳細說明在[這兒](http://kafka.apache.org/documentation.html#design),你可以讀一下。