Schemaless架構(三):Uber基於MySQL的Trip資料庫
本文介紹Schemaless的主要功能:Schemaless trigger的細節與案例。本文是系列文章的第三部分;第一部分是關於Schemaless的設計,第二部分是討論其架構。
Schemaless trigger是一項具有可擴充套件性、容錯性和無損性的技術,監聽Schemaless例項中的變更。在行程(trip)流程中起到引擎的作用,從司機按下“結束行程”並向系統提交費用,直到相應資料進入資料庫等待分析。在Schemaless系列的最後一篇中,我們將深入講解Schemaless trigger的功能,以及如何開發出這個可擴充套件的容錯系統。
簡單來說,在Schemaless資料的基本單位被命名為單元(cell)。它是不可變的,一旦寫入,便無法被覆蓋。(在特殊情況下,我們可以刪除舊記錄);單元可以被行鍵(row key)、列名(column name)和引用鍵(ref key)來引用;單元內容透過編寫引用鍵更高的新版來執行更新,但行鍵和列名保持不變。Schemaless不對其中儲存的資料執行任何操作(故而命名schemaless)。從Schemaless的觀點來看,它只負責儲存JSON物件。
Schemaless Trigger案例
我們來看一下實踐中Schemaless trigger的運作方式。下面的程式碼是簡化版的非同步計費方式(大寫標註Schemaless的列名)。案例Python程式碼:
#我們例項化一個客戶端,以便與Schemaless例項通訊 schemaless_client = SchemalessClient(datastore=’mezzanine’) #為BASE列註冊一個bill_rider功能 @trigger(column=’BASE’) def bill_rider(row_key): # row_key是行程的UUID status = schemaless_client.get_cell_latest(row_key, ‘STATUS’) if status.is_completed: #也就是說我們已經提交了乘客的賬單 return #否則就嘗試提交賬單 #我們從BASE列拿到了基本行程資訊 trip_info = schemaless_client.get_cell_latest(row_key, ‘BASE’) #提交乘客賬單 result = call_to_credit_card_processor_for_billing_trip(trip_info) if result != ‘SUCCESS’: #提交例外,讓Schemaless trigger稍後重試。 raise CouldNotBillRider() #成功提交乘客賬單,寫入Mezzanine schemaless_client.put(row_key, status, body={‘is_completed’: True, ‘result’: result})
在Schemaless例項中,我們在函式中透過新增decorator@trigger來定義trigger,並指定列。如果指定列的單元中有內容,通知Schemaless trigger框架呼叫函式——本例是bill_rider。這裡透過BASE中的一個新單元表明行程結束。觸發trigger,然後透過函式來傳送行鍵——本例是行程UUID。如果需要更多資料,必須從Schemaless例項——本例是從行程儲存Mezzanine中獲取真實資料。
bill_rider trigger函式的資訊流見下表(這裡是乘客結賬)。箭頭方向指明呼叫方與被調方,旁邊的數字指明流程的順序:
首先將行程輸入Mezzanine,Schemaless Trigger框架呼叫bill_rider。在呼叫時,函式向行程儲存請求STATUS列的最新資訊。本例中is_completed欄位不存在,也就是說乘客尚未結賬。然後獲得BASE列的行程資訊,透過函式呼叫信用卡provider來結賬。在本例中,我們成功用信用卡付費,並返回成功資訊到Mezzanine,然後設定STATUS列的is_completed為True。
Trigger框架確保在每個Schemaless例項中的每個單元至少呼叫bill_rider一次。一般來說只觸發trigger函式一次,不過在出錯的情況下(無論是trigger功能還是其他功能短暫出錯),都可能需要多次呼叫該函式。也就是說trigger函式是冪等的,在本例中要檢查單元是否處理完畢。如果答案為是,則返回函式。
在檢視下文中Schemaless如何在流程中提供支援時,要記得這個案例。我們將會解釋Schemaless如何被看作變更日誌,並討論與Schemaless相關的API,分享讓流程支援可擴充套件和可容錯的技術。
將Schemaless視為日誌
Schemaless包含所有單元,也就是說包含指定行鍵、列keypair的所有版本。由於包含單元的所有歷史版本,除了隨機訪問key-value儲存外,Schemaless還可作為變更日誌。事實上它就是一個分割槽日誌,每個分片都是自己的日誌,如下圖:
根據行鍵(也就是UUID)將每個單元寫入特定的分片。分片中的所有單元都有唯一識別符號,稱為新增ID。新增ID是一個自動遞增的欄位,代表著單元的插入順序(越新的單元,新增ID的數字越大)。除了新增ID之外,每個單元都有單元寫入的時間(datetime)。在所有分片備份中,單元的新增ID是唯一的,這點對於故障時轉移非常重要。
Schemaless的API支援隨機訪問和日誌類訪問。隨機訪問API是針對單獨的單元,均由row_key、column_key和ref_key一同定義。
Schemaless還包含這些API端點的批處理版本,這裡省略。之前說過的trigger函式bill_rider就使用這些函式來獲取並操縱單個單元。
對於日誌類訪問API,我們關心單元的分片數字與時間戳以及新增ID(合稱位置location):
與隨機訪問API類似,日誌訪問API有更多可用的knob,實時從多個分片中抓取單元,不過上面的端點更為重要。位置可以是timestamp或added_id。呼叫get_cells_for_shard,除了單元之外,還返回下一個新增ID。例如,如果呼叫位置1000的get_cells_for_shards,請求10個單元,返回的下一個位置偏移是1010。
追蹤日誌
透過日誌類訪問API,可以追蹤Schemaless例項,就像可以在系統中追蹤檔案一樣(比如tail -f),或者類似最新變更輪詢的事件佇列(比如Kafka)。然後,客戶端持續追蹤偏移,並將其用在輪詢中。要想引導追蹤程式,需要從第一條開始(比如位置0),或從任何時間,或偏移後。
Schemaless trigger透過使用日誌類訪問API完成相同的追蹤,並保持追蹤偏移。輪詢API的好處直接表現在,透過Schemaless trigger讓這個過程具有可擴充套件性與容錯性。透過配置從哪個Schemaless例項、哪一列開始輪詢資料,將客戶端程式與Schemaless trigger框架連結。使用的函式或回撥與框架中的資料流相關,在新單元格插入例項時透過Schemaless trigger或呼叫或觸發。反過來,透過框架在程式所執行的主叢集中找到要找的工作程式。框架將工作分到可用程式中,然後透過將分到故障程式的工作分配給其他可用程式,巧妙地解決出現故障的程式。work分配代表著程式設計師只用編寫處理程式(比如trigger函式),並確保它是冪等的。剩下的交給Schemaless trigger來處理。
架構
在這部分中,我們會討論Schemaless trigger如何擴充套件,如何將故障影響最小化。下圖從較高角度展示了其架構,取自之前的賬單結算服務:
賬單結算服務使用了執行在三臺不同主機上的Schemaless trigger,我們(簡單起見)假設每個主機只有一個工作程式。Schemaless trigger框架區將分片按工作程式區分開,因此每個工作程式只負責處理一個特定的分片。注意:工作程式1從分片1拉取資料,工作程式2從分片2和分片5拉取資料,工作程式3從分片3和分片4拉取資料。一個工作程式只處理指定分片的單元,抓取新單元、為這些分片呼叫註冊的回撥函式。一個工作程式就是指定的leader,負責向工作程式分派片區。如果程式掛起,leader將為故障程式分配的片區重新分配給其他程式。
在一個分片中,單元都是以寫入順序來觸發。也就是說如果特定單元的trigger總是由於程式錯誤而出現故障,就會阻礙該片區的單元處理。為了避免延遲,可以配置Schemaless trigger來標記多次出錯的單元,並將它們放在單獨的佇列中。之後,Schemaless trigger就會繼續下一個單元的處理。如果標記單元的數字超過了特定閾值,trigger就會停止。通常代表著系統錯誤,需要人工修復。
透過儲存每個片區中最近一次成功觸發單元的新增ID,Schemaless trigge繼續保持追蹤。該框架將這些偏移儲存到共享儲存中,比如Zookeeper或Schemaless例項自身,也就是說如果程式重啟,trigger就會繼續從儲存片區的儲存偏移開始執行。共享儲存也用在meta-info中,比如協調選出leader,探知新增或移除的工作程式。
可擴充套件性與容錯性
Schemaless trigger是為可擴充套件而設計的。在被追蹤的Schemaless例項中,對於任意客戶端程式,我們能夠新增最多與片區數量一致的工作程式(通常是4096)。此外,我們能夠線上新增或移除worker,來獨立處理Schemaless例項中其他trigger客戶端的變動負載。透過在框架中追蹤進度,我們可以為要傳送資料的Schemaless例項新增儘可能多的客戶端。在伺服器端並沒有邏輯來持續追蹤客戶端或者將狀態推送過去。
Schemaless trigger也是容錯的任何程式故障都可以不影響系統。
-
如果一個客戶端worker的程式出錯,leader會將這個work重新分配,確保所有片區都有程式。
-
如果Schemaless trigger節點上的一個leader出錯,會有新的節點被選成leader。在leader選舉期間,可以繼續處理單元,不過work不能執行重分配工作,也無法移除和新增程式。
-
如果分片儲存(比如ZooKeeper)出錯,單元程式持續進行。不過就像在leader選舉期間一樣,work無法執行重分配工作,而在分片儲存出錯時程式也無法變更。
最後,在Schemaless例項中,Schemaless trigger框架是不可能出現故障的。任何資料庫節點出錯都沒關係,因為Schemaless trigger可以從備份讀取。
總結
從運營角度來看,Schemaless trigger非常好用。由於透過隨機訪問API或者日誌類訪問API都可以訪問資料,Schemaless是理想的真實資料來源儲存庫。透過Schemaless trigger最後,我們可以在執行時新增更多的儲存伺服器,來增加資料容量與效能。如今,Schemaless trigger框架驅動著整個行程處理流,包括分析資料庫中的,以及跨資料中心複製後的。我們對未來的前景倍感興奮。
原文連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26916835/viewspace-1993867/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Schemaless架構(二):Uber的Trip資料庫架構資料庫
- mysql資料庫的基礎架構MySql資料庫架構
- MySQL資料庫架構——高可用演進MySql資料庫架構
- 資料庫 Mysql 邏輯架構簡介資料庫MySql架構
- EF架構~基於EF資料層的實現架構
- 基於Centos7.x平臺搭建PhxSQL+Atlas+MySQL高可用資料庫架構CentOSMySql資料庫架構
- Uber實時資料基礎設施:分散式計算架構分散式架構
- ES資料庫架構資料庫架構
- 阿里架構師,講述基於微服務的軟體架構模式(附資料)阿里架構微服務模式
- 基於.NET的LINQ to SQL 三層架構開發之架構建立SQL架構
- Uber基於Apache Hudi構建PB級資料湖實踐Apache
- 基於Oracle的私有云架構探析(連載三)Oracle架構
- 基於AI的資料架構:業務在前,協作在後AI架構
- 熱璞資料庫HotDB 基礎架構詳解資料庫架構
- MySQL基礎架構MySql架構
- MySQL 基礎架構MySql架構
- 資料庫系列——基於Canal實現MySQL增量資料同步資料庫MySql
- mysql資料庫-資料結構MySql資料庫資料結構
- 基於Hibernate的圖片資料庫儲存(mysql)資料庫MySql
- orientDB學習筆記(三)資料庫 構架設計筆記資料庫
- MySQL資料庫實現高可用架構之MHA的實戰MySql資料庫架構
- NUMA 架構與 資料庫架構資料庫
- 架構與資料庫的關係架構資料庫
- 大資料架構和模式(三)——理解大資料解決方案的架構層大資料架構模式
- Kubernetes 架構及基礎概念架構
- 基於Docker部署Oracle、MySQL等資料庫的資料檔案持久化DockerOracleMySql資料庫持久化
- 窺探QQ基礎資料庫架構演變史資料庫架構
- 基於gin的golang web開發:訪問mysql資料庫GolangWebMySql資料庫
- 基於Go語言構建的萬億級流量大資料平臺架構Go大資料架構
- mysql資料庫基本操作(三)MySql資料庫
- MySQL基礎架構分析MySql架構
- MySQL之基礎架構MySql架構
- MySQL資料庫之網際網路常用架構方案(全)MySql資料庫架構
- 資料庫開發(19)基於物件的資料庫資料庫物件
- 基於 EventBridge 構建資料庫應用整合資料庫
- 基於bin-log&position搭建主從架構MySQL架構MySql
- 關於Mysql索引的資料結構MySql索引資料結構
- Greenplum資料庫系統架構資料庫架構