TiDB 在 OPPO 準實時數據倉庫中的實踐

PingCAP

  •  1.3k

TiDB 在 OPPO 準實時數據倉庫中的實踐

數據庫實時分佈式

發佈於 1月6日   約 24 分鐘

本文轉載自微信公衆號“OPPO大數據”。

作者介紹:OPPO 數據分析與解決方案團隊主要負責 OPPO 全集團的大數據分析和解決方案提供,團隊成員多來自一線互聯網公司及著名高校,在 OPPO 衆多場景的大數據應用方面有很深經驗,極大的支撐了業務迅速發展。

文章具體作者:羊歡,代凱,柳青,陳英樂。

OPPO 大數據中心在 2019 年初承接了接入某業務線核心數據的重要任務:一期目標是建立一個能提供準實時大數據查詢服務的數據倉庫。我們選用了之前從未在公司大規模正式使用過的 TiDB 作爲核心數據庫引擎。本文記錄這次吃螃蟹的一些經驗和教訓,供大家參考。

前期工作

核心挑戰

經過需求調研階段,我們發現面臨以下核心的挑戰:

  1. 大數據能力支持。從業務數據量看,當前雖然尚在 TB 級別,但增長速度非常快,業務本身有進行全量整合分析查詢的需求。
  2. 數據接入困難。數據分散且多樣,跨國,多種 DB 類型,多網絡環境,接入難度較大。
  3. 數據變動頻繁。核心數據存在生命週期,在生命週期內變動頻繁,這與互聯網的核心數據一旦生成就不再變化有較大不同。
  4. 服務實時性較高。數據整合的速度和查詢結果越實時,對業務價值就越大。

現有技術架構體系

公司數據中心目前承載着公司各業務系統積累的數據。

數據倉庫方面的技術體系:

  • 離線數據的存儲和應用架構是主流的 Hadoop+Hive/Spark/Presto。
  • 實時數據服務則基於 Kafka/Flink/SparkStreaming 等流行的框架。

離線數據平臺可以提供 T+1 及小時級別的數據計算服務;而實時數據服務主要適用於互聯網應用場景,即大多行爲數據生成後不再發生變化。這也是業界非常典型的基礎技術架構。

技術選型考量

一開始我們打算採用業界常用的辦法,即利用數據中心現有的基礎設施開發出離線和實時兩套體系的數據並進行整合後提供給報表查詢接口。但其實這個方案其實有一個最致命的問題:大部分此業務數據在完整的生命週期裏是經常發生變動的。而項目裏一個重要的需求是要能近實時(最多半個小時)的查詢出結果。離線任務運行後的結果很可能很快就失效了,需要重新計算。而離線計算耗時較長,根本無法滿足準實時需求;如果把大部分計算交給實時引擎,也要進行較爲複雜的代碼設計和框架的修改適配。

事實上我們已經做好了服務降級的打算。我們面臨困境的實質是接入頻繁變動的行業數據對於主要源自互聯網的大數據技術體系是一種新的挑戰。因此我們繼續不斷的尋找更好的方案。我們的目標是找到具有以下特點的體系:

  1. 能近實時的對所有層級的數據進行更新(主要是原始數據和各層聚合數據)。
  2. 秒級的查詢性能。
  3. 不再有實時和離線的界限,只需要同一套代碼。
  4. 方便的存儲擴展,支持大數據。
  5. 較低的技術棧要求。

在這種背景下,我們關注到了已經在 OPPO 內部進行着少量測試(作爲備份從庫等)的 TiDB。它的主要特點及帶來的好處是:

  1. 完全兼容 MySQL 協議。低技術棧,在合理的索引設計下,查詢性能優異到秒級出結果;對小批量的數據的更新和寫入也相對優秀。
  2. 水平彈性擴展。能支持大數據存儲,擴容成本僅爲機器成本。
  3. 支持大數據情況下的複雜查詢(TiSpark 組件可使用 Spark 引擎)。
  4. 可用性高。Raft 協議保證數據強一致且在不丟失大多數副本的前提下能自動恢復。
  5. 完全開源,社區活躍。開源約 4 年,GitHub Star 數 2 萬,Fork 數 3 千。根據官方數據:截止 19 年 8 月,已經有約 3 千家企業建立了規模不一的測試集羣,500 家企業有線上集羣,其中包括數家銀行(北京銀行,微衆銀行)的核心交易系統。網絡上也能看到衆多一線互聯網公司的案例分享。
  6. 作爲 HTAP,未來將可以方便的對接 TP 類系統。當前離線架構的數據經常需要再次出庫到諸如 MySQL 庫裏以便 TP 系統快速讀取,無疑增加了系統複雜度。HTAP 將交易和分析系統的界限消除,交易數據生成即可用於分析,而分析結果生成後即可以用於交易,沒有了 ETL 過程,非常便利,而且讓 IT 架構邏輯更近似業務邏輯。

