背景
在日益數(shù)據(jù)量增長(zhǎng)的情況下造烁,影響數(shù)據(jù)庫(kù)的讀寫性能午笛,我們一般會(huì)有分庫(kù)分表的方案和使用newSql方案,newSql如TIDB告组。那么為什么需要使用TiDB呢?有什么情況下才用TiDB呢惹谐?解決傳統(tǒng)分庫(kù)分表的什么問題呢?還會(huì)解釋一些關(guān)鍵點(diǎn)和踩坑點(diǎn)氨肌。下面我會(huì)用比較白話的形式解讀,當(dāng)做對(duì)TiDB進(jìn)行推廣怎囚。
目前痛點(diǎn)
目前分庫(kù)表無(wú)論使用原生JDBC+ThreadLocal方案,還是使用中間件proxy恳守、還是SDK嵌入代碼的形式,即使用sharding-jdbc催烘、zdal、mycat都存在著以下問題伊群。
- 分庫(kù)分表算法方案的選型
- 分庫(kù)分表后帶來的后續(xù)維護(hù)工作,每次增加節(jié)點(diǎn)舰始,都需要申請(qǐng)磁盤、機(jī)器
- 新增節(jié)點(diǎn)需要進(jìn)行停機(jī)丸卷、然后遷移數(shù)據(jù),停機(jī)遷移對(duì)線上用戶造成實(shí)時(shí)的讀寫影響谜嫉。遷移失敗還有代碼回滾。遷移前還要等mysql沒有binlog產(chǎn)生后才能遷移骄恶。
- 分庫(kù)分表后,跨庫(kù)一致性問題僧鲁,都是使用最終一致性,代碼維護(hù)繁瑣寞秃。
- 數(shù)據(jù)存儲(chǔ)壓力、數(shù)據(jù)存放量偏移于某個(gè)節(jié)點(diǎn)
- 數(shù)據(jù)索引查詢效率
目的
希望解決上述痛點(diǎn)
TiDB整體架構(gòu)
TiDB是一種分布式數(shù)據(jù)庫(kù)春寿。其實(shí)形式上來講比較像Hadoop的做法,把數(shù)據(jù)分布在不同的機(jī)器上绑改,并且有副本,有負(fù)責(zé)計(jì)算的機(jī)器厘线、也由負(fù)責(zé)存儲(chǔ)的機(jī)器。
入口層為tidb-server渡讼,圖中TiDB,是客戶端接入的入口成箫,負(fù)責(zé)處理請(qǐng)求接口,這一層對(duì)存儲(chǔ)要求不高蹬昌,用于計(jì)算所以對(duì)CPU要求高,還有記錄每個(gè)region的負(fù)責(zé)的范圍凳厢。
第二層是PD,負(fù)責(zé)調(diào)度先紫,如zookeeper的形式筹煮,負(fù)責(zé)數(shù)據(jù)遷移的調(diào)度、選舉的調(diào)度败潦。
第三層是tikv,也叫store劫扒,負(fù)責(zé)真實(shí)存儲(chǔ)數(shù)據(jù)的一層。其中tikv由1個(gè)或者多個(gè)region組成沟饥,Region為最小的存儲(chǔ)單元,就如JVM G1算法的Region的意思贤旷。每個(gè)Region將會(huì)打散分布在各個(gè)tikv下。
數(shù)據(jù)存儲(chǔ)模型
- 行數(shù)據(jù)(元數(shù)據(jù))
一個(gè)表將會(huì)由一個(gè)或者多個(gè)Region存儲(chǔ)艾杏。不同的表將會(huì)在不同的Region,而不是如傳統(tǒng)分庫(kù)那樣每個(gè)庫(kù)里的表都是相同购桑。
那么一個(gè)表下,每一行數(shù)據(jù)存儲(chǔ)在哪個(gè)Region下是如何確定呢勃蜘?
首先,Region里面是一個(gè)Map元旬, key 由 table_id表id、rowid主鍵組成匀归。如:
t[table_id]_r[row_id]
map的value為表中每行數(shù)據(jù)的真實(shí)數(shù)據(jù)。
- 索引數(shù)據(jù)
索引數(shù)據(jù)將會(huì)在另外一個(gè)Region存儲(chǔ)穆端,每建一個(gè)索引,就會(huì)有那個(gè)索引對(duì)應(yīng)的Region体啰。它的Map的 key 由 table_id、index_id 以及索引列的值編碼組成荒勇。如:
t[table_id]_i[index_id][index_value]
value為rowid,這樣就能用rowid來找到上面的表數(shù)據(jù)的位置沽翔。
就如mysql按索引查詢,會(huì)先去找索引記錄仅偎,再去找到主鍵聚簇索引來獲取真實(shí)數(shù)據(jù)一個(gè)邏輯。
數(shù)據(jù)切分
定位在哪個(gè)Region橘沥,就是靠Key來算出落在哪個(gè)Region里面。和分庫(kù)分表的根據(jù)某個(gè)字段來一致性hash算法方案不同座咆。
TiDB的負(fù)責(zé)行真實(shí)數(shù)據(jù)的Region是使用主鍵范圍來劃分的。
有索引情況下箫措,負(fù)責(zé)索引的Region會(huì)根據(jù)索引字段范圍來劃分。
基于Key通過計(jì)算斤蔓,將會(huì)得出一個(gè)數(shù)字,然后按范圍劃分多個(gè)區(qū)間弦牡,每個(gè)區(qū)間由一個(gè)Region管理。
如:一個(gè)表數(shù)據(jù)主鍵rowid落在3個(gè)Region驾锰。
[0,10000)
[10001,20000)
[20001,30000)
這個(gè)范圍需要數(shù)據(jù)入表前確定這個(gè)規(guī)則。
因?yàn)镽egion將會(huì)分布在所有TiKV上椭豫,也就有多個(gè)服務(wù)器去存數(shù)據(jù)旨指,所以利用多機(jī)器CPU和磁盤喳整,解決了痛點(diǎn)5存儲(chǔ)壓力,也解決了痛點(diǎn)1分庫(kù)分表用哪個(gè)算法方案框都,只需要確定主鍵范圍即可。
后續(xù)會(huì)說如何擴(kuò)容魏保。
提高索引效率
現(xiàn)有問題:
傳統(tǒng)分庫(kù)分表的索引都是在每個(gè)mysql實(shí)例里,跟著表走的谓罗。分庫(kù)分表規(guī)則,一般都是根據(jù)表中的userid用戶字段或組合性較高的字段來做切分庫(kù)或者表的鍵檩咱,相同的userid將會(huì)落在相同的庫(kù)或者表。
但是上述情況下税手,表中的索引字段假設(shè)為code芦倒,則code="aaa"的可能會(huì)因?yàn)椴煌膗serid落在不同的庫(kù)中,需要查詢?nèi)康膸?kù)和表后兵扬,再重新聚合,這樣就會(huì)增加CPU查詢的消耗口蝠、還有TCP連接握手的消耗。
TiDB解決:
然而TiKV的有專門用于存儲(chǔ)索引的Region妙蔗,它數(shù)據(jù)結(jié)構(gòu)的Key是由 表id+索引id+索引值id來決定的,value是rowid數(shù)據(jù)行主鍵眉反,并且一個(gè)Region管理一個(gè)范圍的Key,所以同一個(gè)索引同一個(gè)值都會(huì)在一個(gè)Region里面寸五,這樣就比較好快速定位相同的索引值的Region,得出對(duì)應(yīng)的rowid梳杏,再根據(jù)rowid去存儲(chǔ)表數(shù)據(jù)的Region中更快速找到表真實(shí)數(shù)據(jù)淹接。就不需要走全量庫(kù)的索引查找叛溢,因?yàn)閙ysql索引查找機(jī)制是先找到索引值,然后再找聚簇的主鍵后返回整行數(shù)據(jù)雇初,從而提高性能。
這種做法有點(diǎn)像elastic-search的倒排索引靖诗,先根據(jù)value值再定位數(shù)據(jù)原來位置。這里解決痛點(diǎn)6減少索引查詢壓力刊橘。
TIDB特性
1. 提供樂觀事務(wù)模型和悲觀事務(wù)模型
在3.0.8之前只有樂觀事務(wù)模型,都是通過2PC兩次提交的方式來進(jìn)行事務(wù)提交促绵。如果開啟悲觀事務(wù)模型,會(huì)比較像sharding-jdbc的柔性事務(wù)败晴,有重試的功能,但是依然重試過多次(256次)失敗仍然會(huì)丟失尖坤。
1.1 優(yōu)缺點(diǎn)分析
TiDB 事務(wù)有如下優(yōu)點(diǎn):
- 實(shí)現(xiàn)原理簡(jiǎn)單,易于理解慢味。
- 基于單實(shí)例事務(wù)實(shí)現(xiàn)了跨節(jié)點(diǎn)事務(wù)。
- 鎖管理實(shí)現(xiàn)了去中心化纯路。
但 TiDB 事務(wù)也存在以下缺點(diǎn): - 兩階段提交使網(wǎng)絡(luò)交互增多。
- 需要一個(gè)中心化的版本管理服務(wù)驰唬。
- 事務(wù)數(shù)據(jù)量過大時(shí)易導(dǎo)致內(nèi)存暴漲。
1.2 事務(wù)的重試
使用樂觀事務(wù)模型時(shí)定嗓,在高沖突率的場(chǎng)景中,事務(wù)很容易提交失敗宵溅。而 MySQL 內(nèi)部使用的是悲觀事務(wù)模型,在執(zhí)行 SQL 語(yǔ)句的過程中進(jìn)行沖突檢測(cè)恃逻,所以提交時(shí)很難出現(xiàn)異常藕施。為了兼容 MySQL 的悲觀事務(wù)行為,TiDB 提供了重試機(jī)制裳食。
這種加重試就是悲觀事務(wù)。
上述解決痛點(diǎn)4诲祸,不用再去自己維護(hù)跨庫(kù)處理的事務(wù)最終一致性的代碼,如A用戶轉(zhuǎn)賬到B用戶救氯,也如商家和買家的情況,商家比較多收入時(shí)的交易情況着憨。
雖然重試多次仍然會(huì)失敗,但是這部分由TiDB處理甲抖。如果跨庫(kù)事務(wù)以前的系統(tǒng)有框架處理,那現(xiàn)在就不需要如sharding-jdbc的sdk方式需要靠程序運(yùn)行時(shí)才能重試准谚,不然如果我們程序down機(jī)重試就沒了。
2. 自動(dòng)擴(kuò)容
2.1 Region分裂
Region為最小的存儲(chǔ)單元氛魁,當(dāng)數(shù)據(jù)進(jìn)入一個(gè)Region后達(dá)到一定數(shù)量,就會(huì)開始分裂(默認(rèn)是超過現(xiàn)有Region負(fù)責(zé)范圍的1/16)。
注意:數(shù)據(jù)表 Key 的 Range 范圍劃分捶码,需要提前設(shè)置好,TiKV 是根據(jù) Region 的大小動(dòng)態(tài)分裂的惫恼。
這里是解決痛點(diǎn)2的每次都需要申請(qǐng)資源,不再運(yùn)維來做上線前遷移數(shù)據(jù)祈纯,痛點(diǎn)3遷移時(shí)又要停機(jī)影響生成用戶。
因?yàn)門iDB作為中間件腕窥,不帶任何業(yè)務(wù)屬性,所以就不能使用userid等字段來做分片規(guī)則的鍵和自定義算法簇爆,使用主鍵是最通用的選擇爽撒。(其實(shí)我覺得如果TiDB能做到就最好了)
2.2 新增存儲(chǔ)節(jié)點(diǎn)
- 新增節(jié)點(diǎn)或者分裂Region,都有可能會(huì)觸發(fā)遷移Region硕勿,由TiDB自動(dòng)完成。不再需要入侵代碼源武、或者使用中間件做分庫(kù)分表邏輯和數(shù)據(jù)遷移、上線演練粱栖,全程交給運(yùn)維(手動(dòng)甩鍋)。
- 并且不需要代碼服務(wù)停機(jī)查排,不需要等沒有新sql執(zhí)行后才能遷移,這個(gè)是運(yùn)行過程中實(shí)時(shí)遷移數(shù)據(jù)的跋核。
這里就解決了痛點(diǎn)3停機(jī)遷移數(shù)據(jù)、痛點(diǎn)5存儲(chǔ)壓力砂代。
3. 副本容災(zāi)
每個(gè) Region 負(fù)責(zé)維護(hù)集群的一段連續(xù)數(shù)據(jù)(默認(rèn)配置下平均約 96 MiB),每份數(shù)據(jù)會(huì)在不同的 Store 存儲(chǔ)多個(gè)副本(默認(rèn)配置是 3 副本)刻伊,每個(gè)副本稱為 Peer。同一個(gè) Region 的多個(gè) Peer 通過 raft 協(xié)議進(jìn)行數(shù)據(jù)同步捶箱,所以 Peer 也用來指代 raft 實(shí)例中的成員。
所以如果有1億數(shù)據(jù)动漾,將會(huì)由3億數(shù)據(jù)落在磁盤中,雖然消耗磁盤旱眯,但是提高了可靠性。
TIDB成本
- 官方推薦至少部署 3 個(gè) TiKV删豺, 3 個(gè) PD,2 個(gè) TiDB呀页。
- TiDB需要能使用線程數(shù)多的,PD需要CPU比較好的蓬蝶,TiKV需要SSD和CPU比較好的渴逻。
- 在論壇看到大家用的內(nèi)存都是100G的,磁盤都是2T 的SSD音诫。因?yàn)槊啃袛?shù)據(jù)都總共有3個(gè)副本惨奕,消耗磁盤多竭钝。所以一個(gè)系統(tǒng)使用一套TiDB需要不少的成本。
- 然而這只是一個(gè)系統(tǒng)所需香罐,一個(gè)項(xiàng)目中有多個(gè)系統(tǒng)組成情況下,就消耗更多資源了庇茫。并且隨著數(shù)據(jù)日益增多將會(huì)越來越多資源。
使用場(chǎng)景
- 數(shù)據(jù)量達(dá)到一定量級(jí)旦签,需要減少查詢壓力或者連接池不夠等等因素后才需要進(jìn)行。因?yàn)楣俜浇ㄗh需要有2個(gè)tidb-server宁炫、至少兩個(gè)PD、三個(gè)tikv羔巢,而且tikv需要都是SSD固態(tài)硬盤。所以在這種成本下竿秆,不一定所有項(xiàng)目都會(huì)使用,公司不一定愿意花成本去使用幽钢。而在一些數(shù)據(jù)量小的情況,建議還是使用mysql搅吁,等到數(shù)據(jù)量上來后落午,再做數(shù)據(jù)同步到TiDB。
- 已經(jīng)分庫(kù)分表后溃斋,希望改為使用TiDB,也能進(jìn)行合并梗劫,需要使用TiDB Data Migration截碴。
- 先在公司的架構(gòu)組的項(xiàng)目使用,再到不是核心業(yè)務(wù)的項(xiàng)目使用日丹,最后鋪開給核心項(xiàng)目使用。
- 入門成本高哲虾,實(shí)驗(yàn)起來需要成本,因?yàn)楣俜酵扑]的部署方式需要多臺(tái)好的機(jī)器束凑。
注意事項(xiàng)與坑點(diǎn)
- 建議使用3.0.4、3.0.8或者4.0.0 (現(xiàn)在是2020年4月2日)汪诉,不建議使用2.0版本,不然會(huì)出現(xiàn)升級(jí)不兼容的問題谈秫,需要去解決。
- 在增加節(jié)點(diǎn)擴(kuò)容時(shí)孝常,或者Region分裂時(shí),同時(shí)有SQL執(zhí)行insert或者更新數(shù)據(jù)构灸,如果命中到對(duì)應(yīng)的Region正在遷移,就可能會(huì)出現(xiàn)insert或者update出錯(cuò) 說“not leader”沒有找到對(duì)應(yīng)的位置的意思喜颁。但是會(huì)有重試的形式,把數(shù)據(jù)最終提交半开。
- 提前設(shè)置路由Region的分片范圍規(guī)則,不然導(dǎo)入數(shù)據(jù)時(shí)會(huì)都落在一個(gè)節(jié)點(diǎn)上寂拆。如果你以前的主鍵數(shù)據(jù)時(shí)雪花算法得出的,那就需要求出最大最小值自己算范圍手動(dòng)設(shè)置好范圍規(guī)則纠永。
- TiDB 不支持 SELECT LOCK IN SHARE MODE。使用這個(gè)語(yǔ)句執(zhí)行的時(shí)候尝江,效果和沒有加鎖是一樣的,不會(huì)阻塞其他事務(wù)的讀寫。
- 不要再使用Syncer同步數(shù)據(jù)或遷移到TiDB啤覆,因?yàn)槿绻谝呀?jīng)分庫(kù)分表的情況下,使用Syncer同步窗声,在某個(gè)帖子看的說會(huì)出問題。建議使用TiDB Data Migration
- TiDB無(wú)法修改字段類型
總結(jié)
其實(shí)有了上述功能嫌佑,就可以減少分庫(kù)分表的開發(fā)、運(yùn)維維護(hù)成本屋摇,主要是平常分庫(kù)分表到一定量要遷移,經(jīng)常需要監(jiān)控是否到遷移的量了炮温,遷移時(shí)需要演練,遷移時(shí)要更新代碼或者配置并且停業(yè)務(wù)是影響最大的柒啤。
雖然完成分庫(kù)分表好像解決了一些問題,但是帶來的后續(xù)還是很多的担巩,TiDB就給我們解決了上面的問題,這樣就可以更加專注的做業(yè)務(wù)了涛癌。
歡迎關(guān)注,文章更快一步
我的公眾號(hào) :地藏思維
掘金:地藏Kelvin
簡(jiǎn)書:地藏Kelvin
我的Gitee: 地藏Kelvin https://gitee.com/kelvin-cai