標籤:

TiDB / TiSpark 在易果集團實時數倉中的創新實踐

項目背景

目前企業大多數的數據分析場景的解決方案底層都是圍繞 Hadoop 大數據生態展開的,常見的如 HDFS + Hive + Spark + Presto + Kylin,在易果集團,我們初期也是採取這種思路,但是隨著業務規模的快速增長和需求的不斷變化,一些實時或者准實時的需求變得越來越多,這類業務除了有實時的 OLTP 需求,還伴隨著一些有一定複雜度的 OLAP 的需求,單純地使用 Hadoop 已經無法滿足需求。

現有的准實時系統運行在 SQL Server 之上,通過開發人員編寫和維護相應的存儲過程來實現。由於數據量不大,SQL Server 能夠滿足需求,但是隨著業務的發展,數據量隨之增長,SQL Server 越來越不能滿足需求,當數據量到達一定的階段,性能便會出現拐點。這個時候,這套方案已完全無法支撐業務,不得不重新設計新的方案。

選型評估

在評估初期,Greenplum、Kudu、TiDB 都進入了我們的視野,對於新的實時系統,我們有主要考慮點:

  • 首先,系統既要滿足 OLAP 還要滿足 OLTP 的基本需求;
  • 其次,新系統要盡量降低業務的使用要求;
  • 最後,新系統最好能夠與現有的 Hadoop 體系相結合。

Greenplum 是一套基於 PostgreSQL 分析為主的 MPP 引擎,大多用在並發度不高的離線分析場景,但在 OLTP 方面,我們的初步測試發現其對比 TiDB 的性能差很多。

再說說 Kudu。Kudu 是 CDH 2015年發布的一套介於 Hbase 和 HDFS 中間的一套存儲系統,目前在國內主要是小米公司應用的較多,在測試中,我們發現其在 OLTP 表現大致與 TiDB 相當,但是一些中等數據量下,其分析性能相比 TiDB 有一定差距。另外我們的查詢目前主要以 Presto 為主,Presto 對接 Kudu 和 PostgreSQL 都是需要考慮兼容性的問題,而 TiDB 兼容 MySQL 協議,在應用初期可以直接使用 Presto-MySQL 進行統一查詢,下一步再考慮專門開發 Presto-TiDB。

另外,我們希望未來的實時系統和離線系統能夠通用,一套代碼在兩個系統中都能夠完全兼容,目前 Tispark 和 SparkSQL 已經很大程度上實現了這點,這支持我們在以後離線上的小時級任務可以直接切換到 TiDB上,在 TiDB 上實現實時業務的同時,如果有 T+1 的需求也能夠直接指 HDFS 即可,不用二次開發,這是 Kudu 和 GP 暫時實現不了的。

最後,TiSpark 是建立在 Spark 引擎之上,Spark 在機器學習領域裡有諸如 Mllib 等諸多成熟的項目,對比 GP 和 Kudu,演算法工程師們使用 TiSpark 去操作 TiDB 的門檻非常低,同時也會大大提升演算法工程師們的效率。

經過綜合的考慮,我們最終決定使用 TiDB 作為新的實時系統。同時,目前 TiDB 的社區活躍度非常好,這也是我們考慮的一個很重要的方面。

TiDB 簡介

在這裡介紹一下 TiDB 的相關特性:TiDB 是基於 Google Spanner/F1 論文啟發開源的一套 NewSQL 資料庫github.com/pingcap/tidb,它具備如下 NewSQL 核心特性:

  • SQL支持 (TiDB 是 MySQL 兼容的)
  • 水平線性彈性擴展
  • 分散式事務
  • 數據強一致性保證
  • 故障自恢復的高可用

同時,TiDB 還有一套豐富的生態工具,例如:快速部署的 TiDB-Ansible、無縫遷移 MySQL 的 Syncer、異構數據遷移工具 Wormhole、以及 TiDB-Binlog、Backup & Recovery 等。

SQL Server 遷移到 TiDB

由於我們公司的架構是 .NET + SQL Server 架構,所以我們無法像大多數公司一樣去使用 MySQL Binlog 去做數據同步,當然也就無法使用 TiDB 官方提供的 Syncer 工具了。因此我們採用了 Flume + Kafka 的架構,我們自己開發了基於 Flume 的 SQL Server Source 去實時監控 SQL Server 數據變化,進行捕捉並寫入 Kafka 中,同時,我們使用 Spark Streaming 去讀取 Kafka 中的數據並寫入 TiDB,同時我們將之前 SQL Server 的存儲過程改造成定時調度的 MySQL 腳本。

圖:SQL Server 數據遷移到 TiDB

TiDB 前期測試

