怎樣打造一個分佈式數據庫

在技術方面,我本身熱衷於 Open Source,寫了不少 Open Source 的東西,擅長的是 Infrastructure 領域。Infrastructure 領域如今範圍很廣,好比說很典型的分佈式 Scheduler、Mesos、Kubernetes,另外它和 Microservices 所結合的東西也特別多。Infrastructure 領域還有好比 Database 有分 AP(分析型)和 TP(事務型),好比說很典型的你們知道的 Spark、Greenplum、Apache Phoenix 等等,這些都屬於在 AP 的,它們也會去嘗試支持有限的 TP。另外,還有一個比較有意思的就是 Kudu——Cloudera Open Source 的那個項目,它的目標頗有意思:我不作最強的 AP 系統,也不作最強的 TP 系統,我選擇一個相對摺中的方案。從文化哲學上看,它比較符合中國的中庸思想。程序員

另外,我前後建立了 Codis、TiDB。去年12月份建立了 TiKV 這個 project,TiKV 在全部的 rust 項目裏目前排名前三。算法

首先咱們聊聊 Database 的歷史,在已經有這麼多種數據庫的背景下咱們爲何要建立另一個數據庫;以及說一下如今方案遇到的困境,說一下 Google Spanner 和 F一、TiKV 和 TiDB,說一下架構的事情,在這裏咱們會重點聊一下 TiKV。由於咱們產品的不少特性是 TiKV 提供的,好比說跨數據中心的複製、Transaction、auto-scale。sql

 

接下來聊一下爲何 TiKV 用 Raft 能實現全部這些重要的特性,以及 scale、MVCC 和事務模型。東西很是多,我今天不太可能把裏面的技術細節都描述得特別細,由於幾乎每個話題均可以找到一篇或者是多篇論文,因此詳細的技術問題你們能夠單獨來找我聊。微信

後面再說一下咱們如今遇到的窘境,就是你們常規遇到的分佈式方案有哪些問題,好比 MySQL Sharding。咱們建立了無數 MySQL Proxy,好比官方的 MySQL proxy、Youtube 的 Vitess、淘寶的 Cobar、TDDL以及基於 Cobar 的 MyCAT、金山的 Kingshard、360 的 Atlas、京東的 JProxy,我在豌豆莢也寫了一個。能夠說,隨便一個大公司都會造一個 MySQL Sharding 的方案。網絡

爲何咱們要建立另一個數據庫?

昨天晚上我還跟一個同窗聊到,基於 MySQL 的方案它的天花板在哪裏,它的天花板特別明顯。有一個思路是能不能經過 MySQL 的 server 把 InnoDB 變成一個分佈式數據庫,聽起來這個方案很完美,可是很快就會遇到天花板。由於 MySQL 生成的執行計劃是個單機的,它認爲整個計劃的 cost 也是單機的,我讀取一行和讀取下一行之間的開銷是很小的,好比迭代 next row 能夠馬上拿到下一行。實際上在一個分佈式系統裏面,這是不必定的。架構

 

另外,你把數據都拿回來計算這個太慢了,不少時候咱們須要把咱們的 expression 或者計算過程等等運算推下去,向上返回一個最終的計算結果,這個必定要用分佈式的 plan,前面控制執行計劃的節點,它必需要理解下面是分佈式的東西,才能生成最好的 plan,這樣才能實現最高的執行效率。app

好比說你作一個 sum,你是一條條拿回來加,仍是讓一堆機器一塊兒算,最後給我一個結果。 例如我有 100 億條數據分佈在 10 臺機器上,並行在這 10臺機器我可能只拿到 10 個結果,若是把全部的數據每一條都拿回來,這就太慢了,徹底喪失了分佈式的價值。聊到 MySQL 想實現分佈式,另一個實現分佈式的方案就是 Proxy。可是 Proxy 自己的天花板在那裏,就是它不支持分佈式的 transaction,它不支持跨節點的 join,它沒法理解複雜的 plan,一個複雜的 plan 打到 Proxy 上面,Proxy 就傻了,我到底應該往哪個節點上轉發呢,若是我涉及到 subquery sql 怎麼辦?因此這個天花板是瞬間會到,在傳統模型下面的修改,很快會達不到咱們的要求。

