做者介紹:呂磊,摩拜單車高級 DBA。git
摩拜單車 2017 年開始將 TiDB 嘗試應用到實際業務當中,根據業務的不斷髮展,TiDB 版本快速迭代,咱們將 TiDB 在摩拜單車的使用場景逐漸分爲了三個等級:github
本文會選擇三個場景,給你們簡單介紹一下 TiDB 在摩拜單車的使用姿式、遇到的問題以及解決方案。算法
訂單業務是公司的 P0 級核心業務,之前的 Sharding 方案已經沒法繼續支撐摩拜快速增加的訂單量,單庫容量上限、數據分佈不均等問題愈發明顯,尤爲是訂單合庫,單表已是百億級別,TiDB 做爲 Sharding 方案的一個替代方案,不只完美解決了上面的問題,還能爲業務提供多維度的查詢。sql
<center>圖 1 兩地三中心部署架構圖</center>數據庫
整個集羣部署在三個機房,同城 A、同城 B、異地 C。因爲異地機房的網絡延遲較高,設計原則是儘可能使 PD Leader 和 TiKV Region Leader 選在同城機房(Raft 協議只有 Leader 節點對外提供服務),咱們的解決方案以下:安全
<center>圖 2 訂單集羣的遷移過程以及業務接入拓撲圖</center>網絡
爲了方便描述,圖中 Sharding-JDBC 部分稱爲老 Sharding 集羣,DBProxy 部分稱爲新 Sharding 集羣。session
order_id
取模經過 DBproxy 寫入各分表,解決數據分佈不均、熱點等問題。order_id
的讀寫流量,TiDB 合庫做爲 readonly 負責其餘多維度的查詢。2.3.1 上線初期新集羣流量灰度到 20% 的時候,發現 TiDB coprocessor 很是高,日誌出現大量 server is busy 錯誤。數據結構
問題分析:架構
Next/NextChunk()
方法獲取結果。Chunk 是內存中存儲內部數據的一種數據結構,用於減少內存分配開銷、下降內存佔用以及實現內存使用量統計/控制,TiDB 2.0 中使用的執行框架會不斷調用 Child 的 NextChunk
函數,獲取一個 Chunk 的數據。每次函數調用返回一批數據,數據量由一個叫 tidb_max_chunk_size
的 session 變量來控制,默認是 1024 行。訂單表的特性,因爲數據分散,實際上單個 Region 上須要訪問的數據並很少。因此這個場景 Chunk size 直接按照默認配置(1024)顯然是不合適的。解決方案:
2.3.2 數據全量導入 TiDB 時,因爲 TiDB 會默認使用一個隱式的自增 rowid,大量 INSERT 時把數據集中寫入單個 Region,形成寫入熱點。
解決方案:
SHARD_ROW_ID_BITS
,能夠把 rowid 打散寫入多個不一樣的 Region,緩解寫入熱點問題:ALTER TABLE table_name SHARD_ROW_ID_BITS = 8;
。2.3.3 異地機房因爲網絡延遲相對比較高,設計中賦予它的主要職責是災備,並不提供服務。曾經出現過一次大約持續 10s 的網絡抖動,TiDB 端發現大量的 no Leader 日誌,Region follower 節點出現網絡隔離狀況,隔離節點 term 自增,從新接入集羣時候會致使 Region 從新選主,較長時間的網絡波動,會讓上面的選主發生屢次,而選主過程當中沒法提供正常服務,最後可能致使雪崩。
問題分析:
<center>圖 3 Raft 算法中,Follower 出現網絡隔離的場景圖</center>
解決方案:
在線業務集羣,承載了用戶餘額變動、個人消息、用戶生命週期、信用分等 P1 級業務,數據規模和訪問量都在可控範圍內。產出的 TiDB Binlog 能夠經過 Gravity 以增量形式同步給大數據團隊,經過分析模型計算出用戶新的信用分按期寫回 TiDB 集羣。
<center>圖 4 在線業務集羣拓撲圖</center>
數據沙盒,屬於離線業務集羣,是摩拜單車的一個數據聚合集羣。目前運行着近百個 TiKV 實例,承載了 60 多 TB 數據,由公司自研的 Gravity 數據複製中心將線上數據庫實時彙總到 TiDB 供離線查詢使用,同時集羣也承載了一些內部的離線業務、數據報表等應用。目前集羣的總寫入 TPS 平均在 1-2w/s,QPS 峯值 9w/s+,集羣性能比較穩定。該集羣的設計優點有以下幾點:
可供開發人員安全的查詢線上數據。
特殊場景下的跨庫聯表 SQL。
大數據團隊的數據抽取、離線分析、BI 報表。
能夠隨時按需增長索引,知足多維度的複雜查詢。
離線業務能夠直接將流量指向沙盒集羣,不會對線上數據庫形成額外負擔。
分庫分表的數據聚合。
數據歸檔、災備。
<center>圖 5 數據沙盒集羣拓撲圖</center>
4.1.1 TiDB server oom 重啓
不少使用過 TiDB 的朋友可能都遇到過這一問題,當 TiDB 在遇到超大請求時會一直申請內存致使 oom, 偶爾由於一條簡單的查詢語句致使整個內存被撐爆,影響集羣的整體穩定性。雖然 TiDB 自己有 oom action 這個參數,可是咱們實際配置過並無效果。
因而咱們選擇了一個折中的方案,也是目前 TiDB 比較推薦的方案:單臺物理機部署多個 TiDB 實例,經過端口進行區分,給不穩定查詢的端口設置內存限制(如圖 5 中間部分的 TiDBcluster1 和 TiDBcluster2)。例:
[tidb_servers] tidb-01-A ansible_host=$ip_address deploy_dir=/$deploydir1 tidb_port=$tidb_port1 tidb_status_port=$status_port1 tidb-01-B ansible_host=$ip_address deploy_dir=/$deploydir2 tidb_port=$tidb_port2 tidb_status_port=$status_port2 MemoryLimit=20G
實際上 tidb-01-A
、tidb-01-B
部署在同一臺物理機,tidb-01-B
內存超過閾值會被系統自動重啓,不影響 tidb-01-A
。
TiDB 在 2.1 版本後引入新的參數 tidb_mem_quota_query
,能夠設置查詢語句的內存使用閾值,目前 TiDB 已經能夠部分解決上述問題。
4.1.2 TiDB-Binlog 組件的效率問題
你們平時關注比較多的是如何從 MySQL 遷移到 TiDB,但當業務真正遷移到 TiDB 上之後,TiDB 的 Binlog 就開始變得重要起來。TiDB-Binlog 模塊,包含 Pump&Drainer 兩個組件。TiDB 開啓 Binlog 後,將產生的 Binlog 經過 Pump 組件實時寫入本地磁盤,再異步發送到 Kafka,Drainer 將 Kafka 中的 Binlog 進行歸併排序,再轉換成固定格式輸出到下游。
使用過程當中咱們碰到了幾個問題:
Pump 發送到 Kafka 的速度跟不上 Binlog 產生的速度。
Drainer 處理 Kafka 數據的速度太慢,致使延時太高。
單機部署多 TiDB 實例,不支持多 Pump。
其實前兩個問題都是讀寫 Kafka 時產生的,Pump&Drainer 按照順序、單 partition 分別進行讀&寫,速度瓶頸很是明顯,後期增大了 Pump 發送的 batch size,加快了寫 Kafka 的速度。但同時又遇到一些新的問題:
當源端 Binlog 消息積壓太多,一次往 Kafka 發送過大消息,致使 Kafka oom。
當 Pump 高速大批寫入 Kafka 的時候,發現 Drainer 不工做,沒法讀取 Kafka 數據。
和 PingCAP 工程師一塊兒排查,最終發現這是屬於 sarama 自己的一個 bug,sarama 對數據寫入沒有閾值限制,可是讀取卻設置了閾值:https://github.com/Shopify/sarama/blob/master/real_decoder.go#L88。
最後的解決方案是給 Pump 和 Drainer 增長參數 Kafka-max-message
來限制消息大小。單機部署多 TiDB 實例,不支持多 Pump,也經過更新 ansible 腳本獲得瞭解決,將 Pump.service
以及和 TiDB 的對應關係改爲 Pump-8250.service
,以端口區分。
針對以上問題,PingCAP 公司對 TiDB-Binlog 進行了重構,新版本的 TiDB-Binlog 再也不使用 Kafka 存儲 binlog。Pump 以及 Drainer 的功能也有所調整,Pump 造成一個集羣,能夠水平擴容來均勻承擔業務壓力。另外,原 Drainer 的 binlog 排序邏輯移到 Pump 來作,以此來提升總體的同步性能。
4.1.3 監控問題
當前的 TiDB 監控架構中,TiKV 依賴 Pushgateway 拉取監控數據到 Prometheus,當 TiKV 實例數量愈來愈多,達到 Pushgateway 的內存限制 2GB 進程會進入假死狀態,Grafana 監控就會變成圖 7 的斷點樣子:
<center>圖 6 監控拓撲圖</center>
<center>圖 7 監控展現圖</center>
目前臨時處理方案是部署多套 Pushgateway,將 TiKV 的監控信息指向不一樣的 Pushgateway 節點來分擔流量。這個問題的最終仍是要用 TiDB 的新版本(2.1.3 以上的版本已經支持),Prometheus 可以直接拉取 TiKV 的監控信息,取消對 Pushgateway 的依賴。
下面簡單介紹一下摩拜單車自研的數據複製組件 Gravity(DRC)。
Gravity 是摩拜單車數據庫團隊自研的一套數據複製組件,目前已經穩定支撐了公司數百條同步通道,TPS 50000/s,80 線延遲小於 50ms,具備以下特色:
使用場景:
Gravity 的設計初衷是要將多種數據源聯合到一塊兒,互相打通,讓業務設計上更靈活,數據複製、數據轉換變的更容易,可以幫助你們更容易的將業務平滑遷移到 TiDB 上面。該項目 已經在 GitHub 開源,歡迎你們交流使用。
TiDB 的出現,不只彌補了 MySQL 單機容量上限、傳統 Sharding 方案查詢維度單一等缺點,並且其計算存儲分離的架構設計讓集羣水平擴展變得更容易。業務能夠更專一於研發而沒必要擔憂複雜的維護成本。將來,摩拜單車還會繼續嘗試將更多的核心業務遷移到 TiDB 上,讓 TiDB 發揮更大價值,也祝願 TiDB 發展的愈來愈好。