在測試初期,我們採用 TiDB 的版本為 RC4,在測試過程中曾經在同時對一張表進行讀寫時,出現 Region is stale 的錯誤,在 GitHub 上提出 Issue 後,TiDB 官方很快在 Pre-GA 版本中進行了修復。在測試環境,我們是手動通過二進位包的形式來部署 TiDB ,雖然比較簡單,但是當 TiDB 發布 GA 版本之後,版本升級卻是一個比較大的問題,由於早期沒有使用 TiDB-ansible 安裝,官方製作的升級腳本無法使用,而手動進行滾動升級等操作非常麻煩。由於當時是測試環境,在聽取了 TiDB 官方的建議之後,我們重新利用 TiDB 官方提供的 TiDB-ansible 部署了 TiDB 的 GA 版本。只需要下載官方提供的包,修改相應的配置,就能完成安裝和部署。官方也提供了升級腳本,能夠在相鄰的 TiDB 版本之前完成無縫滾動升級。同時 TiDB-ansible 默認會提供 Prometheus + Grafana 的監控安裝,官方提供了非常豐富完善的 Grafana 模板,省去了運維很多監控配置的工作量,借著 TiDB 部署監控的契機,我們也完成了諸如 Redis,RabbitMQ,Elasticsearch 等很多應用程序的監控由 Zabbix 往 Prometheus 的遷移。這裡需要注意的是,如果是用官方提供的部署工具部署 Prometheus 和 Grafana,在執行官方的停止腳本時切記跳過相應的組件,以免干擾其他程序的監控。

TiDB 上線過程

在10月中旬,隨著新機器的採購到位,我們正式將 TiDB 部署到生產環境進行測試,整個架構為 3 台機器,3TiKV+3PD+2TiDB 的架構。在生產環境中的大數據量場景下,遇到了一些新的問題。

首先遇到的問題是 OLTP 方面,Spark Streaming 程序設置的 5 秒一個窗口,當 5 秒之內不能處理完當前批次的數據,就會產生延遲,同時 Streaming 在這個批次結束後會馬上啟動下一個批次,但是隨著時間的積累,延遲的數據就會越來越多,最後甚至延遲了 8 小時之久;另一方面,由於我們使用的是機械硬碟,因此寫入的效率十分不穩定,這也是造成寫入延遲的一個很主要的因素。

出現問題之後我們立即與 TiDB 官方取得聯繫,確認 TiDB 整體架構主要基於 SSD 存儲性能之上進行設計的。我們將 3 台機器的硬碟都換成了 SSD;與此同時,我們的工程師也開發了相應的同步程序來替代 Spark Streaming,隨著硬體的更新以及程序的替換,寫入方面逐漸穩定,程序運行的方式也和 Streaming 程序類似,多程序同時指定一個 Kafka 的 Group ID,同時連接不同機器的 TiDB 以達到寫入效率最大化,同時也實現了 HA,保證了即使一個進程掛掉也不影響整體數據的寫入。

在 OLTP 優化結束之後,隨之而來的是分析方面的需求。由於我們對 TiDB 的定位是實時數據倉庫,這樣就會像 Hadoop 一樣存在很多 ETL 的流程,在 Hadoop 的流程中,以 T+1 為主的任務佔據了絕大多數,而這些任務普遍在凌晨啟動執行,因此只能用於對時間延遲比較大的場景,對實時性要求比較高的場景則不適合,而 TiDB 則能很好的滿足實時或者准實時的需求,在我們的業務場景下,很多任務以 5-10 分鐘為執行周期,因此,必須確保任務的執行時長在間隔周期內完成。

我們取了兩個在 SQL Server 上跑的比較慢的重要腳本做了遷移,相比於 SQL Server/MySQL 遷移至 Hadoop,從 SQL Server 遷移至 TiDB 的改動非常小,SQL Server 的 Merge 操作在 TiDB 里也通過 replace into 能夠完成,其餘一些 SQL Server 的特性,也能夠通過 TiDB 的多行事務得以實現,在這一方面,TiDB 的 GA 版本已經做的非常完善,高度兼容 MySQL,因此遷移的成本非常小,從而使我們能夠將大部分精力放在了調優方面。

