天天看點

《大資料系統建構:可擴充實時資料系統建構原理與最佳實踐》一1.2 擴充傳統資料庫

我們将從許多開發人員的“起點”開始大資料的探索,直擊傳統資料庫技術的局限性。

假設上司的要求是建構一個簡單的網絡分析應用程式。這個應用程式能追蹤客戶期望追蹤的任何統一資源定位符(uniform resource locator, url)的頁面浏覽量。每接收到一次頁面浏覽,客戶的網頁就用其url ping應用程式的web伺服器。此外,應用程式在任何時候都能根據頁面浏覽量給出前100排名的url。

首先啟動一個如圖1-1所示的頁面浏覽量的傳統關系型模式。其後端包括一個該模式的表的rdbms和一個web伺服器。每當有人加載被應用程式追蹤的網頁時,這一網頁帶着頁面浏覽ping web伺服器,同時web伺服器會在資料庫中增加相應的行。

《大資料系統建構:可擴充實時資料系統建構原理與最佳實踐》一1.2 擴充傳統資料庫

讓我們看看在完善應用程式時出現了什麼問題。—如你所見,這裡會遇到可擴充性和複雜性問題。

1.2.1 用隊列擴充

網絡分析産品獲得了巨大的成功,應用程式的流量正像野火一樣增長。例如,公司舉辦了一個盛大的派對,但慶祝時,你開

始從監控系統收到大量的電子郵件。這些郵件都在說同樣的事情—“插入資料庫時發生逾時錯誤。”

你檢視了日志,問題很明顯—資料庫跟不上負載,導緻增加頁面浏覽量的寫請求逾時。

你需要盡快做些事情來解決這個問題。你會意識到每次隻執行一次增量操作到資料庫是很浪費的。如果你在單個請求中批處理多個增量操作,這樣就可以更有效。是以你重構後端,使這一切成為可能。

所謂重構後端,不是讓web伺服器直接通路資料庫,而是在web伺服器和資料庫之間插入一個隊列。當你收到一個新的頁面浏覽後,該事件被添加到隊列中;然後建立一個一次從隊列中讀取100個事件的工作程序,并在單個資料庫更新操作中批量插入它們,如圖1-2所示。

《大資料系統建構:可擴充實時資料系統建構原理與最佳實踐》一1.2 擴充傳統資料庫

這個方案執行得很好,它解決了逾時問題。它甚至還有額外的好處—如果資料庫再次超載,隻會使隊列變得更大,而不會導緻web伺服器逾時和潛在的資料丢失。

不幸的是,添加隊列并做批量更新隻是可擴充性問題的一個“Ok繃”。随着應用程式日漸受歡迎,資料庫會再次超載。現有的工作程序跟不上寫操作的速度,是以你嘗試添加更多的工作程序來并行化更新。不幸的是,這并未起到多大的作用—顯然,資料庫是瓶頸。

使用google搜尋如何擴充寫操作頻繁的關系型資料庫,你會發現最好的方法是使用多個資料庫伺服器,并在所有伺服器上分散該表,使每個伺服器擁有該表所包含資料的一個子集。這種方式被稱為水準分區或分片。這種技術通過多個機器分散寫操作的負載。

這裡使用的分片技術是通過分片的數量對鍵的散列值進行取模,以此為每個鍵選擇分片。使用散列函數将鍵映射到分片,可以使鍵均勻分布到所有分片。接下來寫一個腳本來映射單個資料庫執行個體中的所有行,并把資料分割成四個分片。這個腳本需要一段時間來運作,是以你要關掉增加頁面浏覽的工作程序,以便讓該腳本完成運作,否則在轉換時會丢失頁面浏覽的增量。

最後,所有應用程式代碼需要“知道”如何為每個鍵找到分片。這就需要将用于從配置檔案中讀取分片數量的資料庫處理代碼封裝成一個庫,并且重新部署所有應用程式代碼。你必須修改前100個url的查詢,從每個分片中擷取前100個url并将它們合并,用來獲得全局的前100個url。