另一個很重要的是,MySQL 支持的複製方式是半同步或者是異步,可是半同步能夠降級成異步,也就是說任什麼時候候數據出了問題你不敢切換,由於有多是異步複製,有一部分數據尚未同步過來,這時候切換數據就不一致了。前一陣子出現過某公司忽然不能支付了這種事件,今年有不少這種相似的 case,因此微博上你們都在說「說好的異地多活呢?」……

爲何傳統的方案在這上面解決起來特別的困難,天花板立刻到了,基本上不可能解決這個問題。另外是多數據中心的複製和數據中心的容災,MySQL 在這上面是作很差的。

在前面三十年基本上是關係數據庫的時代,那個時代建立了不少偉大的公司,好比說 IBM、Oracle、微軟也有本身的數據庫,早期還有一個公司叫 Sybase,有一部分特別老的程序員同窗在當年的教程裏面還能夠找到這些東西,可是如今基本上看不到了。

另外是 NoSQL。NoSQL 也是一度很是火,像 Cassandra、MongoDB 等等,這些都屬於在互聯網快速發展的時候建立這些可以 scale 的方案,但 Redis scale 出來比較晚,因此不少時候你們把 Redis 當成一個 Cache,如今慢慢你們把它當成存儲不那麼重要的數據的數據庫。由於它有了 scale 支持之後,你們會把更多的數據放在裏面。

而後到了 2015,嚴格來說是到 2014 年到 2015 年之間,Raft 論文發表之後,真正的 NewSQL 的理論基礎終於完成了。我以爲 NewSQL 這個理論基礎,最重要的劃時代的幾篇論文,一個是谷歌的 Spanner,是在 2013 年初發布的;再就是 Raft 是在 2014 年上半年發佈的。這幾篇至關於打下了分佈式數據庫 NewSQL 的理論基礎,這個模型是很是重要的,若是沒有模型在上面是堆不起來東西的。說到如今,你們可能對於模型仍是能夠理解的,可是對於它的實現難度很難想象。

前面我大概提到了咱們爲何須要另一個數據庫,說到 Scalability 數據的伸縮,而後咱們講到須要 SQL,好比你給我一個純粹的 key-velue 系統的 API,好比我要查找年齡在 10 歲到 20 歲之間的 email 要知足一個什麼要求的。若是隻有 KV 的 API 這是會寫死人的,要寫不少代碼,可是實際上用 SQL 寫一句話就能夠了,並且 SQL 的優化器對整個數據的分佈是知道的,它能夠很快理解你這個 SQL,而後會獲得一個最優的 plan,他獲得這個最優的 plan 基本上等價於一個真正理解 KV 每一步操做的人寫出來的程序。一般狀況下,SQL 的優化器是爲了更加了解或者作出更好的選擇。

另一個就是 ACID 的事務,這是傳統數據庫必需要提供的基礎。之前你不提供 ACID 就不能叫數據庫,可是近些年你們寫一個內存的 map 也能夠叫本身是數據庫。你們寫一個 append-only 文件,咱們也能夠叫只讀數據庫,數據庫的概念比之前極大的泛化了。

另外就是高可用和自動恢復,他們的概念是什麼呢?有些人會有一些誤解,由於今天還有朋友在現場問到,出了故障,好比說一個機房掛掉之後我應該怎麼作切換,怎麼操做。這個實際上至關於仍是上一代的概念,還須要人去幹預,這種不算是高可用。

將來的高可用必定是系統出了問題立刻能夠自動恢復,立刻能夠變成可用。好比說一個機房掛掉了,十秒鐘不能支付,十秒鐘以後系統自動恢復了變得能夠支付,即便這個數據中心不再起來我整個系統仍然是能夠支付的。Auto-Failover 的重要性就在這裏。你們不但願在睡覺的時候被一個報警給拉起來,我相信你們之後具有這樣一個能力,5 分鐘之內的報警不用理會,掛掉一個機房,又掛掉一個機房,這種連續報警纔會理。咱們內部開玩笑說,但願你們都能睡個好覺,很重要的事情就是這個。

說完應用層的事情,如今頗有不少業務,在應用層本身去分片,好比說我按照 user ID在代碼裏面分片,還有一部分是更高級一點我會用到一致性哈希。問題在於它的複雜度,到必定程度以後我自動的分庫,自動的分表,我以爲下一代數據庫是不須要理解這些東西的,不須要了解什麼叫作分庫,不須要了解什麼叫作分表,由於系統是所有自動搞定的。同時複雜度,若是一個應用不支持事務,那麼在應用層去作,一般的作法是引入一個外部隊列,引入大量的程序機制和狀態轉換,A 狀態的時候容許轉換到 B 狀態,B 狀態容許轉換到 C 狀態。

