使用TiDB把自己寫分庫分表方案推翻了

地藏Kelvin發表於2020-04-05

背景

在日益資料量增長的情況下,影響資料庫的讀寫效能,我們一般會有分庫分表的方案和使用newSql方案,newSql如TIDB。那麼為什麼需要使用TiDB呢?有什麼情況下才用TiDB呢?解決傳統分庫分表的什麼問題呢?還會解釋一些關鍵點和踩坑點。下面我會用比較白話的形式解讀,當做對TiDB進行推廣。

目前痛點

目前分庫表無論使用原生JDBC+ThreadLocal方案,還是使用中介軟體proxy、還是SDK嵌入程式碼的形式,即使用sharding-jdbc、zdal、mycat都存在著以下問題。

  1. 分庫分表演算法方案的選型
  2. 分庫分表後帶來的後續維護工作,每次增加節點,都需要申請磁碟、機器
  3. 新增節點需要進行停機、然後遷移資料,停機遷移對線上使用者造成實時的讀寫影響。遷移失敗還有程式碼回滾。遷移前還要等mysql沒有binlog產生後才能遷移。
  4. 分庫分表後,跨庫一致性問題,都是使用最終一致性,程式碼維護繁瑣。
  5. 資料儲存壓力、資料存放量偏移於某個節點
  6. 資料索引查詢效率

目的

希望解決上述痛點

TiDB整體架構

TiDB是一種分散式資料庫。其實形式上來講比較像Hadoop的做法,把資料分佈在不同的機器上,並且有副本,有負責計算的機器、也由負責儲存的機器。

image.png

入口層為tidb-server,圖中TiDB,是客戶端接入的入口,負責處理請求介面,這一層對儲存要求不高,用於計算所以對CPU要求高,還有記錄每個region的負責的範圍。 第二層是PD,負責排程,如zookeeper的形式,負責資料遷移的排程、選舉的排程。 第三層是tikv,也叫store,負責真實儲存資料的一層。其中tikv由1個或者多個region組成,Region為最小的儲存單元,就如JVM G1演算法的Region的意思。每個Region將會打散分佈在各個tikv下。

資料儲存模型

image.png

  1. 行資料(後設資料) 一個表將會由一個或者多個Region儲存。不同的表將會在不同的Region,而不是如傳統分庫那樣每個庫裡的表都是相同。 那麼一個表下,每一行資料儲存在哪個Region下是如何確定呢? 首先,Region裡面是一個Map, key 由 table_id表id、rowid主鍵組成。如:

t[table_id]_r[row_id]

map的value為表中每行資料的真實資料。

  1. 索引資料 索引資料將會在另外一個Region儲存,每建一個索引,就會有那個索引對應的Region。它的Map的 key 由 table_id、index_id 以及索引列的值編碼組成。如:

t[table_id]_i[index_id][index_value]

value為rowid,這樣就能用rowid來找到上面的表資料的位置。 就如mysql按索引查詢,會先去找索引記錄,再去找到主鍵聚簇索引來獲取真實資料一個邏輯。

資料切分

定位在哪個Region,就是靠Key來算出落在哪個Region裡面。和分庫分表的根據某個欄位來一致性hash演算法方案不同。 TiDB的負責行真實資料的Region是使用主鍵範圍來劃分的。 有索引情況下,負責索引的Region會根據索引欄位範圍來劃分。 基於Key通過計算,將會得出一個數字,然後按範圍劃分多個區間,每個區間由一個Region管理。

如:一個表資料主鍵rowid落在3個Region。 [0,10000) [10001,20000) [20001,30000)

這個範圍需要資料入表前確定這個規則。

因為Region將會分佈在所有TiKV上,也就有多個伺服器去存資料,所以利用多機器CPU和磁碟,解決了痛點5儲存壓力,也解決了痛點1分庫分表用哪個演算法方案,只需要確定主鍵範圍即可。

