關(guān)于架構(gòu)設(shè)計(jì)的一些思考

17021211254

轉(zhuǎn)載自:https://my.oschina.net/zhaiyuan/blog/1833302

嵌牛導(dǎo)讀:做 TiDB 的緣起是從思考一個(gè)問題開始的:為什么在數(shù)據(jù)庫(kù)領(lǐng)域有這么多永遠(yuǎn)也躲不開的坑?從 2015 年我們寫下第一行代碼,3 年以來我們迎面遇到無數(shù)個(gè)問題,一邊思考一邊做,盡量用最小的代價(jià)來快速奔跑。

作為一個(gè)開源項(xiàng)目,TiDB 是我們基礎(chǔ)架構(gòu)工程師和社區(qū)一起努力的結(jié)果,TiDB 已經(jīng)發(fā)版到 2.0,有了一個(gè)比較穩(wěn)定的形態(tài),大量在生產(chǎn)環(huán)境使用的伙伴們。可以負(fù)責(zé)任的說,我們做的任何決定都經(jīng)過了非常慎重的思考和實(shí)踐,是經(jīng)過內(nèi)部和社區(qū)一起論證產(chǎn)生的結(jié)果。它未必是最好的,但是在這個(gè)階段應(yīng)該是最適合我們的,而且大家也可以看到 TiDB 在快速迭代進(jìn)化。

嵌牛鼻子:TiDB?數(shù)據(jù)庫(kù)

這篇文章是關(guān)于 TiDB 代表性“為什么”的 TOP 10,希望大家在了解了我們這些背后的選擇之后,能更加純熟的使用 TiDB,讓它在適合的環(huán)境里更好的發(fā)揮價(jià)值。

這個(gè)世界有很多人,感覺大于思想,疑問多于答案。感恩大家保持疑問,我們承諾回饋我們的思考過程,畢竟有時(shí)候很多思考也很有意思。

一、為什么分布式系統(tǒng)并不是銀彈

其實(shí)并沒有什么技術(shù)是完美和包治百病的,在存儲(chǔ)領(lǐng)域更是如此,如果你的數(shù)據(jù)能夠在一個(gè) MySQL 裝下并且服務(wù)器的壓力不大,或者對(duì)復(fù)雜查詢性能要求不高,其實(shí)分布式數(shù)據(jù)庫(kù)并不是一個(gè)特別好的選擇。 選用分布式的架構(gòu)就意味著引入額外的維護(hù)成本,而且這個(gè)成本對(duì)于特別小的業(yè)務(wù)來說是不太劃算的,即使你說需要高可用的能力,那 MySQL 的主從復(fù)制 + GTID 的方案可能也基本夠用,這不夠的話,還有最近引入的 Group Replication。而且 MySQL 的社區(qū)足夠龐大,你能 Google 找到幾乎一切常見問題的答案。

我們做 TiDB 的初衷并不是想要在小數(shù)據(jù)量下取代 MySQL,而是嘗試去解決基于單機(jī)數(shù)據(jù)庫(kù)解決不了的一些本質(zhì)的問題。

有很多朋友問我選擇分布式數(shù)據(jù)庫(kù)的一個(gè)比較合適的時(shí)機(jī)是什么?我覺得對(duì)于每個(gè)公司或者每個(gè)業(yè)務(wù)都不太一樣,我并不希望一刀切的給個(gè)普適的標(biāo)準(zhǔn)(也可能這個(gè)標(biāo)準(zhǔn)并不存在),但是有一些事件開始出現(xiàn)的時(shí)候:比如是當(dāng)你發(fā)現(xiàn)你的數(shù)據(jù)庫(kù)已經(jīng)到了你每天開始絞盡腦汁思考數(shù)據(jù)備份遷移擴(kuò)容,開始隔三差五的想著優(yōu)化存儲(chǔ)空間和復(fù)雜的慢查詢,或者你開始不自覺的調(diào)研數(shù)據(jù)庫(kù)中間件方案時(shí),或者人肉在代碼里面做 sharding 的時(shí)候,這時(shí)給自己提個(gè)醒,看看 TiDB 是否能夠幫助你,我相信大多數(shù)時(shí)候應(yīng)該是可以的。