舉一個簡單的例子,好比說在京東上買東西,先下訂單,支付狀態以後這個商品才能出庫,若是不是支付狀態必定不能出庫,每一步都有嚴格的流程。

Google Spanner / F1

說一下 Google 的 Spanner 和 F1,這是我很是喜歡的論文,也是我最近幾年看過不少遍的論文。 Google Spanner 已經強大到什麼程度呢?Google Spanner 是全球分佈的數據庫,在國內目前廣泛作法叫作同城兩地三中心,它們的差異是什麼呢?以 Google 的數據來說,谷歌比較高的級別是他們有 7 個副本,一般是美國保存 3 個副本,再在另外 2 個國家能夠保存 2 個副本,這樣的好處是萬一美國兩個數據中心出了問題,那整個系統還能繼續可用,這個概念就是好比美國 3 個副本全掛了,整個數據都還在,這個數據安全級別比不少國家的安全級別還要高,這是 Google 目前作到的,這是全球分佈的好處。

如今國內主流的作法是兩地三中心,但如今基本上都不能自動切換。你們能夠看到不少號稱實現了兩地三中心或者異地多活,可是一出現問題都說很差意思這段時間我不能提供服務了。你們無數次的見到這種 case, 我就不列舉了。

Spanner 如今也提供一部分 SQL 特性。在之前,大部分 SQL 特性是在 F1 裏面提供的,如今 Spanner 也在逐步豐富它的功能,Google 是全球第一個作到這個規模或者是作到這個級別的數據庫。事務支持裏面 Google 有點黑科技(其實也沒有那麼黑),就是它有GPS 時鐘和原子鐘。你們知道在分佈式系統裏面,好比說數千臺機器,兩個事務啓動前後順序,這個順序怎麼界定(事務外部一致性)。這個時候 Google 內部使用了 GPS 時鐘和原子鐘,正常狀況下它會使用一個GPS 時鐘的一個集羣,就是說我拿的一個時間戳,並非從一個 GPS 上來拿的時間戳,由於你們知道全部的硬件都會有偏差。若是這時候我從一個上拿到的 GPS 自己有點問題,那麼你拿到的這個時鐘是不精確的。而 Google 它其實是在一批 GPS 時鐘上去拿了可以知足 majority 的精度,再用時間的算法,獲得一個比較精確的時間。你們知道 GPS 也不太安全,由於它是美國軍方的,對於 Google 來說要實現比國家安全級別更高的數據庫,而 GPS 是可能受到干擾的,由於 GPS 信號是能夠調整的,這在軍事用途上面很典型的,你們知道導彈的制導須要依賴 GPS,若是調整了 GPS 精度,那麼導彈精度就廢了。因此他們還用原子鐘去校訂 GPS,若是 GPS 忽然跳躍了,原子鐘上是能夠檢測到 GPS 跳躍的,這部分相對有一點黑科技,可是從原理上來說仍是比較簡單,比較好理解的。

最開始它 Spanner 最大的用戶就是 Google 的 Adwords,這是 Google 最賺錢的業務,Google 就是靠廣告生存的,咱們一直以爲 Google 是科技公司,可是他的錢是從廣告那來的,因此必定程度來說 Google 是一個廣告公司。Google 內部的方向先有了 Big table ,而後有了 MegaStore ,MegaStore 的下一代是 Spanner ,F1 是在 Spanner 上面構建的。

TiDB and TiKV

TiKV 和 TiDB 基本上對應 Google Spanner 和 Google F1,用 Open Source 方式重建。目前這兩個項目都開放在 GitHub 上面,兩個項目都比較火爆,TiDB 是更早一點開源的, 目前 TiDB 在 GitHub 上 有 4300 多個 Star,天天都在增加。

另外,對於如今的社會來說,咱們以爲 Infrastructure 領域閉源的東西是沒有任何生存機會的。沒有任何一家公司,願意把本身的身家性命壓在一個閉源的項目上。舉一個很典型的例子,在美國有一個數據庫叫 FoundationDB,去年被蘋果收購了。FoundationDB 以前和用戶籤的合約都是一年的合約。好比說,我給你服務週期是一年,如今我被另一個公司收購了,我今年服務到期以後,我是知足合約的。可是其餘公司不再能找它服務了,由於它如今不叫 FoundationDB 了,它叫 Apple了,你不能找 Apple 給你提供一個 Enterprise service。