後續會說如何擴容。

提高索引效率

現有問題: 傳統分庫分表的索引都是在每個mysql例項裡,跟著表走的。分庫分表規則,一般都是根據表中的userid使用者欄位或組合性較高的欄位來做切分庫或者表的鍵,相同的userid將會落在相同的庫或者表。

但是上述情況下,表中的索引欄位假設為code,則code="aaa"的可能會因為不同的userid落在不同的庫中,需要查詢全量的庫和表後,再重新聚合,這樣就會增加CPU查詢的消耗、還有TCP連線握手的消耗。

TiDB解決: 然而TiKV的有專門用於儲存索引的Region,它資料結構的Key是由 表id+索引id+索引值id來決定的,value是rowid資料行主鍵,並且一個Region管理一個範圍的Key,所以同一個索引同一個值都會在一個Region裡面,這樣就比較好快速定位相同的索引值的Region,得出對應的rowid,再根據rowid去儲存表資料的Region中更快速找到表真實資料。就不需要走全量庫的索引查詢,因為mysql索引查詢機制是先找到索引值,然後再找聚簇的主鍵後返回整行資料,從而提高效能。

這種做法有點像elastic-search的倒排索引,先根據value值再定位資料原來位置。這裡解決痛點6減少索引查詢壓力

TIDB特性

1. 提供樂觀事務模型和悲觀事務模型

在3.0.8之前只有樂觀事務模型,都是通過2PC兩次提交的方式來進行事務提交。如果開啟悲觀事務模型,會比較像sharding-jdbc的柔性事務,有重試的功能,但是依然重試過多次(256次)失敗仍然會丟失。

1.1 優缺點分析

TiDB 事務有如下優點:

  • 實現原理簡單,易於理解。
  • 基於單例項事務實現了跨節點事務。
  • 鎖管理實現了去中心化。 但 TiDB 事務也存在以下缺點:
  • 兩階段提交使網路互動增多。
  • 需要一箇中心化的版本管理服務。
  • 事務資料量過大時易導致記憶體暴漲。

1.2 事務的重試

使用樂觀事務模型時,在高衝突率的場景中,事務很容易提交失敗。而 MySQL 內部使用的是悲觀事務模型,在執行 SQL 語句的過程中進行衝突檢測,所以提交時很難出現異常。為了相容 MySQL 的悲觀事務行為,TiDB 提供了重試機制。 這種加重試就是悲觀事務。

上述解決痛點4,不用再去自己維護跨庫處理的事務最終一致性的程式碼,如A使用者轉賬到B使用者,也如商家和買家的情況,商家比較多收入時的交易情況。 雖然重試多次仍然會失敗,但是這部分由TiDB處理。如果跨庫事務以前的系統有框架處理,那現在就不需要如sharding-jdbc的sdk方式需要靠程式執行時才能重試,不然如果我們程式down機重試就沒了。

2. 自動擴容

2.1 Region分裂

Region為最小的儲存單元,當資料進入一個Region後達到一定數量,就會開始分裂(預設是超過現有Region負責範圍的1/16)。 注意:資料表 Key 的 Range 範圍劃分,需要提前設定好,TiKV 是根據 Region 的大小動態分裂的。

這裡是解決痛點2的每次都需要申請資源,不再運維來做上線前遷移資料,痛點3遷移時又要停機影響生成使用者

因為TiDB作為中介軟體,不帶任何業務屬性,所以就不能使用userid等欄位來做分片規則的鍵和自定義演算法,使用主鍵是最通用的選擇。(其實我覺得如果TiDB能做到就最好了)

2.2 新增儲存節點

  1. 新增節點或者分裂Region,都有可能會觸發遷移Region,由TiDB自動完成。不再需要入侵程式碼、或者使用中介軟體做分庫分表邏輯和資料遷移、上線演練,全程交給運維(手動甩鍋)。
  2. 並且不需要程式碼服務停機,不需要等沒有新sql執行後才能遷移,這個是執行過程中實時遷移資料的。

