有贊億級訂單同步的探索與實踐
文 | 王啟 on 訂單
一、引子
有贊是提供商家 SAAS 服務,隨著越來越多的商家使用有贊,搜尋或詳情的需求也日益增長,針對需求及場景,之前提到過的訂單管理架構演變及AKF 架構等在這兩篇文章裡已經有所體現,而這些資料的查詢來自於不同的 Nosql,怎麼同步這些非實時儲存系統將是一個很有趣的事情。
1.1 同步現狀
當前有贊訂單同步流程及業務現狀如圖所示,採用了 ES+HBase(tip1)架構體系去解決搜尋和詳情的需求,利用 canal ( tip2 ) 將資料庫變更寫入到 mq 中,然後利用同步系統來解決相關資料同步問題,而後續下文中將敘述有贊訂單同步面臨的問題及應對方案。
二、同步
2.1 實現同步基礎 - 單表同步
2.1.1 亂序問題
單表同步如圖所示:
業務場景中,在這任何一個序號鏈路中,如果併發出現同一主鍵的兩條訊息,同時在 Nosql 中沒有做版本控制,都有可能造成訊息亂序問題,而相反,如果通過順序解析 binlog 的同時,為每條 statement sql 執行的結果分配一個順序 SeqNo,該 SeqNo 保證有序(tip3),再通過 Nosql 中控制樂觀鎖就可以解決順序性問題。
2.1.2 HBase 同步
Hbase 同步相對來說比較簡單,Hbase 內部擁有 timestamp 協助控制每個 qualify 的版本,只要讓 timestamp 傳入上文所說的順序 SeqNo,那麼就可以保證每個欄位讀取出來的資料是最終一致性的。
2.1.3 ES 同步
ES 同步針對單表場景可以通過 index 的操作來進行寫入,index 可以採用 exteneral 版本也稱作外部版本號來進行控制,同樣適用上述的 SeqNo 來做樂觀鎖去解決該問題。
2.1.4 同步過程圖
2.2 實現同步進階 - 多表同步
2.2.1 亂序問題
多表同步基於單表同步的基礎上做相關的同步,如圖所示:
當資料庫的兩張表(不關心是否在一個例項上,如果不在,還更慘,SeqNo 還不一定能夠保證有序)觸發了更新操作,假設 t1 生成 binlog 的 SeqNo 小於 t2 生成binlog 的 SeqNo,若 t1 這條訊息因序號鏈路中的網路抖動或其它原因造成消費晚於 t2,也就是 t2 的 binlogSeq 先寫入 Nosql 中,那麼就會造成一個 t1 的資料無法寫入到 Nosql 中。
2.2.2 HBase同步
其實上述多表同步亂序的問題並不是絕對的,針對 Hbase 這種自帶列版本號的將會自動處理或丟棄低版本資料,同時針對這種情況,設計成每個 table 表中的欄位都會列入到 Hbase 中。舉個例子,針對訂單的情形存入訂單主表和訂單商品表
場景1:1
針對訂單主表,我們寫入的資料以訂單號做 hash,然後以 hash 值:訂單號作為主鍵降低熱點問題,同時定義單 column family,qualifiy 格式為 表:欄位 value為對應的 value 值,timestamp 為 SeqNo,如圖所示:
場景1:n
針對訂單商品表,我們寫入的資料同樣以訂單號生成相應的 rowkey,同時定義單 column family,qualifiy 格式為 表:欄位:對應記錄的 id 值 value 為對應的 value 值,timestamp 為 SeqNo,如圖所示:
通過上述寫入,能夠針對具體到某個欄位都有對應的 timestamp 值的更新,為後期寫入更新資料能夠更新到具體欄位級別。
2.2.3 ES同步
針對上面的同步亂序問題,ES 沒有 HBase 這種列版本號,ES 只有 doc 級別的 version,如果上述真的出現 SeqNo2>SeqNo1,且 SeqNo2 早於 SeqNo1 寫入到 ES 中,則就會出現 SeqNo1 的內容無法寫入,也就會造成順序不一致的情況。那如何去解決這個多表同步問題呢?
既然會亂序,那讓它有序就好了,資料保證有序不就能夠解決這個事情嘛,讓整個鏈路有序也就代表 canal 消費 binlog 資料保證有序且丟到 MQ 中有序,MQ 然後保證順序投遞到 Sync 消費處理程式中,通過消費一條訊息然後 ack 告訴 MQ 是否成功,已達到保證所有資料全部有序(若多執行緒或多機器處理 MQ 中的多個分割槽都是會存在問題)。如圖所示:
如此只要保證 t1 表的資料和 t2 表中的資料在 ES 不互相關聯,每個資料寫入的時候按照 update 方式寫入(如果不存在需要做一次 create 操作),這樣就能保證所有資料按照順序執行。
2.3 配置化同步
上文已經講到資料同步是由一個數據源(這個資料來源可以來自於 MQ、Mysql 等)同步到另外一個數據源(Mysql、ES、HBase、Alert 等),也就是一個管道的過程。借鑑了一下 logstash 官網,同樣處理流程分為 input、filter、output 元件,這些流程稱之為 task 任務,如圖所示:
通過這些元件,抽象化出每個元件都有對應的配置,由這些配置來進行初始化元件,驅動元件去執行流程。簡單來說,只需要在頁面中配置一些元件,無需開發任何一行程式碼就能實現同步任務。如圖所示:
通過一系列的配置,就能配置出一個任務,針對業務邏輯,可以採用動態語言 groovy 來實行指令碼化處理(複雜業務場景可以通過 UDF 函式來做支援),針對 mqinput 拿到的欄位然後經過處理,經過過濾 filter 等,可以直接拿到相關的資料進行組裝,然後配置化的寫入到 ES 中,無需開發任何一行 java 程式碼即可實現流程自動配置化,針對複雜的需求也能夠高效率支援。配置介面如圖所示:
2.3.1 效能瓶頸
上述就能解決 ES 多表同步的問題,但是同樣會存在一些問題:
-
效能瓶頸問題
-
失敗堆積問題
效能瓶頸:比如寫入量超級大的場景情況下,而 Sync 消費程式只能針對 MQ 中的分割槽(kafka 的 partition 概念)消費,每個分割槽只能有一個執行緒去執行,消費速率與消費分割槽成正比,與消費 RT 成反比,尤其是大促場景下就會造成資料消費不過來,資料堆積嚴重問題。
失敗堆積:因為是順序消費,只要某個分割槽的某條訊息消費失敗,後續訊息就會全部堆積,造成資料延遲率超高。所 以建議用順序佇列的場景除非是業務量沒有效能瓶頸的情況下可以採取使用,而怎麼去解決順序佇列或者去掉順序佇列呢?
用順序佇列無非就是保證有序,因為 ES 沒有 HBase 的欄位級別版本號,目前訂單採用的是用 HBase 做一層中間處理層,解決該問題,如圖所示:
通過藉助 HBase 欄位級別版本號幫助每個表保證表內部欄位有序,同時 put 寫入完資料之後,通過額外欄位 version 做 increment 操作,當這兩個寫入動作完成之後立馬 get 操作拿到 HBase 的資料寫入到 ES 中,無論併發程度如何,最終至少有一次的 get 請求拿到的版本 version 欄位是最大的,用該 version 作為 ES 的外部版本號解決 ES 版本號問題。
用此方案會有好處:
-
HBase 協助管理內部欄位版本,同時根據內部操作,協助 ES 拿到對應的版本,且資料能拿到最新資料;
-
去掉了順序佇列,HBase 具有良好的吞吐,相對於順序佇列擁有更大的吞吐量;
-
橫向拓展增大消費速率;
-
ES 可以採用 index 操作,效能更好。
當然也有弊端:HBase 存在抖動的情況,以及主備切換問題。
因為存在抖動或者準備切換問題,會造成資料不一致,我們該怎麼去解決這個事情呢?
2.4 未來擴充套件
目前訂單同步是通過載入配置檔案形式來做的,也就是橫向拓展的機器都會去載入同一份配置檔案,各個任務通過異常解耦,理論上不會有影響,但是會存在載入任務的重要度的問題。
舉個例子:
-
我需要一臺機器臨時去消費資料解決線上問題;
-
有個量級很大但又不是很重要的任務,想不影響其他任務的進行;
-
要做對比,增量延遲對比或全量對比資料,但又不希望影響其他資料;
-
查詢日誌需要所有機器檢視查詢(當然,公司有內部日誌系統,可直接上去檢視) 如此,可以讓同步系統無狀態化,每個任務的配置載入有任務配置平臺來進行配置,指定相關的機器做相關的處理,擴容也可以動態申請擴容,如圖所示,可以自由分配機器處理不同的任務。
三、一致性保障
上文講了有贊在處理訂單的時候怎麼講資料同步到 ES 或 HBase,資料來源於 binlog,寫入到 MQ,也就是說處理的來源來自於 MQ。簡單一句話來講:我們不生產訊息,我們是訊息的搬運工。“搬運工”的角色可以做一些事情,同樣有贊在處理資料對比也是如此,這章講講“搬運工”可以做什麼:
3.1 資料對比
上述一般情況下不會出問題,那如果出問題了怎麼辦,需要做資料對比,而資料來源就是我們剛剛拋棄的順序佇列,順序佇列有個缺點就是堆積,同樣我們也可以利用堆積的特性,讓其第一條訊息堆積十分鐘,那麼後續訊息基本上也會堆積十分鐘,然後就可以消費這個訊息進行資料拉取,拿到最新的資料進行資料對比,如圖所示:
通過對比結果傳送到 alert 中,就可以知道哪些資料不一致,頻率多少,這也是一種同步(mq->filter->alert)!
3.2 全量對比/資料刷入
上述我們講到資料同步到 Nosql 中,但是隻是講了增量的一個過程,涉及到歷史資料,就需要對歷史資料進行遷移,同樣,這也是一種資料同步,後面將會出相關博文怎麼去做全量資料同步。
四、Tips
Tip-1: 為什麼採用 ES + HBase 處理搜尋和詳情?
一般情況下,公司達到一定規模,有類似全文檢索的需求或者高頻 key:value 的時候,大家會推薦 ES+HBase 的架構體系去完成搜尋和詳情的需求,而現實中,絕大多數情況下生產環境不會將資料直接寫入到 ES 或者 Hbase,大家都會優先寫入資料庫,不進行雙寫的操作是因為增加鏈路影響業務。當然 Hbase 可能還好一點,ES 本身就是非實時查詢系統(為什麼是非實時,有興趣的可以去看看ES讀寫流程),這種情況下也造就了 ES 和 HBase 的一個準實時系統。針對業務來說,準實時是可以滿足相關需求的,比如商家搜尋訂單並不要求實時。
Tip-2:為什麼有贊選 canal 解析 binlog,而不是採用業務訊息進行資料同步?
-
資料表被更改,比如修資料情況,業務訊息不會觸發,導致無法寫入到對應的 Nosql 中造成。資料不一致
-
順序性問題無法得到相關保障;
-
業務訊息並不能拿到所有相關的資料進行寫入到 nosql 中。
Tip-3: SeqNo 實現方式,為什麼不用 binlogoffset?
因為 cana 例項與 mysql 例項是 1:N(推薦 1:1),而大部分業務場景同一種資料一般會落在同一個例項上,canal 就可以通過該臺例項的時間與每秒處理的個數相結合。如:timestamp*10000+counter++,而不用 binlogoffset 的原因是 mysql 的例項掛了話,binlogoffset 可能會亂序。
五、結語
有贊交易訂單管理承接了億級流量的同步任務,面臨著眾多的需求挑戰,從最開始的mysql到如今的產品化的同步任務,從單表同步到多表同步,從單索引到多索引,從增量到全量,都有不同的解決之道,如今新興搜尋中臺更是承接億級搜尋和同步流量,如有興趣,歡迎加入,我們一起共同探討。
拓展閱讀
活動預告
4月27日(週六)下午13:30
有贊技術中介軟體團隊聯合Elastic中文社群
圍繞Elastic的開源產品及周邊技術
在杭州舉辦一場線下技術交流活動
本次活動免費開放, 限額兩百名
點選檢視原文,即可報名
也可掃描下圖二維碼,回覆“報名”參加
-The End-
Vol.178
有贊技術團隊
為 442 萬商家,150 個行業,330 億電商交易額
提供技術支援
微商城|零售|美業 | 教育
微信公眾號:有贊coder 微博:@有贊技術
技術部落格:tech.youzan.com
The bigger the dream,
the more important the team.