TiDB 和 TiKV 爲何是兩個項目,由於它和 Google 的內部架構對比差很少是這樣的:TiKV 對應的是 Spanner,TiDB 對應的是 F1 。F1 裏面更強調上層的分佈式的 SQL 層到底怎麼作,分佈式的 Plan 應該怎麼作,分佈式的 Plan 應該怎麼去作優化。同時 TiDB 有一點作的比較好的是,它兼容了 MySQL 協議,當你出現了一個新型的數據庫的時候,用戶使用它是有成本的。你們都知道做爲開發很討厭的一個事情就是,我要每一個語言都寫一個 Driver,好比說你要支持 C++、你要支持 Java、你要支持 Go 等等,這個太累了,並且用戶還得改他的程序,因此咱們選擇了一個更加好的東西兼容 MySQL 協議,讓用戶能夠不用改。一會我會用一個視頻來演示一下,爲何一行代碼不改就能夠用,用戶就能體會到 TiDB 帶來的全部的好處。

這個圖其實是整個協議棧或者是整個軟件棧的實現。你們能夠看到整個系統是高度分層的,從最底下開始是 RocksDB ,而後再上面用 Raft 構建一層能夠被複制的 RocksDB ,在這一層的時候它尚未 Transaction,可是整個系統如今的狀態是全部寫入的數據必定要保證它複製到了足夠多的副本。也就是說只要我寫進來的數據必定有足夠多的副本去 cover 它,這樣才比較安全,在一個比較安全的 Key-value store 上面, 再去構建它的多版本,再去構建它的分佈式事務,而後在分佈式事務構建完成以後,就能夠輕鬆的加上 SQL 層,再輕鬆的加上MySQL 協議的支持。而後,這兩天我比較好奇,本身寫了 MongoDB 協議的支持,而後咱們能夠用 MongoDB 的客戶端來玩,就是說協議這一層是高度可插拔的。TiDB 上能夠在上面構建一個 MongoDB 的協議,至關於這個是構建一個 SQL 的協議,能夠構建一個 NoSQL 的協議。這一點主要是用來驗證 TiKV 在模型上面的支持能力。

這是整個 TiKV 的架構圖,從這個看來,整個集羣裏面有不少 Node,好比這裏畫了四個 Node ,分別對應了四個機器。每個 Node 上能夠有多個 Store,每一個 Store 裏面又會有不少小的 Region,就是說一小片數據,就是一個 Region 。從全局來看全部的數據被劃分紅不少小片,每一個小片默認配置是 64M,它已經足夠小,能夠很輕鬆的從一個節點移到另一個節點,Region 1 有三個副本,它分別在 Node一、Node 2 和 Node4 上面, 相似的Region 2,Region 3 也是有三個副本。每一個 Region 的全部副本組成一個 Raft Group,整個系統能夠看到不少這樣的 Raft groups。

Raft 細節我不展開了,你們有興趣能夠找我私聊或者看一下相應的資料。

由於整個系統裏面咱們能夠看到上一張圖裏面有不少 Raft group 給咱們,不一樣 Raft group 之間的通信都是有開銷的。因此咱們有一個相似於 MySQL 的 group commit 機制 ,你發消息的時候實際上能夠 share 同一個 connection , 而後 pipeline + batch 發送,很大程度上能夠省掉大量 syscall 的開銷。

另外,其實在必定程度上後面咱們在支持壓縮的時候,也有很是大的幫助,就是能夠減小數據的傳輸。對於整個系統而言,可能有數百萬的 Region,它的大小能夠調整,好比說 64M、128M、256M,這個實際上依賴於整個系統裏面當前的情況。

好比說咱們曾經在有一個用戶的機房裏面作過測試,這個測試有一個香港機房和新加坡的機房。結果咱們在作複製的時候,新加坡的機房大於 256M 就複製不過去,由於機房很不穩定,必需要保證數據切的足夠小,這樣才能複製過去。

若是一個 Region 太大之後咱們會自動作 SPLIT,這是很是好玩的過程,有點像細胞的分裂。

而後 TiKV 的 Raft 實現,是從 etcd 裏面 port 過來的,爲何要從 etcd 裏面 port 過來呢?首先 TiKV 的 Raft 實現是用 Rust 寫的。做爲第一個作到生產級別的 Raft 實現,因此咱們從 etcd 裏面把它用 Go 語言寫的 port 到這邊。