而且另一方面,選擇 TiDB 和選擇 MySQL 并不是一刀切的有你沒他的過程,我們?yōu)榱四茏?MySQL 的用戶盡可能減小遷移和改造成本,做了大量的工具能讓整個(gè)數(shù)據(jù)遷移和灰度上線變得平滑,甚至從 TiDB 無縫的遷移回來,而且有些小數(shù)據(jù)量的業(yè)務(wù)你仍然可以繼續(xù)使用 MySQL。所以一開始如果你的業(yè)務(wù)和數(shù)據(jù)量還小,大膽放心的用 MySQL 吧,MySQL still rocks,TiDB 在未來等你。

二、為什么是 MySQL

和上面提到的一樣,并不是 MySQL 不好我們要取代他,而是選擇兼容 MySQL 的生態(tài)對(duì)我們來說是最貼近用戶實(shí)際場(chǎng)景的選擇:

MySQL 的社區(qū)足夠大,有著特別良好的群眾基礎(chǔ),作為一個(gè)新的數(shù)據(jù)庫(kù)來說,如果需要用戶去學(xué)習(xí)一套新的語法,同時(shí)伴隨很重的業(yè)務(wù)遷移的話,是很不利于新項(xiàng)目冷啟動(dòng)的。

MySQL 那么長(zhǎng)時(shí)間積累下來大量的測(cè)試用例和各種依賴 MySQL 的第三方框架和工具的測(cè)試用例是我們一個(gè)很重要的測(cè)試資源,特別是在早期,你如何證明你的數(shù)據(jù)庫(kù)是對(duì)的,MySQL 的測(cè)試就是我們的一把尺子。

已經(jīng)有大量的已有業(yè)務(wù)正在使用 MySQL,同時(shí)也遇到了擴(kuò)展性的問題,如果放棄這部分有直接痛點(diǎn)的場(chǎng)景和用戶,也是不明智的。

另一方面來看,MySQL 自從被 Oracle 收購(gòu)后,不管是性能還是穩(wěn)定性這幾年都在穩(wěn)步的提升,甚至在某些場(chǎng)景下,已經(jīng)開始有替換 Oracle 的能力,從大的發(fā)展趨勢(shì)上來說,是非常健康的,所以跟隨著這個(gè)健康的社區(qū)一起成長(zhǎng),對(duì)我們來說也是一個(gè)商業(yè)上的策略。

三、為什么 TiDB 的設(shè)計(jì)中 SQL 層和存儲(chǔ)層是分開的

一個(gè)顯而易見的原因是對(duì)運(yùn)維的友好性。很多人覺得這個(gè)地方稍微有點(diǎn)反直覺,多一個(gè)組件不就會(huì)增加部署的復(fù)雜度嗎?

其實(shí)在實(shí)際生產(chǎn)環(huán)境中,運(yùn)維并不僅僅包含部署。舉個(gè)例子,如果在 SQL 層發(fā)現(xiàn)了一個(gè) BUG 需要緊急的更新,如果所有部件都是耦合在一起的話,你面臨的就是一次整個(gè)集群的滾動(dòng)更新,如果分層得當(dāng)?shù)脑挘憧赡苄枰闹皇歉聼o狀態(tài)的 SQL 層,反之亦然。

另外一個(gè)更深層次的原因是成本。存儲(chǔ)和 SQL 所依賴的計(jì)算資源是不一樣的,存儲(chǔ)會(huì)依賴 IO,而計(jì)算對(duì) CPU 以及內(nèi)存的要求會(huì)更高,無需配置 PCIe/NVMe/Optane 等磁盤,而且這兩者是不一定對(duì)等的,如果全部耦合在一起的話,對(duì)于資源調(diào)度是不友好的。 對(duì)于 TiDB 來說,目標(biāo)定位是支持 HTAP,即 OLTP 和 OLAP 需要在同一個(gè)系統(tǒng)內(nèi)部完成。顯然,不同的 workload 即使對(duì)于 SQL 層的物理資源需求也是不一樣的,OLAP 請(qǐng)求更多的是吞吐偏好型以及長(zhǎng) query,部分請(qǐng)求會(huì)占用大量?jī)?nèi)存,而 OLTP 面向的是短平快的請(qǐng)求,優(yōu)化的是延遲和 OPS (operation per second),在 TiDB 中 SQL 層是無狀態(tài)的,所以你可以將不同的 workload 定向到不同的物理資源上做到隔離。還是那句話,對(duì)調(diào)度器友好,同時(shí)調(diào)度期的升級(jí)也不需要把整個(gè)集群全部升級(jí)一遍。

另一方面,底層存儲(chǔ)使用 KV 對(duì)數(shù)據(jù)進(jìn)行抽象,是一個(gè)更加靈活的選擇。