随着應用程式變得越來越流行,使用者不得不将資料庫重新切分成更多的分片,才能跟得上寫操作的負載。每次重新分片會讓你覺得越來越痛苦—因為有很多工作程序需要協調,而且不能隻運作一個腳本進行重新分片,那樣速度會很慢。你必須并行地重新分片,并且同時管理大量的活躍工作程序的腳本。如果開發者忘記更新應用程式代碼與新的分片數量,則會導緻大量的增量操作寫入錯誤的分片,是以必須編寫一次性腳本來手動檢查資料和移動任何錯位的資料。

最終,系統中有如此多的分片,以至于其中一台資料庫機器的硬碟頻出故障。當計算機當機時,這部分資料是不可用的。解決這個問題的方法如下:

更新隊列或工作程序系統,将不可用分片的增量操作放入一個單獨的“等待”隊列,且試圖每5min重新整理一次“等待”隊列。

使用資料庫的複制功能為每個分片添加一個從分片,是以這樣就會有一個備份以防主分片出現故障。雖然客戶不在從分片中執行寫操作,但至少還可以在應用程式中檢視狀态。

開發者會有這樣的想法:“早期我花費時間為客戶建構新功能,現在看來我隻有花費所有時間來處理讀寫資料的問題了。”

當運作隊列或工作程序代碼時,開發者在生産環境中不小心為每個url部署了一個錯誤的網頁浏覽量的增量(為2,而不是1),直到24h後才意識到這個錯誤,但已經造成了損壞。因為無法知道哪些資料被損壞,是以每周的備份起不到幫助作用。雖然這種方式試圖使系統具備可擴充性和對機器故障的可容忍性,但無法使系統具備對人為錯誤的應對方式。無論你怎樣努力試圖阻止錯誤的産生,它都将不可避免地在生産環境中出現。

随着簡單網絡分析應用程式的發展,系統不斷變得越來越複雜:隊列、分片、副本、重新切分的腳本等。在資料上開發應用程式,不僅需要知道資料庫模式,還需要知道更多的東西。代碼需要知道如何與正确的分片通信,如果出錯,從錯誤的分片進行讀或寫操作将是不可避免的。

還有一個問題是,資料庫對它的分布式特性不是自我感覺的,是以它不能幫客戶處理分片、副本和分布式查詢。這種複雜性在操作資料庫和開發應用程式代碼的部分中都會存在。

但最嚴重的問題是,系統并不是針對人為錯誤設計的。恰恰相反,實際上,随着系統變得越來越複雜,它越來越有可能産生錯誤。軟體中的錯誤是不可避免的,如果不是在研發時犯錯,則可能是寫了任意破壞資料的腳本。備份無疑是不夠的,開發者必須仔細考慮系統的設計方法,以降低人為錯誤可能導緻的損害。容忍人為錯誤不是随意的,尤其在大資料給建構應用程式添加那麼多複雜性時它更加必要。

我們所要學習的大資料技術,将以令人矚目的方式解決這些可擴充性和複雜性問題。首先,為大資料所使用的資料庫和計算系統是可以感覺到自己的分布式特性的,是以可以幫助客戶處理諸如分片和備份這些事情。使用者不會遇到不小心查詢了錯誤的分片的場景,因為這種邏輯是内化在資料庫中的。擴充時,使用者隻需添加節點即可,系統将會自動重新調整到新的節點。

我們将了解的另一個核心技術是“使資料不可變”,即不是存儲頁面浏覽量作為核心資料集,而是要存儲原始網頁浏覽資訊。因為随着新頁面浏覽的傳入,存儲頁面浏覽量需要使用者不斷地改變資料集。而原始網頁浏覽資訊是從來不會改變的。是以當出現錯誤時,你可能會寫入損壞的資料,但至少不會破壞良好的資料。基于資料變化方面,這是一個比傳統系統更為強大的容忍人為錯誤的保證。對于傳統的資料庫,使用者應謹慎使用不可變資料,因為這樣的資料集将以很快的速度增長。但由于大資料技術可以擴充到如此多的資料,開發者就可以用不同的方式設計系統。