這個是 Raft 官網上面列出來的 TiKV在裏面的狀態,你們能夠看到 TiKV 把全部 Raft 的 feature 都實現了。 好比說 Leader Election、Membership Changes,這個是很是重要的,整個系統的 scale 過程高度依賴 Membership Changes,後面我用一個圖來說這個過程。後面這個是 Log Compaction,這個用戶不太關心。

這是很典型的細胞分裂的圖,實際上 Region 的分裂過程和這個是相似的。

咱們看一下擴容是怎麼作的。

好比說以如今的系統假設,咱們剛開始說只有三個節點,有 Region1 分別是在 1 、二、4,我用虛線鏈接起來表明它是一個 Raft group ,你們能夠看到整個系統裏面有三個 Raft group ,在每個 Node 上面數據的分佈是比較均勻的,在這個假設每個 Region 是 64M ,至關於只有一個 Node 上面負載比其餘的稍微大一點點。

一個在線視頻默認咱們都是推薦 3 個副本或者 5 個副本的配置。Raft 自己有一個特色,若是一個 leader down 掉以後,其它的節點會選一個新的 leader ,那麼這個新的 leader 會把它尚未 commit 但已經 reply 過去的 log 作一個 commit ,而後會再作 apply ,這個有點偏 Raft 協議,細節我不講了。

複製數據的小的 Region,它其實是跨多個數據中心作的複製。這裏面最重要的一點是永遠不丟失數據,不管如何我保證個人複製必定是複製到 majority ,任什麼時候候我只要對外提供服務,容許外面寫入數據必定要複製到 majority 。很重要的一點就是恢復的過程必定要是自動化的,我前面已經強調過,若是不能自動化恢復,那麼中間的宕機時間或者對外不可服務的時間,便不是由整個系統決定的,這是相對回到了幾十年前的狀態。

MVCC

MVCC 我稍微仔細講一下這一塊。MVCC 的好處,它很好支持 Lock-free 的 snapshot read ,一下子我有一個圖會展現 MVCC 是怎麼作的。isolation level 就不講了, MySQL 裏面的級別是能夠調的,咱們的 TiKV 有 SI,還有 SI+lock,默認是支持 SI 的這種隔離級別,而後你寫一個 select for update 語句,這個會自動的調整到 SI 加上 lock 這個隔離級別。這個隔離級別基本上和 SSI 是一致的。還有一個就是 GC 的問題,若是你的系統裏面的數據產生了不少版本,你須要把這個比較老的數據給 GC 掉,好比說正常狀況下咱們是不刪除數據的, 你寫入一行,而後再寫入一行,不斷去 update 同一行的時候,每一次 update 會產生新的版本,新的版本就會在系統裏存在,因此咱們須要一個 GC 的模塊把比較老的數據給 GC 掉,實際上這個 GC 不是 Go 裏面的GC,不是 Java 的 GC,而是數據的 GC。

這是一個數據版本,你們能夠看到咱們的數據分紅兩塊,一個是 meta,一個是 data。meta 相對於描述個人數據當前有多少個版本。你們能夠看到綠色的部分,好比說咱們的 meta key 是 A ,keyA 有三個版本,是 A1 、A二、A3,咱們把 key 本身和 version 拼到一塊兒。那咱們用 A一、A二、A3 分別描述 A 的三個版本,那麼就是 version 1/2/3。meta 裏面描述,就是個人整個 key 相對應哪一個版本,我想找到那個版本。好比說我如今要讀取 key A 的版本10,但顯然如今版本 10 是沒有的,那麼小於版本 10 最大的版本是 3,因此這時我就能讀取到 3,這是它的隔離級別決定的。關於 data,我剛纔已經講過了。

分佈式事務模型

接下來是分佈式事務模型,實際上是基於 Google Percolator,這是 Google 在 2006 發表的一篇論文,是 Google 在作內部增量處理的時候發現了這個方法,本質上仍是二階段提交的。這使用的是一個樂觀鎖,好比說我提供一個 transaction ,我去改一個東西,改的時候是發佈在本地的,並無立刻 commit 到數據存儲那一端,這個模型就是說,我修改的東西我立刻去 Lock 住,這個基本就是一個悲觀鎖。但若是到最後一刻我才提交出去,那麼鎖住的這一小段的時間,這個時候實現的是樂觀鎖。樂觀鎖的好處就是當你衝突很小的時候能夠獲得很是好的性能,由於衝突特別小,因此我本地修改一般都是有效的,因此我不須要去 Lock ,不須要去 roll back 。本質上分佈式事務就是 2PC (兩階段提交) 或者是 2+x PC,基本上沒有 1PC,除非你在別人的級別上作弱化。好比說我容許你讀到當前最新的版本,也容許你讀到前面的版本,書裏面把這個叫作幻讀。若是你調到這個程度是比較容易作 1PC 的,這個實際上仍是依賴用戶設定的隔離級別的,若是用戶須要更高的隔離級別,這個 1PC就不太好作了。