一個(gè)好處是簡(jiǎn)單。對(duì)于 Scale-out 的需求,對(duì) KV 鍵值對(duì)進(jìn)行分片的難度遠(yuǎn)小于對(duì)帶有復(fù)雜的表結(jié)構(gòu)的結(jié)構(gòu)化數(shù)據(jù),另外,存儲(chǔ)層抽象出來后也可以給計(jì)算帶來新的選擇,比如可以對(duì)接其他的計(jì)算引擎,和 TiDB SQL 層同時(shí)平行使用,TiSpark 就是一個(gè)很好的例子。

從開發(fā)角度來說,這個(gè)拆分帶來的靈活度還體現(xiàn)在可以選擇不同的編程語言來開發(fā)。對(duì)于無狀態(tài)的計(jì)算層來說,我們選擇了 Go 這樣開發(fā)效率極高的語言,而對(duì)于存儲(chǔ)層項(xiàng)目 TiKV 來說,是更貼近系統(tǒng)底層,對(duì)于性能更加敏感,所以我們選擇了 Rust,如果所有組件都耦合在一起很難進(jìn)行這樣的按需多語言的開發(fā),對(duì)于開發(fā)團(tuán)隊(duì)而言,也可以實(shí)現(xiàn)專業(yè)的人干專業(yè)的事情,存儲(chǔ)引擎的開發(fā)者和 SQL 優(yōu)化器的開發(fā)者能夠并行的開發(fā)。 另外對(duì)于分布式系統(tǒng)來說,所有的通信幾乎都是 RPC,所以更明確的分層是一個(gè)很自然的而且代價(jià)不大的選擇。

四、為什么不復(fù)用 MySQL 的 SQL 層,而是選擇自己重寫

這點(diǎn)是我們和很多友商非常不一樣的地方。 目前已有的很多方案,例如 Aurora 之類的,都是直接基于 MySQL 的源碼,保留 SQL 層,下面替換存儲(chǔ)引擎的方式實(shí)現(xiàn)擴(kuò)展,這個(gè)方案有幾個(gè)好處:一是 SQL 層代碼直接復(fù)用,確實(shí)減輕了一開始的開發(fā)負(fù)擔(dān),二是面向用戶這端確實(shí)能做到 100% 兼容 MySQL 應(yīng)用。

但是缺點(diǎn)也很明顯,MySQL 已經(jīng)是一個(gè) 20 多年的老項(xiàng)目,設(shè)計(jì)之初也沒考慮分布式的場(chǎng)景,整個(gè) SQL 層并不能很好的利用數(shù)據(jù)分布的特性生成更優(yōu)的查詢計(jì)劃,雖然替換底層存儲(chǔ)的方案使得存儲(chǔ)層看上去能 Scale,但是計(jì)算層并沒有,在一些比較復(fù)雜的 Query 上就能看出來。另外,如果需要真正能夠大范圍水平擴(kuò)展的分布式事務(wù),依靠 MySQL 原生的事務(wù)機(jī)制還是不夠的。

自己重寫整個(gè) SQL 層一開始看上去很困難,但其實(shí)只要想清楚,有很多在現(xiàn)代的應(yīng)用里使用頻度很小的語法,例如存儲(chǔ)過程什么的,不去支持就好了,至少?gòu)?Parser 這層,工作量并不會(huì)很大。 同時(shí)優(yōu)化器這邊自己寫的好處就是能夠更好的與底層的存儲(chǔ)配合,另外重寫可以選擇一些更現(xiàn)代的編程語言和工具,使得開發(fā)效率也更高,從長(zhǎng)遠(yuǎn)來看,是個(gè)更加省事的選擇。

五、為什么用 RocksDB 和 Etcd Raft

很多工程師都有著一顆造輪子(玩具)的心,我們也是,但是做一個(gè)工業(yè)級(jí)的產(chǎn)品就完全不一樣了,目前的環(huán)境下,做一個(gè)新的數(shù)據(jù)庫(kù),底層的存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)能選的大概就兩種:1. B+Tree, 2. LSM-Tree。