這裡就解決了痛點3停機遷移資料、痛點5儲存壓力

3. 副本容災

每個 Region 負責維護叢集的一段連續資料(預設配置下平均約 96 MiB),每份資料會在不同的 Store 儲存多個副本(預設配置是 3 副本),每個副本稱為 Peer。同一個 Region 的多個 Peer 通過 raft 協議進行資料同步,所以 Peer 也用來指代 raft 例項中的成員。

所以如果有1億資料,將會由3億資料落在磁碟中,雖然消耗磁碟,但是提高了可靠性。

TIDB成本

  1. 官方推薦至少部署 3 個 TiKV, 3 個 PD,2 個 TiDB。
  2. TiDB需要能使用執行緒數多的,PD需要CPU比較好的,TiKV需要SSD和CPU比較好的。
  3. 在論壇看到大家用的記憶體都是100G的,磁碟都是2T 的SSD。因為每行資料都總共有3個副本,消耗磁碟多。所以一個系統使用一套TiDB需要不少的成本。
  4. 然而這只是一個系統所需,一個專案中有多個系統組成情況下,就消耗更多資源了。並且隨著資料日益增多將會越來越多資源。

使用場景

  1. 資料量達到一定量級,需要減少查詢壓力或者連線池不夠等等因素後才需要進行。因為官方建議需要有2個tidb-server、至少兩個PD、三個tikv,而且tikv需要都是SSD固態硬碟。所以在這種成本下,不一定所有專案都會使用,公司不一定願意花成本去使用。而在一些資料量小的情況,建議還是使用mysql,等到資料量上來後,再做資料同步到TiDB。
  2. 已經分庫分表後,希望改為使用TiDB,也能進行合併,需要使用TiDB Data Migration。
  3. 先在公司的架構組的專案使用,再到不是核心業務的專案使用,最後鋪開給核心專案使用。
  4. 入門成本高,實驗起來需要成本,因為官方推薦的部署方式需要多臺好的機器。

注意事項與坑點

  1. 建議使用3.0.4、3.0.8或者4.0.0 (現在是2020年4月2日),不建議使用2.0版本,不然會出現升級不相容的問題,需要去解決。
  2. 在增加節點擴容時,或者Region分裂時,同時有SQL執行insert或者更新資料,如果命中到對應的Region正在遷移,就可能會出現insert或者update出錯 說“not leader”沒有找到對應的位置的意思。但是會有重試的形式,把資料最終提交。
  3. 提前設定路由Region的分片範圍規則,不然匯入資料時會都落在一個節點上。如果你以前的主鍵資料時雪花演算法得出的,那就需要求出最大最小值自己算範圍手動設定好範圍規則。
  4. TiDB 不支援 SELECT LOCK IN SHARE MODE。使用這個語句執行的時候,效果和沒有加鎖是一樣的,不會阻塞其他事務的讀寫。
  5. 不要再使用Syncer同步資料或遷移到TiDB,因為如果在已經分庫分表的情況下,使用Syncer同步,在某個帖子看的說會出問題。建議使用TiDB Data Migration
  6. TiDB無法修改欄位型別

總結

其實有了上述功能,就可以減少分庫分表的開發、運維維護成本,主要是平常分庫分表到一定量要遷移,經常需要監控是否到遷移的量了,遷移時需要演練,遷移時要更新程式碼或者配置並且停業務是影響最大的。 雖然完成分庫分表好像解決了一些問題,但是帶來的後續還是很多的,TiDB就給我們解決了上面的問題,這樣就可以更加專注的做業務了。


歡迎關注,文章更快一步

我的公眾號 :地藏思維

地藏思維

掘金:地藏Kelvin

簡書:地藏Kelvin

我的Gitee: 地藏Kelvin gitee.com/kelvin-cai

相關文章