對於這次的項目來說,我們最看重的三點是:

  1. 可以很方便的支持數據頻繁更新。
  2. 優秀的查詢響應速度。
  3. 支持方便的無限擴容。

由於並不可見的重大缺陷,且閱讀了許多比此次項目數據量級大得多的成功案例,我們正式開始了吃螃蟹的征程。

實踐過程

項目架構和實施

項目一期的架構和實施相對簡單。主要是集羣建設+數據同步+模型建設+任務調度。下面簡要介紹一下。

集羣建設

TiDB集羣的架構圖及部署文檔參考官方網站即可,不再贅述,以下是項目配置供參考:

關於存儲官方推薦採用 NVME SSD,這樣能最大發揮 TiKV 的 IO 能力 。目前由於某些原因,暫時退而求其次採用 SATA SSD,通過把磁盤分成 2 組 TiKV 數據盤,每一組 3 塊盤做 RAID0,最後剩餘 2 塊盤做 RAID1 作爲系統盤,將磁盤 IO 能力提升。然後每組數據磁盤上部署一個 TiKV 節點。TiDB 的部署採用官網推薦的 TiDB Ansible 部署方式,這裏不再贅述,大家可以去 PingCAP 官網查看。

數據同步

項目採用了定期(每 10 分鐘,可調整)調度 Python 腳本以實現增量抽取數據。源數據庫是 Oracle/SQLServer,目標數據庫是 TiDB 集羣。數據同步腳本是自研的,代碼簡潔但非常強大,核心是採用 pyodbc 開源庫,並具有以下特點:

  1. 支持多種數據目標/源 DB,豐富的自定義 DDL 支持(包括自動建表,添加字段註釋,自定義字段處理),自定義抽取 SQL(既可以完整同步數據,亦可以同步前就進行一些預處理,靈活性強)。
  2. 便捷的讀寫併發量控制(讀寫依賴數據隊列溝通,還可以平衡數據源併發查詢壓力及目標庫的寫壓力,以及歷史數據同步)。

同步腳本要求有增量抽取的控制字段,比如 update_time 等,一般規範的表設計均能滿足,但項目中確實遇到一些因歷史原因導致我們不得不進行全表覆蓋同步,部分表還存在“硬刪除”的情況 。最後通過開發新的刪除服務以記錄刪除的主鍵,進行同步刪除同步。

對於普通的一次增量同步,比如同步最近 10 分鐘的數據。我們是定義好同步腳本,傳入時間週期及合理的併發數,發起查詢請求,並將返回的數據返回到臨時隊列中;寫進程則按 5 千條一次讀隊列中的數據,按主鍵先刪後插,實現了增量的數據新增或者更新。

另外,出於項目週期及成本等考慮,項目並未採用讀取 Oracle Redo Log 的方式。這種方式的優點是最小化地減少讀寫操作;缺點是需要付費組件支持,需要單獨開發,以及日誌容量問題導致的系統運維難度高等。

數據同步看起來簡單,但實際上還是遇到了以下困難並進行了相應的解決:

  1. 由於是多進程同步,部分異常捕獲在初期被忽略了,在後來驗證的過程中一一補齊,最後保證了只要任務正常完成,同步即無誤。
  2. 數據併發寫壓力較大(初始化時數據同步量非常大)的情況下,會出現 TiDB 承壓,存在 TiKV 寫失敗的情況,需要控制併發量,並在實踐中得到最佳的配置。
  3. 連接頻繁失敗問題,用 Proxy 解決,以及高可用方案。由於 TiDB 在遇到超大 SQL 請求時,會一直申請內存直到 OOM,最後 TiDB 重啓,最後採用 HAPROXY 來解決 TiDB 的高可用性。這樣一個節點重啓儘量不影響其他 SQL 的運行。另外 HAPROXY 本身也需要保證高可用,最後是藉助運維的 OGW 集羣來負責HAPROXY的高可用。
  4. 聯合索引設置不合理,導致索引浪費,未來需要進行索引優化。
  5. 國外數據庫與國內網絡連接不穩定,主從庫同步延遲導致無法完整同步數據。最後採取了實時監控主從同步延遲及獲取數據業務時間最大值等雙重措施保證數據同步的準確性和及時性。
  6. 數據同步缺少監控機制,對同數據同步過程中是否有數據丟失,或者說怎麼保證兩邊數據庫是一致的,時間久了會不會出現不一致的情況,怎麼快速修復等,目前是通過腳本定期統計兩邊表記錄數的方式進行監控。