但是對(duì)于 B+Tree 來說每個(gè)寫入,都至少要寫兩次磁盤: 1. 在日志里; 2. 刷新臟頁(yè)的時(shí)候,即使你的寫可能就只改動(dòng)了一個(gè) Byte,這個(gè) Byte 也會(huì)放大成一個(gè)頁(yè)的寫 (在 MySQL 里默認(rèn) InnoDB 的 Page size 是 16K),雖然說 LSM-Tree 也有寫放大的問題,但是好處是 LSM-tree 將所有的隨機(jī)寫變成了順序?qū)懀▽?duì)應(yīng)的 B+tree 在回刷臟頁(yè)的時(shí)候可能頁(yè)和頁(yè)之間并不是連續(xù)的)。 另一方面,LSMTree 對(duì)壓縮更友好,數(shù)據(jù)存儲(chǔ)的格式相比 B+Tree 緊湊得多,F(xiàn)acebook 發(fā)表了一些關(guān)于 MyRocks 的文章對(duì)比在他們的 MySQL 從 InnoDB 切換成 MyRocks (Facebook 基于 RocksDB 的 MySQL 存儲(chǔ)引擎)節(jié)省了很多的空間。所以 LSM-Tree 是我們的選擇。

選擇 RocksDB 的出發(fā)點(diǎn)是 RocksDB 身后有個(gè)龐大且活躍的社區(qū),同時(shí) RocksDB 在 Facebook 已經(jīng)有了大規(guī)模的應(yīng)用,而且 RocksDB 的接口足夠通用,并且相比原始的 LevelDB 暴露了很多參數(shù)可以進(jìn)行針對(duì)性的調(diào)優(yōu)。隨著對(duì)于 RocksDB 理解和使用的不斷深入,我們也已經(jīng)成為 RocksDB 社區(qū)最大的使用者和貢獻(xiàn)者之一,另外隨著 RocksDB 的用戶越來越多,這個(gè)項(xiàng)目也會(huì)變得越來越好,越來越穩(wěn)定,可以看到在學(xué)術(shù)界很多基于 LSM-Tree 的改進(jìn)都是基于 RocksDB 開發(fā)的,另外一些硬件廠商,特別是存儲(chǔ)設(shè)備廠商很多會(huì)針對(duì)特定存儲(chǔ)引擎進(jìn)行優(yōu)化,RocksDB 也是他們的首選之一。

反過來,自己開發(fā)存儲(chǔ)引擎的好處和問題同樣明顯,一是從開發(fā)到產(chǎn)品的周期會(huì)很長(zhǎng),而且要保證工業(yè)級(jí)的穩(wěn)定性和質(zhì)量不是一個(gè)簡(jiǎn)單的事情,需要投入大量的人力物力。好處是可以針對(duì)自己的 workload 進(jìn)行定制的設(shè)計(jì)和優(yōu)化,由于分布式系統(tǒng)天然的橫向擴(kuò)展性,單機(jī)有限的性能提升對(duì)比整個(gè)集群吞吐其實(shí)意義不大,把有限的精力投入到高可用和擴(kuò)展性上是一個(gè)更加經(jīng)濟(jì)的選擇。 另一方面,RocksDB 作為 LSM-Tree 其實(shí)現(xiàn)比工業(yè)級(jí)的 B+Tree 簡(jiǎn)單很多(參考對(duì)比 InnoDB),從易于掌握和維護(hù)方面來說,也是一個(gè)更好的選擇。 當(dāng)然,隨著我們對(duì)存儲(chǔ)的理解越來越深刻,發(fā)現(xiàn)很多專門針對(duì)數(shù)據(jù)庫(kù)的優(yōu)化在 RocksDB 上實(shí)現(xiàn)比較困難,這個(gè)時(shí)候就需要重新設(shè)計(jì)新的專門的引擎,就像 CPU 也能做圖像處理,但遠(yuǎn)不如 GPU,而 GPU 做機(jī)器學(xué)習(xí)又不如專用的 TPU。

選擇 Etcd Raft 的理由也類似。先說說為什么是 Raft,在 TiDB 項(xiàng)目啟動(dòng)的時(shí)候,我們其實(shí)有過在 MultiPaxos 和 Raft 之間的糾結(jié),后來結(jié)論是選擇了 Raft。Raft 的算法整體實(shí)現(xiàn)起來更加工程化,從論文就能看出來,論文中甚至連 RPC 的結(jié)構(gòu)都描述了出來,是一個(gè)對(duì)工業(yè)實(shí)現(xiàn)很友好的算法,而且當(dāng)時(shí)工業(yè)界已經(jīng)有一個(gè)經(jīng)過大量用戶考驗(yàn)的開源實(shí)現(xiàn),就是 Etcd。而且 Etcd 更加吸引我們的地方是它對(duì)測(cè)試的態(tài)度,Etcd 將狀態(tài)機(jī)的各個(gè)接口都抽象得很好,基本上可以做到與操作系統(tǒng)的 API 分離,極大降低了寫單元測(cè)試的難度,同時(shí)設(shè)計(jì)了很多 hook 點(diǎn)能夠做諸如錯(cuò)誤注入等操作,看得出來設(shè)計(jì)者對(duì)于測(cè)試的重視程度。

