使用 TiKV 構(gòu)建分布式類(lèi) Redis 服務(wù)

什么是 Redis

Redis 是一個(gè)開(kāi)源的程腹,高性能的匣吊,支持多種數(shù)據(jù)結(jié)構(gòu)的內(nèi)存數(shù)據(jù)庫(kù),已經(jīng)被廣泛用于數(shù)據(jù)庫(kù)寸潦,緩存色鸳,消息隊(duì)列等領(lǐng)域。它有著豐富的數(shù)據(jù)結(jié)構(gòu)支持见转,譬如 String命雀,Hash,Set 和 Sorted Set斩箫,用戶(hù)通過(guò)它們能構(gòu)建自己的高性能應(yīng)用吏砂。

Redis 非常快乘客,沒(méi)準(zhǔn)是世界上最快的數(shù)據(jù)庫(kù)了狐血,它雖然使用內(nèi)存,但也提供了一些持久化機(jī)制以及異步復(fù)制機(jī)制來(lái)保證數(shù)據(jù)的安全易核。

Redis 的不足

Redis 非承僦酷,但它也有一些問(wèn)題:

  1. 內(nèi)存很貴牡直,而且并不是無(wú)限容量的缀匕,所以我們不可能將大量的數(shù)據(jù)存放到一臺(tái)機(jī)器。
  2. 異步復(fù)制并不能保證 Redis 的數(shù)據(jù)安全井氢。
  3. Redis 提供了 transaction mode弦追,但其實(shí)并不滿足 ACID 特性。
  4. Redis 提供了集群支持花竞,但也不能支持跨多個(gè)節(jié)點(diǎn)的分布式事務(wù)劲件。

所以有時(shí)候掸哑,我們需要一個(gè)更強(qiáng)大的數(shù)據(jù)庫(kù),雖然在延遲上面可能趕不上 Redis零远,但也有足夠多的特性苗分,譬如:

  1. 豐富的數(shù)據(jù)結(jié)構(gòu)
  2. 高吞吐,能接受的延遲
  3. 強(qiáng)數(shù)據(jù)一致
  4. 水平擴(kuò)展
  5. 分布式事務(wù)

為什么選擇 TiKV

大約 4 年前牵辣,我開(kāi)始解決上面提到的 Redis 遇到的一些問(wèn)題摔癣。為了讓數(shù)據(jù)持久化,最直觀的做法就是將數(shù)據(jù)保存到硬盤(pán)上面纬向,而不是在內(nèi)存里面择浊。所以我開(kāi)發(fā)了 LedisDB,一個(gè)使用 Redis 協(xié)議逾条,提供豐富數(shù)據(jù)結(jié)構(gòu)琢岩,但將數(shù)據(jù)放在 RocksDB 的數(shù)據(jù)庫(kù)。LedisDB 并不是完全兼容 Redis师脂,所以后來(lái)担孔,我和其他同事繼續(xù)創(chuàng)建了 RebornDB,一個(gè)完全兼容 Redis 的數(shù)據(jù)庫(kù)吃警。

無(wú)論是 LedisDB 還是 RebornDB糕篇,因?yàn)樗麄兌际菍?shù)據(jù)放在硬盤(pán),所以能存儲(chǔ)更大量的數(shù)據(jù)酌心。但它們?nèi)匀徊荒芴峁?ACID 的支持拌消,另外,雖然我們可以通過(guò)codis 去提供集群的支持安券,我們也不能很好的支持全局的分布式事務(wù)拼坎。

所以我們需要另一種方式,幸運(yùn)的是完疫,我們有TiKV

TiKV 是一個(gè)高性能债蓝,支持分布式事務(wù)的 key-value 數(shù)據(jù)庫(kù)壳鹤。雖然它僅僅提供了簡(jiǎn)單的 key-value API,但基于 key-value饰迹,我們可以構(gòu)造自己的邏輯去創(chuàng)建更強(qiáng)大的應(yīng)用芳誓。譬如,我們就構(gòu)建了 TiDB 啊鸭,一個(gè)基于 TiKV 的锹淌,兼容 MySQL 的分布式關(guān)系型數(shù)據(jù)庫(kù)。TiDB 通過(guò)將 database 的 schema 映射到 key-value 來(lái)支持了相關(guān) SQL 特性赠制。所以對(duì)于 Redis赂摆,我們也可以采用同樣的辦法 - 構(gòu)建一個(gè)支持 Redis 協(xié)議的服務(wù),將 Redis 的數(shù)據(jù)結(jié)構(gòu)映射到 key-value 上面。