在腳本遷移完畢之後,一些簡單的腳本能夠在秒級完成達到了我們的預期。但是一些複雜的腳本的表現在初期並沒表現出優勢,一些腳本與 SQL Server 持平甚至更慢,其中最大的腳本 SQL 代碼量一共 1000 多行,涉及將近 20 張中間表。在之前的 SQL Server 上,隨著數據量慢慢增大,每天的執行時長逐漸由 1-2 分鐘增長到 5-6 分鐘甚至更久,在雙11當天凌晨,隨著單量的湧入和其他任務的干擾延遲到 20 分鐘甚至以上。在遷移至 TiDB 初期,在半天的數據量下 TiDB 的執行時長大致為 15 分鐘左右,與 SQL Server 大致相同,但是並不能滿足我們的預期。我們參考了 TiDB 的相關文檔對查詢參數做了一些調優,幾個重要參數為:tidb_distsql_scan_concurrency,tidb_index_serial_scan_concurrency,tidb_index_join_batch_size(TiDB 提供了很好的並行計算能力)。經過驗證,調整參數後,一些 SQL 能夠縮短一倍的執行時間,但這裡依舊不能完全滿足我們的需求。

引入 TiSpark

隨後,我們把目光轉向了 TiDB 的一個子項目 TiSparkgithub.com/pingcap/tisp,用官網的介紹來講 TiSpark 就是藉助 Spark 平台,同時融合 TiKV 分散式集群的優勢,和 TiDB 一起解決 HTAP 的需求。TiDB-ansible 中也帶有 TiSpark 的配置,由於我們已經擁有了 Spark 集群,所以直接在現有的 Spark 集群中集成了 TiSpark。雖然該項目開發不久,但是經過測試,收益非常明顯。

TiSpark 的配置非常簡單,只需要把 TiSprak 相關的 jar 包放入 Spark 集群中的 jars 文件夾中就能引入 TiSpark,同時官方也提供了 3 個腳本,其中兩個是啟動和停止 TiSpark 的 Thrift Server,另一個是提供的 TiSpark 的 cli 客戶端,這樣我們就能像使用 Hive 一樣使用 TiSpark 去做查詢。

在初步使用之後,我們發現一些諸如 select count(*) from table 等 SQL 相比於 TiDB 有非常明顯的提升,一些簡單的 OLAP 的查詢基本上都能夠在 5 秒之內返回結果。經過初步測試,大致在 OLAP 的結論如下:一些簡單的查詢 SQL,在數據量百萬級左右,TiDB 的執行效率可能會比 TiSpark 更好,在數據量增多之後 TiSpark 的執行效率會超過 TiDB,當然這也看 TiKV 的配置、表結構等。在 TiSpark 的使用過程中,我們發現 TiSpark 的查詢結果在百萬級時,執行時間都非常穩定,而 TiDB 的查詢時間則會隨著數據量的增長而增長(經過與 TiDB 官方溝通,這個情況主要是因為沒有比較好的索引進行數據篩選)。針對我們的訂單表做測試,在數據量為近百萬級時,TiDB 的執行時間為 2 秒左右,TiSpark 的執行時間為 7 秒;當數據量增長為近千萬級時,TiDB 的執行時間大致為 12 秒(不考慮緩存),TiSpark 依舊為 7 秒,非常穩定。

因此,我們決定將一些複雜的 ETL 腳本用 TiSpark 來實現,對上述的複雜腳本進行分析後,我們發現,大多數腳本中間表很多,在 SQL Server 中是通過 SQL Server 內存表實現,而遷移至 TiDB,每張中間表都要刪除和插入落地,這些開銷大大增加了執行時長(據官方答覆 TiDB 很快也會支持 View、內存表)。在有了 TiSpark 之後,我們便利用 TiSpark 將中間表緩存為 Spark 的內存表,只需要將最後的數據落地回 TiDB,再執行 Merge 操作即可,這樣省掉了很多中間數據的落地,大大節省了很多腳本執行的時間。

在查詢速度解決之後,我們發現腳本中會有很多針對中間表 update 和 delete 的語句。目前 TiSpark 暫時不支持 update 和 delete 的操作(和 TiSpark 作者溝通,後續會考慮支持這兩個操作),我們便嘗試了兩種方案,一部分執行類似於 Hive,採用 insert into 一張新表的方式來解決;另外一部分,我們引入了 Spark 中的 Snappydata 作為一部分內存表存儲,在 Snappydata 中進行 update 和 delete,以達到想要的目的。因為都是 Spark 的項目,因此在融合兩個項目的時候還是比較輕鬆的。

最後,關於實時的調度工具,目前我們是和離線調度一起進行調度,這也帶來了一些問題,每次腳本都會初始化一些 Spark 參數等,這也相當耗時。在未來,我們打算採用 Spark Streaming 作為調度工具,每次執行完成之後記錄時間戳,Spark Streaming 只需監控時間戳變化即可,能夠避免多次初始化的耗時,通過 Spark 監控,我們也能夠清楚的看到任務的延遲和一些狀態,這一部分將在未來進行測試。

TiDB 官方支持