這是一個路由,正常來說,你們可能會好奇一個 SQL 語句怎麼最後會落到存儲層,而後能很好的運行,最後怎麼能映射到 KV 上面,又怎麼能路由到正確的節點,由於整個系統可能有上千個節點,你怎麼能正確路由到那一個的節點。咱們在 TiDB 有一個 TiKV driver , 另外 TiKV 對外使用的是 Google Protocol Buffer 來做爲通信的編碼格式。

Placement Driver

來講一下 Placement Driver 。Placement Driver 是什麼呢?整個系統裏面有一個節點,它會時刻知道如今整個系統的狀態。好比說每一個機器的負載,每一個機器的容量,是否有新加的機器,新加機器的容量究竟是怎麼樣的,是否是能夠把一部分數據挪過去,是否是也是同樣下線, 若是一個節點在十分鐘以內沒法被其餘節點探測到,我認爲它已經掛了,無論它其實是不是真的掛了,可是我也認爲它掛了。由於這個時候是有風險的,若是這個機器萬一真的掛了,意味着你如今機器的副本數只有兩個,有一部分數據的副本數只有兩個。那麼如今你必須立刻要在系統裏面從新選一臺機器出來,它上面有足夠的空間,讓我如今只有兩個副本的數據從新再作一份新的複製,系統始終維持在三個副本。整個系統裏面若是機器掛掉了,副本數少了,這個時候應該會被自動發現,立刻補充新的副本,這樣會維持整個系統的副本數。這是很重要的 ,爲了不數據丟失,必須維持足夠的副本數,由於副本數每少一個,你的風險就會再增長。這就是 Placement Driver 作的事情。

同時,Placement Driver 還會根據性能負載,不斷去 move 這個 data 。好比說你這邊負載已經很高了,一個磁盤假設有 100G,如今已經用了 80G,另一個機器上也是 100G,可是他只用了 20G,因此這上面還能夠有幾十 G 的數據,好比 40G 的數據,你能夠 move 過去,這樣能夠保證系統有很好的負載,不會出現一個磁盤巨忙無比,數據已經多的裝不下了,另一個上面尚未東西,這是 Placement Driver 要作的東西。

Raft 協議還提供一個很高級的特性叫 leader transfer。leader transfer 就是說在我不移動數據的時候,我把個人 leadership 給你,至關於從這個角度來說,我把流量分給你,由於我是 leader,因此數據會到我這來,但我如今把 leader給你,我讓你來當 leader,原來打給個人請求會被打給你,這樣個人負載就降下來。這就能夠很好的動態調整整個系統的負載,同時又不搬移數據。不搬移數據的好處就是,不會造成一個抖動。

MySQL Sharding

MySQL Sharding 我前面已經提到了它的各類天花板,MySQL Sharding 的方案很典型的就是解決基本問題之後,業務稍微複雜一點,你在 sharding 這一層根本搞不定。它永遠須要一個 sharding key,你必需要告訴個人 proxy,個人數據要到哪裏找,對用戶來講是極不友好的,好比我如今是一個單機的,如今我要切入到一個分佈式的環境,這時我必需要改個人代碼,我必需要知道我這個 key ,個人 row 應該往哪裏 Sharding。若是是用 ORM ,這個基本上就無法作這個事情了。有不少 ORM 它自己假設我後面只有一個 MySQL。但 TiDB 就能夠很好的支持,由於我全部的角色都是對的,我不須要關注 Sharding 、分庫、分表這類的事情。

這裏面有一個很重要的問題沒有提,我怎麼作 DDL。若是這個表很是大的話,好比說咱們有一百億吧,橫跨了四臺機器,這個時候你要給它作一個新的 Index,就是我要添加一個新的索引,這個時候你必需要不影響任何現有的業務,實際上這是多階段提交的算法,這個是 Google 和 F1 一塊兒發出來那篇論文。