與其自己重新實(shí)現(xiàn)一個(gè) Raft,不如借力社區(qū),互相成長(zhǎng)。現(xiàn)在我們也是 Etcd 社區(qū)的一個(gè)活躍的貢獻(xiàn)者,一些重大的 Features 例如 Learner 等新特性,都是由我們?cè)O(shè)計(jì)和貢獻(xiàn)給 Etcd 的,同時(shí)我們還在不斷的為 Etcd 修復(fù) Bug。

沒有完全復(fù)用 Etcd 的主要的原因是我們存儲(chǔ)引擎的開發(fā)語言使用了 Rust,Etcd 是用 Go 寫的,我們需要做的一個(gè)工作是將他們的 Raft 用 Rust 語言重寫,為了完成這個(gè)事情,我們第一步是將 Etcd 的單元測(cè)試和集成測(cè)試先移植過來了(沒錯(cuò),這個(gè)也是選擇 Etcd 的一個(gè)很重要的原因,有一個(gè)測(cè)試集作為參照),以免移植過程破壞了正確性。另外一方面,就如同前面所說,和 Etcd 不一樣,TiKV 的 Raft 使用的是 Multi-Raft 的模型,同一個(gè)集群內(nèi)會(huì)存在海量的互相獨(dú)立 Raft 組,真正復(fù)雜的地方在如何安全和動(dòng)態(tài)的分裂,移動(dòng)及合并多個(gè) Raft 組,我在我的?這篇文章?里面描述了這個(gè)過程。

六、為什么有這樣的硬件配置要求

我們其實(shí)對(duì)生產(chǎn)環(huán)境硬件的要求還是蠻高的,對(duì)于存儲(chǔ)節(jié)點(diǎn)來說,SSD 或者 NVMe 或者 Optane 是剛需,另外對(duì) CPU 及內(nèi)存的使用要求也很高,同時(shí)對(duì)大規(guī)模的集群,網(wǎng)絡(luò)也會(huì)有一些要求 (詳見我們的官方文檔推薦配置的?相關(guān)章節(jié)),其中一個(gè)很重要的原因是我們底層的選擇了 RocksDB 的實(shí)現(xiàn),對(duì)于 LSM Tree 來說因?yàn)榇嬖趯懛糯蟮奶烊惶匦裕瑢?duì)磁盤吞吐需求會(huì)相應(yīng)的更高,尤其是 RocksDB 還有類似并行 Compaction 等特性。 而且大多數(shù)機(jī)械磁盤的機(jī)器配置傾向于一臺(tái)機(jī)器放更大容量的磁盤(相比 SSD),但是相應(yīng)的內(nèi)存卻一般來說不會(huì)更大,例如 24T 的機(jī)械磁盤 + 64G 內(nèi)存,磁盤存儲(chǔ)的數(shù)據(jù)量看起來更大,但是大量的隨機(jī)讀會(huì)轉(zhuǎn)化為磁盤的讀,這時(shí)候,機(jī)械磁盤很容易出現(xiàn) IO 瓶頸,另一方面,對(duì)于災(zāi)難恢復(fù)和數(shù)據(jù)遷移來說,也是不太友好的。

另外,TiDB 的各個(gè)組件目前使用 gRPC 作為 RPC 框架,gPRC 是依賴?HTTP2?作為底層協(xié)議,相比很多樸素的 RPC 實(shí)現(xiàn),會(huì)有一些額外的 CPU 開銷。TiKV 內(nèi)部使用 RocksDB 的方式會(huì)伴隨大量的 Prefix Scan,這意味著大量的二分查找和字符串比較,這也是和很多傳統(tǒng)的離線數(shù)據(jù)倉(cāng)庫(kù)很不一樣的 Pattern,這個(gè)會(huì)是一個(gè) CPU 密集型的操作。在 TiDB 的 SQL 層這端,SQL 是計(jì)算密集型的應(yīng)用這個(gè)自然不用說,另外對(duì)內(nèi)存也有一定的需求。由于 TiDB 的 SQL 是一個(gè)完整的 SQL 實(shí)現(xiàn),表達(dá)力和眾多中間件根本不是一個(gè)量級(jí),有些算子,比如 Hashjoin,就是會(huì)在內(nèi)存里開辟一塊大內(nèi)存來執(zhí)行 Join,所以如果你的查詢邏輯比較復(fù)雜,或者 Join 的一張子表比較大的情況下(偏 OLAP 實(shí)時(shí)分析業(yè)務(wù)),對(duì)內(nèi)存的需求也是比較高的,我們并沒有像單機(jī)數(shù)據(jù)庫(kù)的優(yōu)化器一樣,比如 Order by 內(nèi)存放不下,就退化到磁盤上,我們的哲學(xué)是盡可能的使用內(nèi)存。 如果硬件資源不足,及時(shí)的通過拒絕執(zhí)行和失敗通知用戶,因?yàn)橛袝r(shí)候半死不活的系統(tǒng)反而更加可怕。