模型建設

一期項目主要目標是將分散的數據統一存儲起來,以及進行一些大量數據明細表之間的關聯查詢。當時面臨兩種選擇:

方案一:

僅對源數據進行基礎性的處理,然後使用複雜的 SQL 完成業務模型的定義(OPPO 自研報表平臺 InnerEye 支持按 SQL 語句自定義查詢接口),每次用戶查詢的時候,都通過這個模型 SQL 即時的運算並返回結果(可設置緩存時間)。這個做法的好處是幾乎沒有任何的中間甚至結果數據的開發工作;壞處是對算力的極大浪費,而且後期併發度變大後,性能將是瓶頸。

方案二:

進行常規的分層模型開發,按週期更新數據。由於一期項目較少聚合類報表,多是明細級數據查詢,我們僅僅將模型主要分爲共享層和應用層。查詢接口直接使用應用層單表查詢,可以通過優化索引實現秒查數據;共享層則是爲各個應用層的結果表提供一些公共的基礎數據支持。這種做法將面臨的挑戰將是:如何在 10 分鐘內,將所有的數據模型都完成相應的增量更新或者插入。

評估方案一的時候,使用了 TiSpark 進行了驗證,然而結果並不是很好,響應時間達數分鐘,當然原因可能是集羣算力不夠,也可能是 SQL 不夠優化。最終考慮到未來併發的壓力,很快把這個偷懶的方案最終否決了。

在實施方案二的過程中發現,有良好的索引的情況下,只要遵循增量更新的原則,完全能滿足性能需求。模型建設的輸出是一系列的 SQL 計算腳本。

最後,根據此業務系統目前的數據情況將數據模型設計爲三層設計,基礎數據,共享數據,應用數據。另外有獨立的維表數據層及系統數據層。

以上各層的數據,沒有進行分庫分表(在 TiDB 的技術框架中,不需要進行分庫分表來提升性能),數據生成後的一段時間(一般最長一個月)內都會發生變更。

由於採用的是增量更新,因此能很快的完成。唯一的缺點是:在系統初始化或者要修復很長時間段的數據時,由於索引的存在導致寫入速度較慢(相對無索引的文件表),但依然可以通過一定技術方案來規避。

任務調度

目前 OPPO 的分佈式調度系統是基於 airflow 開源項目搭建。同步任務與計算任務分屬獨立的 DAG,這樣雖然會多一些體力活(建立跨 DAG 依賴任務),但減少了不同類型/國家的任務的耦合度,方便了運維,提高了數據服務的可用性。

調度系統的使用過程中,需要注意的點主要有:

  1. 隊列數量。合理設置任務隊列的總數,保證任務執行的及時性及機器負載的平衡。
  2. 多機器。由於系統的準實時性,至少準備兩臺計算和同步的物理服務器,以保證數據服務不中斷。
  3. 優化 airfow 本身。由於 airflow 本身存在一些問題,因此需要建立獨立於 airflow 的運行監控機制。比如通過對其 db 表的查詢來監控其是否出現任務長時間阻塞等異常情況;另外需要定時清除歷史運行記錄,以提升 airflow 的 web 服務體驗。
  4. 時差問題。由於各國家地區數據庫存在時差問題,最後採用了腳本共用、調度分離的方式,減少耦合帶來的調度堵塞問題。

遇到的問題

從最開始的 2.x 版本,到現在穩定運行的 2.1.13,主要遇到了以下幾個重要的問題:

1. 提交事務大小限制問題

TiDB 本身是 TP 系統,因此出於對事務穩定性的考慮,對每次提交事務涉及的數據量大小有所限制。但由於項目本身每個任務涉及的數量有可能高達千萬行,因此需要打開TiDB的允許批量插入/刪除設置項。

