分佈式數據庫 TiDB 在攜程的實踐

作者簡介

 

Army,攜程數據庫專家,主要負責分佈式數據庫運維及研究。

Keira,資深數據庫工程師,主要負責 MySQL 和 TiDB 運維。

Rongjun,攜程大數據架構開發,專注離線和實時大數據產品和技術。

前言

攜程自 2014 年左右開始全面使用 MySQL 數據庫,隨着業務增長、數據量激增,單機實例逐漸出現瓶頸,如單錶行數過大導致歷史數據查詢耗時升高,單庫容量過大導致磁盤空間不足等。爲應對這些問題,我們採取了諸多措施如分庫分表的水平拆分、一主多從讀寫分離、硬件 SSD 升級、增加前端 Redis 緩存等,但同時也使得整個業務層架構更加複雜,且無法做到透明的彈性,因此開始將目光轉移到分佈式數據庫以解決這些痛點。

近年來受到 Spanner&F1 的啓發,基於 CAP 理論和 Paxos、Raft 協議作爲工程實現的分佈式數據庫得到了蓬勃發展,從硅谷的 CockroachDB 到國產的 TiDB 都在社區產生了很強的影響力。攜程也對這些產品從社區活躍度、用戶規模、易用性等多個方面做了調研,最終選擇了國產的 TiDB。

TiDB 是一個開源的 NewSQL 數據庫,支持混合事務和分析處理(HTAP)工作負載,兼容大部分 MySQL 語法,並且提供水平可擴展性、強一致性和高可用性。主要由 PingCAP 公司開發和支持,並在 Apache 2.0 下授權。2018 年 11 月我們開始 TiDB 的 POC 以及與攜程現有運維平臺的整合,2019 年 1 月第一個線上應用正式接入,最初的目標只是保證數據庫的可用性以及可以存儲足夠多的關係型數據。隨着 TiDB 快速迭代,越來越多的功能進入社區,如 HATP 特性,讓我們不侷限於最初的目標,開始了新的探索。本文將介紹 TiDB 在攜程業務場景中的運維實踐,希望對讀者有所幫助和參考。

一、架構

攜程內部歷時 1 年,代號爲 “流浪地球” 的機房級故障演練,驗證了 IDC 級別故障容災能力。我們將 TiDB 的三個副本分佈在三個數據中心,保證在單中心故障時不影響對外服務,同時數據一致性也不受影響,並在 tidb-server 層實現了自動探活以及自動故障切換,讓 RPO 等於 0,RTO 小於 30S。

我們先來了解一下 TiDB 的整體架構(如圖 1-1),再結合攜程的場景來部署。

圖 1-1 TiDB 的整體架構圖

從 TiDB 的架構圖我們可以看到,得益於組件 PD 和 TiKV 都通過 Raft 實現了數據的容災,原生就提供了多 IDC 的部署能力,和 Google Spanner 採用原子鐘方案不同的是,TiDB 採用了 PD 進行單點全局統一授時的 Timestamp 方案。TiDB 中的每個事務都需要向 PD leader 獲取 TSO,當 TiDB 與 PD leader 不在同一個數據中心時,它上面運行的事務也會因此受網絡延遲影響。目前攜程的跨 IDC 延遲在 0.5-2ms 之間 ,屬於可接受的延遲範圍。配置三數據中心時,需要對相應的 TiKV 和 PD 的 label 配置規則,這樣 PD 在調度 region 副本時會根據標籤屬性在每一個機房都擁有一份全量數據。具體的一個配置示例,如圖 1-2:

圖 1-2 TiDB 在攜程的部署架構和配置

這種部署架構的優點:

二、應用場景

TiDB 目前已經應用到攜程的多個業務場景,包括風控、社區、營銷、搜索、酒店等。這裏選取兩個比較典型的使用案例——國際業務 CDP 平臺和酒店結算業務。

2.1 國際業務 CDP 平臺

因爲 Trip 數據來源比較廣泛,既有自身數據也有外部數據;數據形式也非常多樣化,既有結構化數據,也有半結構化和非結構化數據;數據加工形式既有離線數據處理,也有在線數據處理,因此國際業務構建了 CDP 平臺以解決加工這些數據,形成業務系統、運營、市場需要並且可以理解的數據和標籤,具體可以閱讀往期文章:《攜程國際業務動態實時標籤處理平臺實踐》。