另外一方面,還有很多用戶使用 TiDB 的目的是用于替換線上 OLTP 業(yè)務(wù),這類業(yè)務(wù)對(duì)于性能要求是比較高的。 一開始我們并沒有在安裝階段嚴(yán)格檢查用戶的機(jī)器配置,結(jié)果很多用戶在硬件明顯沒有匹配業(yè)務(wù)壓力的情況下上線,可能一開始沒什么問題,但是峰值壓力一上來,可能就會(huì)造成故障,盡管 TiDB 和 TiKV 對(duì)這種情況做了層層的內(nèi)部限流,但是很多情況也無濟(jì)于事。 所以我們決定將配置檢查作為部署腳本的強(qiáng)制檢查,一是減少了很多溝通成本,二是可以讓用戶在上線時(shí)盡可能的減少后顧之憂。

七、為什么用 Range-based 的分片策略,而不是 Hash-based

Hash-based 的問題是實(shí)現(xiàn)有序的 Scan API 會(huì)比較困難,我們的目標(biāo)是實(shí)現(xiàn)一個(gè)標(biāo)準(zhǔn)的關(guān)系型數(shù)據(jù)庫(kù),所以會(huì)有大量的順序掃描的操作,比如 Table Scan,Index Scan 等。用 Hash 分片策略的一個(gè)問題就是,可能同一個(gè)表的數(shù)據(jù)是不連續(xù)的,一個(gè)順序掃描即使幾行都可能會(huì)跨越不同的機(jī)器,所以這個(gè)問題上沒得選,只能是 Range 分片。 但是 Range 分片可能會(huì)造成一些問題,比如頻繁讀寫小表問題以及單點(diǎn)順序?qū)懭氲膯栴}。 在這里首先澄清一下,靜態(tài)分片在我們這樣的系統(tǒng)里面是不存在的,例如傳統(tǒng)中間件方案那樣簡(jiǎn)單的將數(shù)據(jù)分片和物理機(jī)一一對(duì)應(yīng)的分片策略會(huì)造成:

動(dòng)態(tài)添加節(jié)點(diǎn)后,需要考慮數(shù)據(jù)重新分布,這里必然需要做動(dòng)態(tài)的數(shù)據(jù)遷移;

靜態(tài)分片對(duì)于根據(jù) workload 實(shí)時(shí)調(diào)度是不友好的,例如如果數(shù)據(jù)存在訪問熱點(diǎn),系統(tǒng)需要能夠快速進(jìn)行數(shù)據(jù)遷移以便于將熱點(diǎn)分散在不同的物理服務(wù)商上。

回到剛才提到的基于 Range 分片的問題,剛才我說過,對(duì)于順序?qū)懭霟狳c(diǎn)的問題確實(shí)存在,但也不是不可解。對(duì)于大壓力的順序?qū)懭氲膱?chǎng)景大多數(shù)是日志或者類似的場(chǎng)景,這類場(chǎng)景的典型特點(diǎn)是讀寫比懸殊(讀 << 寫),幾乎沒有 Update 和隨機(jī)刪除,針對(duì)這種場(chǎng)景,寫入壓力其實(shí)可以通過 Partition Table 解決,這個(gè)已經(jīng)在 TiDB 的開發(fā)路線圖里面,今年之內(nèi)會(huì)和大家見面。

另外還有一個(gè)頻繁讀寫小表造成的熱點(diǎn)問題。這個(gè)原因是,在底層,TiDB 的數(shù)據(jù)調(diào)度的最小單位是 Region,也就是一段段按字節(jié)序排序的鍵值 Key-Value Pairs (默認(rèn)大小 96M),假設(shè)如果一個(gè)小表,總大小連 96M 都不到,訪問還特別頻繁,按照目前的機(jī)制,如果不強(qiáng)制的手動(dòng) Split,調(diào)度系統(tǒng)無論將這塊數(shù)據(jù)調(diào)度到什么位置,新的位置都會(huì)出現(xiàn)熱點(diǎn),所以這個(gè)問題本質(zhì)上是無解的。因此建議如果有類似的訪問 pattern,盡可能的將通用的小表放到 Redis 之類的內(nèi)存緩存中,或者甚至直接放在業(yè)務(wù)服務(wù)的內(nèi)存里面(反正小)。