如何開(kāi)始

整個(gè)架構(gòu)非常簡(jiǎn)單烟号,我們僅僅需要做的就是構(gòu)建一個(gè) Redis 的 Proxy绊谭,這個(gè) Proxy 會(huì)解析 Redis 協(xié)議,然后將 Redis 的數(shù)據(jù)結(jié)構(gòu)映射到 key-value 上面汪拥。

Redis Protocol

Redis 協(xié)議被叫做 RESP(Redis Serialization Protocol)达传,它是文本類(lèi)型的,可讀性比較好迫筑,并且易于解析宪赶。它使用 “rn” 作為每行的分隔符并且用不同的前綴來(lái)代表不同的類(lèi)型。例如脯燃,對(duì)于簡(jiǎn)單的 String搂妻,第一個(gè)字節(jié)是 “+”,所以一個(gè) “OK” 行就是 “+OKrn”曲伊。

大多數(shù)時(shí)候叽讳,客戶(hù)端會(huì)使用最通用的 Request-Response 模型用于跟 Redis 進(jìn)行交互》啬迹客戶(hù)端會(huì)首先發(fā)送一個(gè)請(qǐng)求岛蚤,然后等待 Redis返回結(jié)果。請(qǐng)求是一個(gè) Array懈糯,Array 里面元素都是 bulk strings涤妒,而返回值則可能是任意的 RESP 類(lèi)型。Redis 同樣支持其他通訊方式:

  1. Pipeline - 這種模式下面客戶(hù)端會(huì)持續(xù)的給 Redis 發(fā)送多個(gè)請(qǐng)求赚哗,然后等待 Redis 返回一個(gè)結(jié)果她紫。
  2. Push - 客戶(hù)端會(huì)在 Redis 上面訂閱一個(gè) channel,然后客戶(hù)端就會(huì)從這個(gè) channel 上面持續(xù)受到 Redis push 的數(shù)據(jù)屿储。

下面是一個(gè)簡(jiǎn)單的客戶(hù)端發(fā)送 LLEN mylist 命令到 Redis 的例子:

C: *2\r\n
C: $4\r\n
C: LLEN\r\n
C: $6\r\n
C: mylist\r\n

S: :48293\r\n

客戶(hù)端會(huì)發(fā)送一個(gè)帶有兩個(gè) bulk string 的 array贿讹,第一個(gè) bulk string 的長(zhǎng)度是 4,而第二個(gè)則是 6够掠。Redis 會(huì)返回一個(gè) 48293 整數(shù)民褂。正如你所見(jiàn),RESP 非常簡(jiǎn)單疯潭,自然而然的赊堪,寫(xiě)一個(gè) RESP 的解析器也是非常容易的。

作者創(chuàng)建了一個(gè) Go 的庫(kù) goredis竖哩,基于這個(gè)庫(kù)哭廉,我們能非常容易的從連接上面解析出 RESP,一個(gè)簡(jiǎn)單的例子:

// Create a buffer IO from the connection.
br := bufio.NewReaderSize(conn, 4096)
// Create a RESP reader.
r := goredis.NewRespReader(br)
// Parse the Request
req := r.ParseRequest()

函數(shù) ParseRequest 返回一個(gè)解析好的 request相叁,它是一個(gè) [][]byte 類(lèi)型遵绰,第一個(gè)字段是函數(shù)名字辽幌,譬如 “LLEN”,然后后面的字段則是這個(gè)命令的參數(shù)街立。

TiKV 事務(wù) API

在我們開(kāi)始之前舶衬,作者將會(huì)給一個(gè)簡(jiǎn)單實(shí)用 TiKV 事務(wù) API 的例子,我們調(diào)用 Begin 開(kāi)始一個(gè)事務(wù):

txn, err := db.Begin()

函數(shù) Begin 創(chuàng)建一個(gè)事務(wù)赎离,如果出錯(cuò)了逛犹,我們需要判斷 err,不過(guò)后面作者都會(huì)忽略 err 的處理梁剔。