TiDB 在其中主要承擔存儲業務持久化的標籤以及內部 SOA 調用的查詢服務。查詢分爲 UID 等維度的基礎信息查詢、訂單訂閱基礎信息查詢的 OLTP,以及 EDM\Marketing 等人羣的 OLAP 查詢。整個 CDP 平臺的架構如圖 2-1:

圖 2-1 CDP 平臺架構圖

具體的數據處理,歷史全量數據通過數據批處理引擎(如 Spark)轉換完成以後批量寫入到數據持久化存儲引擎(TiDB),增量數據業務應用以消息的形式發送到 Kafka 或者 QMQ 消息隊列,通過實時 DAG 處理完後持久化到存儲引擎(TiDB)。

持久標籤訪問的主要場景有兩個,一種是跟現有 CRM 系統對接,在線根據業務的特徵圈選符合條件的業務數據,這種場景的查詢條件不固定,返回結果集因篩選條件而定,對於數據存儲引擎的數據計算和處理能力要求比較高,即我們在數據處理領域經常提到的 OLAP 的場景。另一種場景是線上業務根據前端傳入的業務標籤相關的唯一標識來查詢是否滿足特定業務要求,或者返回指定特徵值,滿足業務處理的需要,需要毫秒級響應,對應的是 OLTP 場景。

由於標籤的多樣性,有查詢記錄的字段多達 60 個,查詢條件是 60 個字段的隨機組合,無法通過傳統數據庫層的 Index 來提高查詢效率,經典的方案是 OLTP 和 OLAP 分離,但數據會存儲多份,多數據源的數據一致性是一個很大的挑戰。

對於這種場景,我們開啓了 TiDB 的 TiFlash,TiFlash 是 TiDB HTAP 形態的關鍵組件,它是 TiKV 的列存擴展,在提供了良好的隔離性的同時,也兼顧了強一致性。列存副本通過 Raft Learner 協議異步複製,但是在讀取的時候通過 Raft 校對索引配合 MVCC 的方式獲得 Snapshot Isolation 的一致性隔離級別。TiFlash MPP 模式如圖 2-2。

圖 2-2 TiDB MPP 模式

這種架構很好地解決了 HTAP 場景的隔離性以及列存同步的問題,開啓之後幾個典型查詢性能提升:

TiFlash MPP 提升,20s -> 1s

Set @@session.tidb_allow_mpp=1;

Set @@session.tidb_enforce_mpp=0;

TiFlash 列裁剪,16.9s -> 2.8s

Set @@session.tidb_allow_mpp=1;

Set @@session.tidb_enforce_mpp=0;

Set session tidb_isolation_read_engines =’tidb,tiFlash’;

2.2 酒店結算業務

攜程酒店結算業務全庫 6T,單服務器存儲 6T 全量數據有很大挑戰。常規的方法是用分庫分表的方式來減少實例數據量及壓力,但分庫分表的維度很難確定,無論從酒店維度還是供應商維度都無法避免跨片的查詢,給程序的開發帶來了很大的困難,並且大部分查詢都是聚合運算,因此我們嘗試遷移到 TiDB。

目前最大的表存儲了 28 億條數據,讀寫已完全切換到 TiDB。具體所使用的部署模式和上節提到的國際業務 CDP 平臺類似,同樣是開啓了 TiDB 的 TiFlash 來加速查詢的性能,具體的性能如圖 2-3:

圖 2-3 酒店結算性能監控

三、一些問題的實踐

3.1 參數不合理導致的性能問題

分佈式數據庫有別於傳統單機,通常 MySQL 遇到性能問題時可以快速定位是由於網絡抖動、SQL 缺失索引還是請求次數激增等原因導致的,但分佈式的 TiDB 組件衆多,各個組件之間的網絡通信、某個組件資源不足、SQL 複雜等都可能是導致出現性能問題的原因。目前官方提供了問題導圖,方便根據不同的場景儘快定位原因。這裏給出一個具體的案例,總結了一個典型問題的排查思路。

國際業務集羣使用官方默認配置的集羣上線測試時,發現寫入耗時高達秒級,且耗時波動較大。來自應用端的監控(縱座標單位爲毫秒), 如圖 3-1:

圖 3-1 IBA 寫入響應監控

根據 Pingcap 的導圖發現 scheduler command duration 的時間約等於事務的 prewrite 時間(縱座標單位爲秒),可以看出 scheduler-worker 不足。如圖 3-2:

圖 3-2 scheduler command duration 的時間

所以我們做了如下的調整:

調整完成後來自應用端的監控(縱座標單位爲毫秒),如圖 3-3,紅色箭頭處是參數調整的時間點:

圖 3-3 IBA 寫入響應監控

**總結:**默認配置並非最佳配置,需要根據服務器硬件、使用場景不斷調試並最終固化爲每個集羣甚至所有集羣的最佳實踐配置;根據 PingCAP 提供的問題導圖,逐步定位具體哪個組件哪個方面存在瓶頸,我們同時也在進一步開發一鍵定位工具,能更快速的定位性能瓶頸。

3.2 分佈式帶來的自增列問題

含自增列的表,在自增列不強制賦值的情況下,insert 語句報主鍵衝突:

報錯 SQL:INSERT INTO xxx_table (id, name, tag, comment, creator`) VALUES (?, ?, ?, ?, ?)

報錯內容:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 175190 for key 'PRIMARY'.

在 PingCAP 官方文檔上,有以下介紹:

TiDB 中,自增列只保證自增且唯一,並不保證連續分配。TiDB 目前採用批量分配 ID 的方式,所以如果在多臺 TiDB 上同時插入數據,分配的自增 ID 會不連續。TiDB 實現自增 ID 的原理是每個 tidb-server 實例緩存一段 ID 值用於分配(目前會緩存 30000 個 ID),用完這段值再去取下一段。假設集羣中有兩個 tidb-server 實例 A 和 B(A 緩存 [1,30000] 的自增 ID,B 緩存 [30001,60000] 的自增 ID),依次執行如下操作:客戶端向 B 插入一條將 id 設置爲 1 的語句 insert into t values (1, 1),並執行成功。客戶端向 A 發送 Insert 語句 insert into t (c) (1),這條語句中沒有指定 id 的值,所以會由 A 分配,當前 A 緩存了 [1, 30000] 這段 ID,所以會分配 1 爲自增 ID 的值,並把本地計數器加 1。而此時數據庫中已經存在 id 爲 1 的數據,最終返回 Duplicated Error 錯誤。

通過這段介紹,我們瞭解到造成自增主鍵衝突,是因爲存在自增主鍵顯式插入導致。

**結論:**分佈式數據庫對於表自增列是預分配的,自增主鍵顯式插入會導致 tidb-server 上的計數器混亂,引起數據寫入報錯。在開發規範中,我們明確要求 TiDB 不允許自增主鍵顯式插入。

3.3 修改字段是否爲空導致默認值異常

如下的表,我們字段從 int 升級到 bigint

CREATE TABLE test (id int);

alter table test add col1 int(11) null default '0';

alter table test add col2 int(11) null default '0';   

alter table test change col1 col1 bigint(20) null default '0';  

alter table test change col2 col2 bigint(20) null default '0';  

我們發現默認值 0 不合適,因此,執行下面的語句,把默認值調整爲 null

alter table test change col1 col1 bigint(20) null ;   

alter table test change col2 col2 bigint(20) null';   

此時,我們插入一條數據:insert into test(id) values(1);

神奇的發現,col1 和 col2 的值依舊是 0. 這和我們的預期不符。經過一系列重現測試,以及社區論壇的查找,我們發現這個問題是一個已知問題。https://github.com/pingcap/tidb/pull/20491. 該 Bug 在 TiDB 4.0.9 及以後版本已修復。

**結論:**成熟的社區論壇是 TiDB 日常運維和快速排障的利器,藉助社區論壇上各種技術探索和交流分享,可以汲取優質內容,收穫前沿知識,快速定位和解決問題。

四、監控與告警

對於分佈式數據庫運維,監控和告警是非常核心的一環,冒煙現象或者不規範現象,需要及時發現,及時解決,避免問題惡化。監控準確、告警及時可以幫助運維人員準確定位問題,快速解決故障。TiDB 使用開源時序數據庫 Prometheus 作爲監控和性能指標信息存儲方案,使用 Grafana 作爲可視化組件進行展示,我們在此基礎上進一步做了整合。

4.1 TiDB 監控大盤

TiDB 原生提供 prometheus+Grafana 的性能大盤,數據非常豐富,但數據分散在單獨的集羣,無法提供全局視角,我們通過 prometheus 源生 remote write 到 9201 端口,自研了一個 adaptor 監聽 9201 端口,轉發性能數據到攜程統一監控平臺,搭建了我們自己的監控大盤。如圖 4-1:

圖 4-1 整合後的 TiDB 監控大盤 

4.2 三副本監控

TiDB 使用三個以上的副本,通過 raft 協議來保證數據的一致性。當出現多數副本丟失或者宕機時,這部分數據處於不可用狀態,是否存在副本缺失或者副本狀態異常是需要特別注意的。因此我們會針對副本的數目及狀態進行巡檢,確保不會出現長時間內副本不足的情況,一旦發現有副本丟失,可以增加副本的調度線程,務必及時解決副本缺失問題。Region Peer 的監控如圖 4-2:

圖 4-2 三副本監控

4.3 磁盤容量監控

TiDB 存儲數據量龐大,需要特別關注機器磁盤剩餘可使用空間的情況,以免寫滿磁盤造成不必要的故障。對於磁盤的監控,我們的閾值是物理磁盤的 80%,一旦磁盤使用容量超過閾值,我們需要安排加機器擴容。對比相同情況下 MySQL 複雜的拆分方法,TiDB 的處理方法更簡單高效。磁盤的監控告警如圖 4-3:

圖 4-3 TiDB 磁盤監控

4.4 配置標準化檢查

TiDB 集羣的配置文件參數、系統參數衆多,不同實例的配置項各不相同,且經常會對集羣擴容縮容,因此我們要求變更前後集羣的配置必須嚴格按照標準配置進行調整。只要做到配置標準,很大程度上就會保證集羣標準化運行。配置標準化的監控告警如圖 4-4:

圖 4-4 配置標準化的監控告警

4.5 性能告警

有時候會存在突發的流量上升,或者瞬間的性能尖峯的情況,這時候就需要關注性能告警。METRICS_SCHEMA 是基於 Prometheus 中 TiDB 監控指標的一組視圖,有了基礎的性能數據,我們只需要根據性能閾值,及時告警,及時分析處理。

五、周邊工具

除了監控與告警,我們也開發了一系列周邊工具,對於 TiDB 的運維,帶來了更大的便利。這些周邊工具主要包括:

5.1 和現有的數據周邊工具打通

現有的數據周邊工具主要包括:數據庫的發佈(DDL),數據在線查詢,數據在線修改,以及和現有的大數據流程打通等,這些支持 MySQL 的工具也一樣可以支持 TiDB,爲 MySQL 遷移 TiDB 打解決了後顧之憂,讓之前使用 MySQL 的開發測試人員可以方便流暢地切換到 TiDB。

5.2 TiDB 部署工具

TiDB 集羣實例角色較多,集羣部署有別於傳統單機,需要單獨開發一套部署工具,包括集羣上線流程、集羣下線流程、擴容縮容實例、集羣版本升級等。

5.3 TiDB 閃回工具

有時候會遇到開發測試人員誤操作數據的情況,可以使用數據閃回工具進行回退,我們藉助 TiDB binlog 開發了閃回工具,對 binlog 的內容做反轉,生成數據恢復 SQL,供 TiDB 數據恢復使用。

六、未來規劃

6.1 故障的一鍵分析

分佈式數據庫與單機不同, TiDB 組件比較多,可供調整的參數有數百個,各個組件之間的網絡通信、某個組件資源不足、SQL 複雜等都可能導致出現性能問題,後續計劃將 TiDB 診斷做成自動化和智能化,目前已經通過改造 TiDB server 源碼,完成了 TiDB 的全鏈路 SQL 收集和分析,這將作爲未來故障一鍵分析的基礎。 

6.2 基於 HDD 硬盤測試

TiDB 所有的優化都是基於 SSD 來做的,高性能意味着高成本。我們還是會面臨數據量比較大,但寫入和查詢都比較少,響應要求不高的場景。我們目前已經完成基於 HDD 硬盤的測試,選擇的機器配置爲 12 塊 10T HDD 硬盤,單機部署 12 個 TiKV 實例,這種架構已經在小範圍應用。

6.3 同城雙中心自適應同步方案 DR Auto-Sync

DR Auto-Sync 正處在高速迭代的週期中,後續版本將會有一系列高可用和容災能力的加強。從 5.3.0 開始將支持雙中心對等部署,藉此獲得快速恢復多副本的能力,我們也在保持關注中。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/lrhLNOWQFrCYfqFWzHQ0Pg