TiDB 特意對事務大小設置了一些限制以減少這種影響:

  • 單個事務包含的 SQL 語句不超過 5000 條(默認)。
  • 每個鍵值對不超過 6MB。
  • 鍵值對的總數不超過 300,000。
  • 鍵值對的總大小不超過 100MB。

爲了避免在運行中出現過大事務,在項目中採取以下配置:

SET SESSION TiDB_batch_insert = 1;
SET SESSION TiDB_batch_delete = 1;set autocommit=1;

同時由於索引的存在,在進行數據的寫入過程中,過多的索引會加大事務的開銷,可以通過減少批次大小來降低單次事務(默認是 20000):

set @@session.TiDB_dml_batch_size = 5000;

2. Proxy 連接失敗的問題

項目運行過程中多次應用端出現 connect timeout 的情況,除去 TiDB Server 本來實例重啓的問題,haproxy 的連接超時時間設置過短,導致執行時間稍長的 SQL 就會被斷開連接,這個時候需要調整 haproxy 的超時參數:

timeout queue 30m
timeout connect 30m
timeout client 30m
timeout server 30m

3. TiDB Server 服務重啓問題

在項目過程中曾出現了多次 TiDB Server 服務重啓的現象,主要原因及措施如下:

  • TiDB Server 節點出現了 OOM。由於前期負載較低,將 TiSpark 服務直接部署在了 TiDB Server 節點,導致有大查詢時經常出現 OOM 情況。後面將 TiSpark 服務和 TiDB Server 服務進行了分開部署,並調整 OOM 相關配置爲:oom-action: "cancel"。
  • 機器故障問題。更換相關硬件設施。

4. 無法鎖表問題

爲了解決“硬刪除”問題,對小表同步的時候採取了覆蓋更新的模型,即先刪除全表再寫入新數據。但由於目前 TiDB 沒有鎖表的功能(鎖寫或者讀),導致這個小小的空檔如果被其他任務讀取就會造成數據錯誤。雖然由於有任務依賴關係的存在,這種情況非常少發生,但在數據修復或者人工運行任務的時候,還是會造成問題。

目前的解決方案是手工實現簡單的鎖表機制;另外就是可以使用臨時表然後 replace into 來解決。至於 TiDB 的系統級別的鎖表功能已經在規劃中了。

5. 與 Hadoop 數據湖的打通

項目受到了上級的一個重大的挑戰:在 TiDB 中的數據無法與現有數據(主要以 hive 表形式存儲於 Hadoop 集羣中)形成協同作用,項目價值會因此大打折扣。

針對這個挑戰,最開始打算再同步一份數據到 Hadoop 集羣中,但這樣做其實是存儲的極大浪費,但在當時似乎是唯一的辦法。在項目快接近尾聲的時候,發現可以通過在 TiSpark 集羣上通過 thriftServer(最後進化到使用 Livy 服務)的方式,打通兩個體系的數據,實現 hdfs 和 TiKV 兩個數據源的混合查詢。最後也確實取得了成功並已經服務了數個需求。相關的技術細節未來將以另外的文章進行說明和分享。

6. 髒數據處理

假設要插入 20 萬條數據,但由於事務限制,系統只能 5000 行條提交一次,一共需要提交 40 次。

現在的問題是這 40 次可能在任一一次提交中失敗,這樣先前提交的數據就成了髒數據,因此在重試的時候需要刪除這些數據後再做。因爲數倉任務經常有重跑的需求,而目前 TiDB 體系下沒有分區覆蓋,因此這是一個需要注意的點。

運行性能

目前系統上線約三個月,暫未出現任何較大的技術問題,運行非常平穩。以下是抽取的一些日常運行數據或壓測數據供參考。

1. 集羣 OPS 和 QPS

在現有環境上,集羣 OPS 最大可達到 61K,QPS 最大可達到 12.11K,查詢性能比較穩定。

2. 高可用

主要基於 TiDB Server 之上負載均衡組件 Haproxy 和 TiKV 的多副本機制實現。

3. 查詢穩定性

上圖中,除了有部分整機信息聚合查詢外耗時較長(主要使用 TiSpark 組件)外,可以看到 99% 的查詢在 4S 內進行了返回,而 95% 的查詢在 104ms 內返回,可以說性能是非常不錯。目前表的數據行量主要處於百萬到百億行級別,而且索引數量並不多,因此能獲得當前的性能可以說超出預期。