八、為什么性能(延遲)不是唯一的評(píng)價(jià)標(biāo)準(zhǔn)

很多朋友問過我,TiDB 能替換 Redis 嗎?大家對(duì) Redis 和 TiDB 的喜愛之情我也很能理解,但是很遺憾,TiDB 并不是一個(gè)緩存服務(wù),它支持跨行強(qiáng)一致事務(wù),在非易失設(shè)備上實(shí)現(xiàn)持久化存儲(chǔ),而這些都是有代價(jià)的。

簡(jiǎn)單來說,寫磁盤的 IO 開銷 (WAL,持久化),多副本高可用和保證分布式事務(wù)必然會(huì)犧牲延遲,更不用說做跨數(shù)據(jù)中心的同步了,在這點(diǎn)上,我認(rèn)為如果需要很低延遲的響應(yīng)速度(亞毫秒級(jí))就需要在業(yè)務(wù)端做緩存了。TiDB 的定位是給業(yè)務(wù)提供一個(gè)可擴(kuò)展的 The Source of Truth (真相之源),即使業(yè)務(wù)層的緩存失效,也有一個(gè)地方能夠提供強(qiáng)一致的數(shù)據(jù),而且業(yè)務(wù)不用關(guān)心容量問題。**另一方面,衡量一個(gè)分布式系統(tǒng)更有意義的指標(biāo)是吞吐,這個(gè)觀點(diǎn)我在很多文章里已經(jīng)提到過,提高并發(fā)度,如果系統(tǒng)的吞吐能夠隨著集群機(jī)器數(shù)量線性提升,而且延遲是穩(wěn)定的才有意義,而且這樣才能有無限的提升空間。**在實(shí)際的環(huán)境中,單個(gè) TiDB 集群已經(jīng)有一些用戶使用到了百萬級(jí)別的 QPS,這個(gè)在單機(jī)架構(gòu)上是幾乎不可能實(shí)現(xiàn)的。另外,這幾年硬件的進(jìn)步速度非常快,特別是 IO 相關(guān)的創(chuàng)新,比如 NVMe SSD 的普及,還有剛剛商用的持久化內(nèi)存等新的存儲(chǔ)介質(zhì)。很多時(shí)候我們?cè)谲浖用嫔辖g盡腦汁甚至犧牲代碼的優(yōu)雅換來一點(diǎn)點(diǎn)性能提升,很可能換塊磁盤就能帶來成倍的提升。

我們公司內(nèi)部有一句話:Make it right before making it fast。正確性和可靠性的位置是在性能之前的,畢竟在一個(gè)不穩(wěn)定的系統(tǒng)上談性能是沒有意義的。

九、為什么彈性伸縮能力如此重要

在業(yè)務(wù)初期,數(shù)據(jù)量不大,業(yè)務(wù)流量和壓力不大的時(shí)候,基本隨便什么數(shù)據(jù)庫(kù)都能夠搞定,但很多時(shí)候業(yè)務(wù)的爆發(fā)性增長(zhǎng)可能是沒有辦法預(yù)期的,特別是一些 ToC 端的應(yīng)用。早期的 Twitter 用戶一定對(duì)時(shí)不時(shí)的大鯨魚(服務(wù)不可用)深惡痛絕,近一點(diǎn)還有前兩年有一段時(shí)間爆紅的足記 App,很短的時(shí)間之內(nèi)業(yè)務(wù)和數(shù)據(jù)量爆發(fā)性增長(zhǎng),數(shù)據(jù)庫(kù)幾乎是所有這些案例中的核心瓶頸。 很多互聯(lián)網(wǎng)的 DBA 和年輕的架構(gòu)師會(huì)低估重構(gòu)業(yè)務(wù)代碼帶來的隱形成本,在業(yè)務(wù)早期快速搞定功能和需求是最重要的。想象一下,業(yè)務(wù)快速增長(zhǎng),服務(wù)器每天都因?yàn)閿?shù)據(jù)庫(kù)過載停止服務(wù)的時(shí)候,DBA 告訴你沒辦法,先讓你重新去把你的業(yè)務(wù)全改寫成分庫(kù)分表的形式,在代碼里到處加 Sharding key,犧牲一切非 Sharding key 的多維關(guān)聯(lián)查詢和相關(guān)的跨 Shard 的強(qiáng)一致事務(wù),然后數(shù)據(jù)復(fù)制好多份……這種時(shí)候是真正的時(shí)間等于金錢,決定這個(gè)公司生死存亡的時(shí)候不是去寫業(yè)務(wù)和功能代碼,而是因?yàn)榛A(chǔ)設(shè)施的限制被迫重構(gòu),其實(shí)是非常不好的。 如果這個(gè)時(shí)候,有一個(gè)方案,能夠讓你幾乎無成本的,不修改業(yè)務(wù)代碼的時(shí)候?qū)?shù)據(jù)庫(kù)吞吐進(jìn)行線性擴(kuò)展(無腦加機(jī)器其實(shí)是最便宜的),最關(guān)鍵的是為了業(yè)務(wù)的進(jìn)化爭(zhēng)取了時(shí)間,我相信這個(gè)選擇其實(shí)一點(diǎn)都不難做。