當(dāng)我們開(kāi)始了一個(gè)事務(wù)之后虽画,我們就可以干很多操作了:

value, err := txn.Get([]byte(“key”))
// Do something with value and then update the newValue to the key.
txn.Put([]byte(“key”), newValue)

上面我們得到了一個(gè) key 的值,并且將其更新為新的值荣病。TiKV 使用樂(lè)觀事務(wù)模型码撰,它會(huì)將所有的改動(dòng)都先緩存到本地,然后在一起提交給 Server个盆。

// Commit the transaction
txn.Commit(context.TODO())

跟其他事務(wù)處理一樣脖岛,我們也可以回滾這個(gè)事務(wù):

txn.Rollback()

如果兩個(gè)事務(wù)操作了相同的 key,它們就會(huì)沖突颊亮。一個(gè)事務(wù)會(huì)提交成功柴梆,而另一個(gè)事務(wù)會(huì)出錯(cuò)并且回滾。

映射 Data structure 到 TiKV

現(xiàn)在我們知道了如何解析 Redis 協(xié)議终惑,如何在一個(gè)事務(wù)里面做操作绍在,下一步就是支持 Redis 的數(shù)據(jù)結(jié)構(gòu)了。Redis 主要有 4 中數(shù)據(jù)結(jié)構(gòu):String雹有,Hash偿渡,Set 和 Sorted Set,但是對(duì)于 TiKV 來(lái)說(shuō)霸奕,它只支持 key-value溜宽,所以我們需要將這些數(shù)據(jù)結(jié)構(gòu)映射到 key-value。

首先质帅,我們需要區(qū)分不同的數(shù)據(jù)結(jié)構(gòu)坑质,一個(gè)非常容易的方式就是在 key 的后面加上 Type flag。例如临梗,我們可以將 ’s’ 添加到 String,所以一個(gè) String key “abc” 在 TiKV 里面其實(shí)就是 “abcs”稼跳。

對(duì)于其他類(lèi)型盟庞,我們可能需要考慮更多,譬如對(duì)于 Hash 類(lèi)型汤善,我們需要支持如下操作:

HSET key field1 value1
HSET key field2 value2
HLEN key

一個(gè) Hash 會(huì)有很多 fields什猖,我有時(shí)候想知道整個(gè) Hash 的個(gè)數(shù)票彪,所以對(duì)于 TiKV,我們不光需要將 Hash 的 key 和 field 合在一起變成 TiKV 的一個(gè) key不狮,也同時(shí)需要用另一個(gè) key 來(lái)保存整個(gè) Hash 的長(zhǎng)度降铸,所以整個(gè) Hash 的布局類(lèi)似:

key + ‘h’ -> length
key + ‘f’ + field1 -> value
key + ‘f’ + field2 -> value 

如果我們不保存 length,那么如果我們想知道 Hash 的 length摇零,每次都需要去掃整個(gè) Hash 得到所有的 fields推掸,這個(gè)其實(shí)并不高效。但如果我們用另一個(gè) key 來(lái)保存 length驻仅,任何時(shí)候谅畅,當(dāng)我們加入一個(gè)新的 field,我們都需要去更新這個(gè) length 的值噪服,這也是一個(gè)開(kāi)銷(xiāo)毡泻。對(duì)于我來(lái)說(shuō),我傾向于使用另一個(gè) key 來(lái)保存 length粘优,因?yàn)?HLEN 是一個(gè)高頻的操作仇味。

例子

作者構(gòu)建了一個(gè)非常簡(jiǎn)單的例子 example ,里面只支持 String 和 Hash 的一些操作雹顺,我們可以 clone 下來(lái)并編譯:

git clone https://github.com/siddontang/redis-tikv-example.git $GOPATH/src/github.com/siddontang/redis-tikv-example

cd $GOPATH/src/github.com/siddontang/redis-tikv-example
go build

在運(yùn)行之前丹墨,我們需要啟動(dòng) TiKV,可以參考instruction无拗,然后執(zhí)行:

./redis-tikv-example

這個(gè)例子會(huì)監(jiān)聽(tīng)端口 6380带到,然后我們可以用任意的 Redis 客戶(hù)端,譬如 redis-cli 去連接:

redis-cli -p 6380
127.0.0.1:6380> set k1 a
OK
127.0.0.1:6380> get k1
"a"
127.0.0.1:6380> hset k2 f1 a
(integer) 1
127.0.0.1:6380> hget k2 f1
"a"

尾聲

現(xiàn)在已經(jīng)有一些公司基于 TiKV 來(lái)構(gòu)建了他們自己的 Redis Server英染,并且也有一個(gè)開(kāi)源的項(xiàng)目tidis 做了相同的事情揽惹。tidis 已經(jīng)比較完善,如果你想替換自己的 Redis四康,可以嘗試一下搪搏。

正如同你所見(jiàn),TiKV 其實(shí)算是一個(gè)基礎(chǔ)的組件闪金,我們可以在它的上面構(gòu)建很多其他的應(yīng)用疯溺。如果你對(duì)我們現(xiàn)在做的事情感興趣,歡迎聯(lián)系我:tl@pingcap.com哎垦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末囱嫩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子漏设,更是在濱河造成了極大的恐慌墨闲,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件郑口,死亡現(xiàn)場(chǎng)離奇詭異鸳碧,居然都是意外死亡盾鳞,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)瞻离,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)腾仅,“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了灌具?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵符衔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任受神,我火速辦了婚禮,結(jié)果婚禮上格侯,老公的妹妹穿的比我還像新娘鼻听。我一直安慰自己,他們只是感情好联四,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布撑碴。 她就那樣靜靜地躺著,像睡著了一般朝墩。 火紅的嫁衣襯著肌膚如雪醉拓。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天收苏,我揣著相機(jī)與錄音亿卤,去河邊找鬼。 笑死鹿霸,一個(gè)胖子當(dāng)著我的面吹牛排吴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播懦鼠,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼钻哩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了肛冶?” 一聲冷哼從身側(cè)響起街氢,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎睦袖,沒(méi)想到半個(gè)月后阳仔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年近范,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片延蟹。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡评矩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出阱飘,到底是詐尸還是另有隱情斥杜,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布沥匈,位于F島的核電站蔗喂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏高帖。R本人自食惡果不足惜缰儿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望散址。 院中可真熱鬧乖阵,春花似錦、人聲如沸预麸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吏祸。三九已至对蒲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贡翘,已是汗流浹背蹈矮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留床估,地道東北人含滴。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像丐巫,于是被迫代替她去往敵國(guó)和親谈况。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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

  • 分布式緩存技術(shù)PK:選擇Redis還是Memcached? 經(jīng)平臺(tái)同意授權(quán)轉(zhuǎn)載 作者:田京昆(騰訊后臺(tái)研發(fā)工程師)...
    meng_philip123閱讀 68,920評(píng)論 7 60
  • 本文將從Redis的基本特性入手,通過(guò)講述Redis的數(shù)據(jù)結(jié)構(gòu)和主要命令對(duì)Redis的基本能力進(jìn)行直觀介紹。之后概...
    團(tuán)長(zhǎng)plus閱讀 1,253評(píng)論 0 15
  • 叔本華說(shuō)联喘,人得不到滿足時(shí)便會(huì)痛苦华蜒。得到滿足時(shí),便會(huì)無(wú)趣豁遭。痛苦和無(wú)趣就像鐘擺的兩端叭喜,人就在這兩端之間晃著。 我不全是...
    功夫小蠅0007閱讀 308評(píng)論 0 0
  • 夢(mèng)想,很遠(yuǎn)也很近 夢(mèng)想闪幽,并不是遙不可及 只是你不夠努力罷了啥辨, 無(wú)需去找其他的任何理由。 你已經(jīng)習(xí)慣了窩在你的舒適區(qū)...
    SH_215a閱讀 425評(píng)論 0 1
  • 中國(guó)人似乎對(duì)證書(shū)情有獨(dú)鐘盯腌,每個(gè)國(guó)人一生都會(huì)陸續(xù)獲得各種各樣的證書(shū)溉知,身份證、結(jié)婚證腊嗡、房產(chǎn)證着倾、獨(dú)生子女證、駕駛證等等燕少,...
    董余蘭閱讀 630評(píng)論 0 1