升級 3.0.5

由於 2.X 版本在達到 250 萬個 region 左右出現了一些性能問題,IO/CPU 負載接近滿負荷。跟官方溝通後,我們決定升級到 3.0.5 這一穩定版本。升級後,在沒有任何硬件變更的情況下,性能有了接近翻倍的提升,目前系統的核心資源都出現大幅空閒。

TiDB 技術體系的限制

項目結束後,現在回過頭來看 TiDB,我們認爲有以下一些比較重要的點需要注意:

  1. TiDB 首先是一個 TP 系統。即:目前來看 TiDB 主要是爲了 TP 系統設計的,AP 方面的功能有待加強。事實上 PingCAP 已經認識到了 AP 的重要性,在 3.x 中,AP 的功能將會通過引入 TiFlash 組件而大大加強,從而成爲真正的 HTAP。
  2. TiDB 存儲成本相對 Hadoop 集羣來說較高。目前至少要求是 SSD;加上未來 TiFlash 的引入,1 份數據將會存 4 份,存儲成本相對更大。
  3. TiDB 目前(截止 2019 年 9 月)尚未有 PB 級別的生產集羣。因此可能直接應用於海量數據的互聯網數據應用可能會遇到其他一些問題。

其他經驗教訓

1. 不要在一個可能包含很長字符串的列上創建索引

在 TiDB 建立索引可以極大提高查詢性能,但要避免在一個可能包含很長字符串的列建索引,否則在創建和使用索引時,都會花費較大的代價。而且當大小超過默認的 3072 byte 時,TiDB 會報錯。

2. 確保開啓位置標籤機制

當一個機器部署多個 TiKV 實例,未提高系統穩定性和可用性,一定要確保開啓了位置標籤機制。前期部署集羣服務時,雖然在 inventory.ini 文件中設置了以下內容 location_labels = ["host"],但是後來發現並沒有生效,導致一個機器 down 了以後,集羣中某些數據查詢出現了嚴重問題:

究其原因是因爲位置標籤機制沒有生效,導致同一個節點上存在同一個 region 的兩個副本(一共 3 副本),導致不能再正常對外提供相關服務了。

可以通過 pd-ctl 確認位置標籤機制生效,如 config show all 的時候有如下內容,代表已生效:

如果沒有生效,可通過以下方式使得生效:

config set location-labels "host"

總結:一臺機器部署多個 TiKV 實例的場景,要充分利用 location_labels 機制,將副本部署到不同的機器上,以增強系統的穩定性。

3. 不支持三段式查詢

目前 TiSpark 還不支持如下的三段式查詢。

dbname.tablename.columnname

如以下 sql 會執行失敗:

select dbname.tablename.columnname from dbname.tablename

可以通過別名的方式加以解決:

select A.columnname from dbname.tablename as A

4. 主鍵變更

目前在 TiDB 上進行變更主鍵(增加或者刪除字段)是不被支持的,唯一的辦法只有重建表。這在某些場景會成爲一個較爲痛苦的經歷。因此在表設計階段需要非常的小心,爭取一次做對。

總結

項目以極小的人力投入較爲成功的實現了預定目標,也陸續服務到了許多其他部門和項目,產生了良好的數據協同效應。

從此次實踐中,我們認爲:隨着 AP 能力的加強,TiDB 幾乎可以做爲大多數亞 PB 級別數據應用的存儲引擎。 因爲它的 HTAP 優雅架構能大大簡化運維和開發人員的工作,讓他們集中到業務邏輯表達和處理上。

當前的主流大數據技術主要源於互聯網平臺,大多在某些方面有妥協,因而需要相互補充,導致系統整體架構越來越複雜,最終讓運維及開發門檻也越來越高,這也是目前沒有更好辦法的辦法。但最優的數據系統架構應該是將業務邏輯無關的技術工作儘可能掩藏起來,交給數據庫引擎來打理。在這個話題上我看一篇非常不錯的文章大家可以參閱:《從大數據到數據庫》

事實上,隨着越來越多的非互聯網業務越來越信息化,其系統數據增長雖然尚達不到互聯網動輒就PB級,但也很輕易的達到TB級別;這個級別的TP/AP系統技術選型其實還是一個較大的空白。目前看TiDB是該領域的一個非常好的選擇。

項目中 PingCAP 團隊給予了大量的直接幫助,在此致謝!

 

閱讀 557發佈於 1月6日

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章