其實(shí)做 TiDB 的初心正是如此,我們過去見到了太多類似的血和淚,實(shí)在不能忍了,分庫(kù)分表用各種中間件什么的炫技是很簡(jiǎn)單,但是我們想的是真正解決很多開發(fā)者和 DBA 眼前的燃眉之急。

最近這段時(shí)間,有兩個(gè)用戶的例子讓我印象很深,也很自豪,一個(gè)是 Mobike,一個(gè)是轉(zhuǎn)轉(zhuǎn),前者是 TiDB 的早期用戶,他們自己也在數(shù)據(jù)增長(zhǎng)很快的時(shí)候就開始使用 TiDB,在快速的發(fā)展過程中沒有因?yàn)閿?shù)據(jù)庫(kù)的問題掉鏈子;后者是典型的快速發(fā)展的互聯(lián)網(wǎng)公司,一個(gè) All-in TiDB 的公司,這個(gè)早期的技術(shù)選型極大的解放了業(yè)務(wù)開發(fā)的生產(chǎn)力,讓業(yè)務(wù)能夠更放開手腳去寫業(yè)務(wù)代碼,而不是陷入無休止的選擇 Sharding key,做讀寫分離等等和數(shù)據(jù)庫(kù)較勁的事情。

為業(yè)務(wù)開發(fā)提供更加靈活便捷和低成本的智能基礎(chǔ)存儲(chǔ)服務(wù),是我們做 TiDB 的出發(fā)點(diǎn)和落腳點(diǎn),分布式/高可用/方便靈活的編程接口/智能省心,這些大的方向上也符合未來主流的技術(shù)發(fā)展趨勢(shì)。對(duì)于CEO 、 CTO 和架構(gòu)師這類的管理者而言,在解決眼前問題的同時(shí),跟隨大的技術(shù)方向,不給未來多變的業(yè)務(wù)埋坑,公司盡可能快速發(fā)展,這個(gè)才是核心要去思考的問題。

十、如何根據(jù)自己的實(shí)際情況參考業(yè)內(nèi)的使用案例

TiDB 是一個(gè)通用的數(shù)據(jù)庫(kù),甚至希望比一般的數(shù)據(jù)庫(kù)更加通用,TiDB 是很早就嘗試融合 OLTP 和 OLAP 的邊界的數(shù)據(jù)庫(kù)產(chǎn)品,我們是最早將 HTAP 這個(gè)概念從實(shí)驗(yàn)室和論文里帶到現(xiàn)實(shí)的產(chǎn)品之一。這類通用基礎(chǔ)軟件面臨的一個(gè)問題就是我們?cè)谠缙谄鋵?shí)很難去指導(dǎo)垂直行業(yè)的用戶把 TiDB 用好,畢竟各自領(lǐng)域都有各自的使用場(chǎng)景和特點(diǎn),TiDB 的開發(fā)團(tuán)隊(duì)的背景大部分是互聯(lián)網(wǎng)行業(yè),所以天然的會(huì)對(duì)互聯(lián)網(wǎng)領(lǐng)域的架構(gòu)和場(chǎng)景更熟悉,但是比如在金融,游戲,電商,傳統(tǒng)制造業(yè)這些行業(yè)里其實(shí)我們不是專家,不過現(xiàn)在都已經(jīng)有很多的行業(yè)專家和開發(fā)者已經(jīng)能將 TiDB 在各自領(lǐng)域用得很好。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容