當前位置:
首頁 > 最新 > 如何對分散式 NewSQL 資料庫 TiDB 進行性能調優

如何對分散式 NewSQL 資料庫 TiDB 進行性能調優

協作翻譯

原文:How to Do Performance Tuning on TiDB, A Distributed NewSQL Database

鏈接:https://dzone.com/articles/how-to-do-performance-tuning-on-tidb-a-distributed

譯者:Tocy, 邊城, 白又白呀, imqipan, rever4433, 雨田桑, 涼涼_

在分散式系統中進行調優不是開玩笑的事情。分散式系統中調優比單節點伺服器調優複雜得多,它的瓶頸可能出現在任何地方,單個節點上的系統資源,子組件,或者節點間的協作,甚至網路帶寬這些都可能成為瓶頸。

性能調優就是發現並解決這些瓶頸的實踐,直到系統達到最佳性能水平。我會在本文中分享如何對 TiDB 的「寫入」操作進行調優,使其達到最佳性能的實踐。

TiDB是開源的混合事務處理/分析處理(HTAP)的 NewSQL 資料庫。一個 TiDB 集群擁有幾個 TiDB 服務、幾個 TiKV 服務和一組 Placement Deiver(PD)(通常 3-5 個節點)。

TiDB 服務是無狀態 SQL 層,TiKV 服務是鍵值對存儲層,PD 則是管理組件,從頂層視角負責存儲元數據以及負載均衡。下面是一個 TiDB 集群的架構,你可以在TiDB 官方文檔中找到每個組成部分的詳細描述。

採集監控數據

Prometheus是一個開源的系統監測的解決方案,採集每個內部組件的監控數據,並定期發給Prometheus。藉助開源的時序分析平台Grafana,我們可以輕易觀測到這些數據的表現。使用Ansible部署 TiDB 時,Prometheus 和 Grafana 是默認安裝選項。通過觀察這些數據的變化,我們可以看到每個組件是否處於運行狀態,可以定位瓶頸所在,可以調整參數來解決問題。

?插入 SQL 語句的寫入流(Writeflow)

假設我們使用如下 SQL 來插入一條數據到表 t

上面是一個簡單而直觀的簡述,介紹了 TiDB 如何處理 SQL 語句。TiDB 伺服器收到 SQL 語句後,根據索引的編號將語句轉換為一個或多個鍵值對(KV),這些鍵值對被發送到相關聯的 TiKV 伺服器,這些伺服器以 Raft 日誌的形式複製保存。最後,Raf 日誌被提交,這些鍵值對會被寫入指定的存儲引擎。

在此過程中,有 3 類關鍵的過程要處理:轉換 SQL 為多個鍵值對、Region 複製和二階段提交。接下來讓我們深入探討各細節。

?從 SQL 轉換為鍵值對

與其他資料庫系統不同,TiDB 只存儲鍵值對,以提供無限的水平可伸縮性以及強大的一致性。那麼要如何實現諸如資料庫、表和索引等高層概念呢?在 TiDB 中,每個表都有一個關聯的全局唯一編號,被稱為 「table-id」。特定表中的所有數據(包括記錄和索引)的鍵都是以 8 位元組的 table-id 開頭的。每個索引都有一個名為 「index-id」 的表範圍的唯一編號。下面展示了記錄鍵和索引鍵的編碼規則。

?Region(區域)的概念

在 TiDB 中,Region 表示一個連續的、左閉右開的鍵值範圍 [start_key,end_key)。每個 Region 有多個副本,並且每個副本稱為一個 peer 。每個 Region 也歸屬於單獨的 Raft 組,以確保所有 peer 之間的數據一致性。(有關如何在 TiKV 中實現 Raft 一致性演算法的更多信息,請參閱 PingCAP 傑出工程師唐劉的相關博文。)由於我之前提到的編碼規則的原因,同一表的臨近記錄很可能位於同一 Region 中。

當集群第一次初始化時,只存在一個 Region 。當 Region 達到特定大小(當前默認值為96MB)時, Region 將動態分割為兩個鄰近的 Region ,並自動將數據分布到系統中以提供水平擴展。

?二階段提交

我們的事務處理模型設計靈感來源於Percolator,並在此基礎上進行了一些優化。簡單地說,這是一個二階段提交協議,即預寫入和提交。

每個組件中都有更多的內容,但從宏觀層次來理解足以為性能調優設置場景。現在我們來深入研究四種調優技術。

調優技巧 #1: 調度器

所有寫入命令都被發送到調度器模型,然後被複制。調度器模型由一個調度線程和幾個工作線程組成。為什麼需要調度器模型?在向資料庫寫入數據之前,需要檢查是否允許這些寫命令,以及這些寫命令是否滿足事務約束。所有這些檢查工作都需要從底層存儲引擎讀取信息,它們通過調度由工作線程來進行處理。

如果看到所有工作線程的 CPU 使用量總和超過 scheduler-worker-pool-size * 80% 時,就需要通過增加調度工作線程的數理來提高性能。

可以通過修改配置文件中 『storage』 節的 『scheduler-worker-pool-size』 來改變調度工作線程的數量。對於 CPU 核心數目小於 16 的機器,默認情況下配置了 4 個調度工作線程,其它情況下默認值是 8。參閱相關代碼部分:scheduler-worker-pool-size = 4

調優技巧 #2:raftstore進程與apply進程

像我前邊提到的,我們在多節點之間使用Raft實現強一致性。在將一個鍵值對寫入資料庫之前,這個鍵值對首先要被複製成Raft log格式,同時還要被寫入各個節點硬碟中保存。在Raft log被提交後,相關的鍵值對才能被寫入資料庫。