簡單來說是這樣的,先把狀態標記成 delete only ,delete only 是什麼意思呢?由於在分佈式系統裏面,全部的系統對於 schema 的視野不是一致的,好比說我如今改了一個值,有一部分人發現這個值被改了,可是還有一部分人尚未開始訪問這個,因此根本不知道它被改了。而後在一個分佈系統裏,你也不可能實時通知到全部人在同一時刻發現它改變了。好比說從有索引到沒有索引,你不能一步切過去,由於有的人認爲它有索引,因此他給它建了一個索引,可是另一個機器他認爲它沒有索引,因此他就把數據給刪了,索引就留在裏面了。這樣遇到一個問題,我經過索引找的時候告訴我有, 實際數據卻沒有了,這個時候一致性出了問題。好比說我 count 一個 email 等於多少的,我經過 email 建了一個索引,我認爲它是在,可是 UID 再轉過去的時候可能已經不存在了。

好比說我先標記成 delete only,我刪除它的時候無論它如今有沒有索引,我都會嘗試刪除索引,因此個人數據是乾淨的。若是我刪除掉的話,我無論結果是什麼樣的,我嘗試去刪一下,可能這個索引還沒 build 出來,可是我仍然刪除,若是數據沒有了,索引必定沒有了,因此這能夠很好的保持它的一致性。後面再相似於前面,先標記成 write only 這種方式,連續再迭代這個狀態,就能夠迭代到一個最終能夠對外公開的狀態。好比說當我迭代到必定程度的時候,我能夠從後臺 build index ,好比說我一百億,正在操做的 index 會立刻 build,可是還有不少沒有 build index ,這個時候後臺不斷的跑 map-reduce 去 build index ,直到整個都 build 完成以後,再對外 public ,就是說我這個索引已經可用了,你能夠直接拿索引來找,這個是很是經典的。在這個 Online,Asynchronous Schema Change in F1 paper以前,你們都不知道這事該怎麼作。

Proxy Sharding 的方案不支持分佈式事務,更不用說跨數據中心的一致性事務了。 TiKV 很好的支持 transaction,剛纔提到的 Raft 除了增長副本以外,還有 leader transfer,這是一個傳統的方案都沒法提供的特性。以及它帶來的好處,當我瞬間平衡整個系統負載的時候,對外是透明的, 作 leader transfer 的時候並不須要移動數據,只是個簡單的 leader transfer 消息。

而後說一下若是你們想參與咱們項目的話是怎樣的過程,由於整個系統是徹底開源的,若是你們想參與其中任何一部分均可以,好比說我想參與到分佈式 KV,能夠直接貢獻到 TiKV。TiKV 須要寫 Rust,若是你們對這塊特別有激情能夠體驗寫 Rust 的感受 。

TiDB 是用 Go 寫的,Go 在中國的羣衆基礎是很是多的,目前也有不少人在貢獻。整個 TiDB 和TiKV 是高度協做的項目,由於 TiDB 目前還用到了 etcd ,咱們在和 CoreOS 在密切的合做,也特別感謝 CoreOS 幫咱們作了不少的支持,咱們也爲 CoreOS 的 etcd 提了一些 patch。同時,TiKV 使用 RocksDB ,因此咱們也爲 RocksDB 提了一些 patch 和 test,咱們也很是感謝 Facebook RocksDB team 對咱們項目的支持。

另一個是 PD,就是咱們前面提的 Placement Driver,它負責監控整個系統。這部分的算法比較好玩,你們若是有興趣的話,能夠去本身控制整個集羣的調度,它和 Kubernetes 或者是Mesos 的調度算法是不同的,由於它調度的維度實際上比那個要更多。好比說磁盤的容量,你的 leader 的數量,你的網絡當前的使用狀況,你的 IO 的負載和 CPU 的負載均可以放進去。同時你還可讓它調度不要跨一個機房裏面建多個副本。


感謝魏星對本文的審校。

給InfoQ中文站投稿或者參與內容翻譯工做,請郵件至editors@cn.infoq.com。也歡迎你們經過新浪微博(@InfoQ@丁曉昀),微信(微信號:InfoQChina)關注咱們。

原文:http://www.infoq.com/cn/articles/how-to-build-a-distributed-database?utm_campaign=infoq_content&utm_source=infoq&utm_medium=feed&utm_term=global

相關文章
相關標籤/搜索