在遷移過程中,我們得到了 TiDB 官方很好的支持,其中也包括 TiSpark 相關的技術負責人,一些 TiSpark 的 Corner Case 及使用問題,我們都會在群里拋出,TiDB 的官方人員會非常及時的幫助我們解決問題,在官方支持下,我們遷移至 TiSpark 的過程很順利,沒有受到什麼太大的技術阻礙。

實時數倉 TiDB / TiSpark

在遷移完成之後,其中一條複雜的 SQL,一共 Join 了 12 張表(最大表數量億級,部分表百萬級),在平時小批量的情況下,執行時間會在 5 分鐘左右,我們也拿了雙11全量的數據進行了測試,執行時間在 9 分鐘以上,而採用了 TiSpark 的方式去執行,雙11全量的數據也僅僅花了 1 分鐘,性能提升了 9 倍。整個大腳本在 SQL Server 上運行雙11的全量數據以前至少要消耗 30 分鐘,利用 TiDB 去執行大致需要 20 分鐘左右,利用 TiSpark 只需要 8 分鐘左右,相對 SQL Server 性能提升 4 倍,也就是說,每年數據量最高峰的處理能力達到了分鐘級,很好的滿足了我們的需求。

最後,不管是用 TiDB 還是用 TiSpark 都會有一部分中間表以及與原表進行 Merge 的操作,這裡由於 TiDB 對事務進行的限制,我們也採用以萬條為單批次進行批量的插入和 Merge,既避免了超過事務的報錯又符合 TiDB 的設計理念,能夠達到最佳實踐。

有了 TiSpark 這個項目,TiDB 與 Hadoop 的生態體系得到進一步的融合,在沒有 TiSpark 之前,我們的系統設計如下:

圖:多套數倉並存

可以發現,實時數倉與 T+1 非同步數倉是兩個相對獨立的系統,並沒有任何交集,我們需要進行數據實時的同步,同時也會在夜晚做一次非同步同步,不管是 Datax 還是 Sqoop 讀取關係型資料庫的效率都遠遠達不到 TiSpark 的速度,而在有了 TiSpark 之後,我們可以對 T+1 非同步數倉進行整合,於是我們的架構進化為如下:

圖:TiDB / TiSpark 實時數倉平台

這樣就能夠利用 TiSpark 將 TiDB 和 Hadoop 很好的串聯起來,互為補充,TiDB 的功能也由單純的實時數倉變成能夠提供如下幾個功能混合資料庫:

1. 實時數倉,上游 OLTP 的數據通過 TiDB 實時寫入,下游 OLAP 的業務通過 TiDB / TiSpark 實時分析。

2. T+1 的抽取能夠從 TiDB 中利用 TiSpark 進行抽取。

  • TiSpark 速度遠遠超過 Datax 和 Sqoop 讀取關係型資料庫的速度;
  • 抽取工具也不用維護多個系統庫,只需要維護一個 TiDB 即可,大大方便了業務的統一使用,還節省了多次維護成本。
  • TiDB 天然分散式的設計也保證了系統的穩定、高可用。

3. TiDB 分散式特性可以很好的平衡熱點數據,可以用它作為業務庫熱點數據的一個備份庫,或者直接遷入 TiDB 。

上面這三點也是我們今後去努力的方向,由此可見,TiSpark 不僅對於 ETL 腳本起到了很重要的作用,在我們今後的架構中也起到了舉足輕重的作用,為我們創建一個實時的統一的混合資料庫提供了可能。

與此同時,我們也得到 TiDB 官方人員的確認,TiDB 將於近期支持視圖、分區表,並會持續增強 SQL 優化器,同時也會提供一款名為 TiDB Wormhole 的異構平台數據實時遷移工具來便捷的支持用戶的多元化遷移需求。我們也計劃將更多的產品線逐步遷入 TiDB。

總結

同時解決 OLAP 和 OLTP 是一件相當困難的事情,TiDB 和 TiSpark 雖然推出不久,但是已經滿足很多應用場景,同時在易用性和技術支持上也非常值得稱讚,相信 TiDB 一定能夠在越來越多的企業中得到廣泛應用。

? 作者簡介:羅瑞星,曾就職於前程無憂,參加過 Elasticsearch 官方文檔中文翻譯工作,現就職於易果集團,擔任資深大數據工程師,負責易果集團數據分析架構設計等工作。

推薦閱讀:

黃東旭DTCC2017演講實錄:When TiDB Meets Kubernetes
三篇文章了解 TiDB 技術內幕 —— 談調度
TiDB DevCon2018.tick(1.20)
【開源訪談】黃東旭:「無人區」的探索者,TiDB 的前行之路

TAG:TiDB |