這樣就產生兩種寫入操作:一個是寫Raft log,一個是把鍵值對寫入資料庫。為了在TiKV中獨立地執行這兩種操作,我們創建一個raftstore進程,它的工作是攔截所有Raft信息,並寫Raft log到硬碟中;同時我們創建另一個進程apply worker,它的職責是把鍵值對寫到資料庫中。在Grafana中,這兩個進程顯示在TiKV面板的子面板Thread CPU中(如下圖所示)。它們都是極其重要的寫操作負載,在Grafana中我們很容易就能發現它們相當繁忙。

為什麼需要特別關注這兩個進程?當一些TiKV伺服器的apply或者raftstore進程很繁忙,而另一些機器卻很空閑的時候,也就是說寫操作負載不均衡的時候,這些比較繁忙的伺服器就成了集群中的瓶頸。造成這種情況的一種原因是使用了單調遞增的列,比如使用AUTOINCREMENT指定主鍵,或者在值不斷增加的列上創建索引,例如最後一次訪問的時間戳。

要優化這樣的場景並消除瓶頸,必須避免在單調增加的列上設計主鍵和索引。

在傳統單節點資料庫系統上,使用AUTOINCREMENT關鍵字可以為順序寫入帶來極大好處,但是在分散式資料庫系統中,使所有組件的負載均衡才是最重要的。

調優技巧#3:RocksDB

RocksDB是一個高性能,有大量特性的永久性 KV 存儲。 TiKV 使用 RocksDB 作為底層存儲引擎,和其他諸多功能,比如列族、範圍刪除、前綴索引、memtable 前綴布隆過濾器,sst 用戶定義屬性等等。 RocksDB 提供詳細的性能調優文檔。

每個 TiKV 伺服器下面都有兩個 RocksDB 實例:一個存儲數據,我們稱之為 kv-engine ,另一個存儲 Raft 日誌,我們稱之為 raft-engine 。kv-engine 有4個列族:「default」 、「lock」、 「write」 和 「raft」 。大多數記錄存儲在 「default」 列族中,所有索引都存儲在 「write」 列族中。

你可以通過修改配置文件關聯部分中的 block-cache-size 值來調整這兩個 RocksDB 實例,以實現最佳性能。相關部分是: [rocksdb.defaultcf]block-cache-size = 「1GB」和 [rocksdb.writecf]block-cache-size = 「1GB」

我們調整 block-cache-size 的原因是因為 TiKV 伺服器頻繁地從「write」 列族中讀取數據以檢查插入時是否滿足事務約束,所以為 「write」 列族的塊緩存設置合適的大小非常重要。當 「write」 列族的 block-cache 命中率低於 90% 時,應該增加 「write」 列族的 block-cache-size 大小。

「write」列族的 block-cache-size 的默認值為總內存的 15% ,而 「default」 列族的默認值為 25% 。例如,如果我們在 32GB 內存的機器上部署 TiKV 節點,那麼對於 「default」 列族, 「write」 列族的 block-cache-size 的值約為 4.8GB 到 8GB 。

在繁重的寫入工作量中, 「default」 列族中的數據很少被訪問,所以當我們確定 「write」 列族的緩存命中率低於 90%(例如50%)時,我們知道 「write」 列族大約是默認 4.8 GB的兩倍。

為了調優以獲得更好的性能,我們可以明確地將 「write」 列族的 block-cache-size 設置為 9GB 。但是,我們還需要將 「default」 列族的 block-cache-size 大小減少到 4GB ,以避免 OOM(內存不足)風險。你可以在 Grafana 的 「RocksDB-kv」 面板中找到 RocksDB 的詳細統計信息,以幫助進行調優。

RocksDB-kv 面板

調優技巧#4: 批量插入

使用批量插入可以實現更好的寫入性能。從 TiDB 伺服器的角度來看,批量插入不僅可以減少客戶端與 TiDB 伺服器之間的 RPC 延遲,還可以減少 SQL 解析時間。在 TiKV 內部,批量插入可以通過將多個記錄合併到一個 Raft 日誌條目中來減少 Raft 信息的總數量。

根據我們的經驗,建議將批量大小保持在 50?100 行之內。當一個表中有超過 10 個索引時,應減少批量處理的大小,因為按照上述編碼規則,插入一行類似數據將創建超過 10 個鍵值對。

總結

我希望本文能夠在使用TiDB時幫助你了解一些常見的瓶頸狀況,以及如何調優這些問題,以便在「寫入」過程中實現最優性能。綜上所述:

不要讓一些 TiKV 節點處理大部分「寫入」工作負載,避免在單調增加的列上設計主鍵和索引。

當 TiKV 調度模型中的調度器的總 CPU 使用率超過 scheduler-worker-pool-size*80% 時,請增加 scheduler-worker-pool-size 的值。

當寫入任務頻繁讀取"write"列族並且塊緩存命中率低於 90% 時,在 RocksDB 中增加 block-cache-size 的值。

使用批量插入來提高「寫入」操作的性能。

我們的許多客戶,從電子商務市場和遊戲,到金融科技、媒體和旅行,已經在生產中使用這些調優技術,以充分利用 TiDB 的設計、體系結構和優化。期待在不久的將來分享他們的使用案例和經驗。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 開源中國 的精彩文章:

分散式共享 Session之SpringSession 源碼細節
Spring Boot 2.0.0 終於正式發布,重大修訂版本

TAG:開源中國 |