使用 TiKV 構建分散式類 Redis 服務
什麼是 Redis
是一個開源的,高效能的,支援多種資料結構的記憶體資料庫,已經被廣泛用於資料庫,快取,訊息佇列等領域。它有著豐富的資料結構支援,譬如 String,Hash,Set 和 Sorted Set,使用者透過它們能構建自己的高效能應用。
Redis 非常快,沒準是世界上最快的資料庫了,它雖然使用記憶體,但也提供了一些持久化機制以及非同步複製機制來保證資料的安全。
Redis 的不足
Redis 非常酷,但它也有一些問題:
記憶體很貴,而且並不是無限容量的,所以我們不可能將大量的資料存放到一臺機器。
非同步複製並不能保證 Redis 的資料安全。
Redis 提供了 transaction mode,但其實並不滿足 ACID 特性。
Redis 提供了叢集支援,但也不能支援跨多個節點的分散式事務。
所以有時候,我們需要一個更強大的資料庫,雖然在延遲上面可能趕不上 Redis,但也有足夠多的特性,譬如:
豐富的資料結構
高吞吐,能接受的延遲
強資料一致
水平擴充套件
分散式事務
為什麼選擇 TiKV
大約 4 年前,我開始解決上面提到的 Redis 遇到的一些問題。為了讓資料持久化,最直觀的做法就是將資料儲存到硬碟上面,而不是在記憶體裡面。所以我開發了 ,一個使用 Redis 協議,提供豐富資料結構,但將資料放在 RocksDB 的資料庫。LedisDB 並不是完全相容 Redis,所以後來,我和其他同事繼續建立了 ,一個完全相容 Redis 的資料庫。
無論是 LedisDB 還是 RebornDB,因為他們都是將資料放在硬碟,所以能儲存更大量的資料。但它們仍然不能提供 ACID 的支援,另外,雖然我們可以透過 去提供叢集的支援,我們也不能很好的支援全域性的分散式事務。
所以我們需要另一種方式,幸運的是,我們有。
TiKV 是一個高效能,支援分散式事務的 key-value 資料庫。雖然它僅僅提供了簡單的 key-value API,但基於 key-value,我們可以構造自己的邏輯去建立更強大的應用。譬如,我們就構建了 ,一個基於 TiKV 的,相容 MySQL 的分散式關係型資料庫。TiDB 透過將 database 的 schema 對映到 key-value 來支援了相關 SQL 特性。所以對於 Redis,我們也可以採用同樣的辦法 - 構建一個支援 Redis 協議的服務,將 Redis 的資料結構對映到 key-value 上面。
如何開始
整個架構非常簡單,我們僅僅需要做的就是構建一個 Redis 的 Proxy,這個 Proxy 會解析 Redis 協議,然後將 Redis 的資料結構對映到 key-value 上面。
Redis Protocol
Redis 協議被叫做 (Redis Serialization Protocol),它是文字型別的,可讀性比較好,並且易於解析。它使用 “rn” 作為每行的分隔符並且用不同的字首來代表不同的型別。例如,對於簡單的 String,第一個位元組是 “+”,所以一個 “OK” 行就是 “+OKrn”。
大多數時候,客戶端會使用最通用的 Request-Response 模型用於跟 Redis 進行互動。客戶端會首先傳送一個請求,然後等待 Redis返回結果。請求是一個 Array,Array 裡面元素都是 bulk strings,而返回值則可能是任意的 RESP 型別。Redis 同樣支援其他通訊方式:
Pipeline - 這種模式下面客戶端會持續的給 Redis 傳送多個請求,然後等待 Redis 返回一個結果。
Push - 客戶端會在 Redis 上面訂閱一個 channel,然後客戶端就會從這個 channel 上面持續受到 Redis push 的資料。
下面是一個簡單的客戶端傳送 LLEN mylist
命令到 Redis 的例子:
C: *2rn C: $4rn C: LLENrn C: $6rn C: mylistrn S: :48293rn
客戶端會傳送一個帶有兩個 bulk string 的 array,第一個 bulk string 的長度是 4,而第二個則是 6。Redis 會返回一個 48293 整數。正如你所見,RESP 非常簡單,自然而然的,寫一個 RESP 的解析器也是非常容易的。
作者建立了一個 Go 的庫 ,基於這個庫,我們能非常容易的從連線上面解析出 RESP,一個簡單的例子:
// Create a buffer IO from the connection.br := bufio.NewReaderSize(conn, 4096)// Create a RESP reader.r := goredis.NewRespReader(br)// Parse the Requestreq := r.ParseRequest()
函式 ParseRequest
返回一個解析好的 request,它是一個 [][]byte
型別,第一個欄位是函式名字,譬如 “LLEN”,然後後面的欄位則是這個命令的引數。
TiKV 事務 API
在我們開始之前,作者將會給一個簡單實用 TiKV 事務 API 的例子,我們呼叫 Begin
開始一個事務:
txn, err := db.Begin()
函式 Begin
建立一個事務,如果出錯了,我們需要判斷 err,不過後面作者都會忽略 err 的處理。
當我們開始了一個事務之後,我們就可以幹很多操作了:
value, err := txn.Get([]byte(“key”))// Do something with value and then update the newValue to the key.txn.Put([]byte(“key”), newValue)
上面我們得到了一個 key 的值,並且將其更新為新的值。TiKV 使用樂觀事務模型,它會將所有的改動都先快取到本地,然後在一起提交給 Server。
// Commit the transactiontxn.Commit(context.TODO())
跟其他事務處理一樣,我們也可以回滾這個事務:
txn.Rollback()
如果兩個事務操作了相同的 key,它們就會衝突。一個事務會提交成功,而另一個事務會出錯並且回滾。
對映 Data structure 到 TiKV
現在我們知道了如何解析 Redis 協議,如何在一個事務裡面做操作,下一步就是支援 Redis 的資料結構了。Redis 主要有 4 中資料結構:String,Hash,Set 和 Sorted Set,但是對於 TiKV 來說,它只支援 key-value,所以我們需要將這些資料結構對映到 key-value。
首先,我們需要區分不同的資料結構,一個非常容易的方式就是在 key 的後面加上 Type flag。例如,我們可以將 ’s’ 新增到 String,所以一個 String key “abc” 在 TiKV 裡面其實就是 “abcs”。
對於其他型別,我們可能需要考慮更多,譬如對於 Hash 型別,我們需要支援如下操作:
HSET key field1 value1 HSET key field2 value2 HLEN key
一個 Hash 會有很多 fields,我有時候想知道整個 Hash 的個數,所以對於 TiKV,我們不光需要將 Hash 的 key 和 field 合在一起變成 TiKV 的一個 key,也同時需要用另一個 key 來儲存整個 Hash 的長度,所以整個 Hash 的佈局類似:
key + ‘h’ -> length key + ‘f’ + field1 -> value key + ‘f’ + field2 -> value
如果我們不儲存 length,那麼如果我們想知道 Hash 的 length,每次都需要去掃整個 Hash 得到所有的 fields,這個其實並不高效。但如果我們用另一個 key 來儲存 length,任何時候,當我們加入一個新的 field,我們都需要去更新這個 length 的值,這也是一個開銷。對於我來說,我傾向於使用另一個 key 來儲存 length,因為 HLEN
是一個高頻的操作。
例子
作者構建了一個非常簡單的例子 ,裡面只支援 String 和 Hash 的一些操作,我們可以 clone 下來並編譯:
git clone $GOPATH/src/github.com/siddontang/redis-tikv-examplecd $GOPATH/src/github.com/siddontang/redis-tikv-example go build
在執行之前,我們需要啟動 TiKV,可以參考,然後執行:
./redis-tikv-example
這個例子會監聽埠 6380,然後我們可以用任意的 Redis 客戶端,譬如 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"
尾聲
現在已經有一些公司基於 TiKV 來構建了他們自己的 Redis Server,並且也有一個開源的專案 做了相同的事情。tidis
已經比較完善,如果你想替換自己的 Redis,可以嘗試一下。
正如同你所見,TiKV 其實算是一個基礎的元件,我們可以在它的上面構建很多其他的應用。如果你對我們現在做的事情感興趣,歡迎聯絡我:。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2236/viewspace-2806665/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 構建基於RocketMQ的分散式事務服務MQ分散式
- 微服務分散式架構之redis篇微服務分散式架構Redis
- [分散式]--Dubbo分散式服務框架-服務治理分散式框架
- 利用分散式賬本技術構建更加智慧的公共服務分散式
- swoole 服務的建構函式函式
- Spring Cloud構建分散式電子商務平臺:服務消費(基礎)SpringCloud分散式
- 分散式架構-可靠通訊-服務安全分散式架構
- 使用開源技術構建有贊分散式 KV 儲存服務分散式
- Spring Cloud雲服務架構 - 企業分散式微服務雲架構構建SpringCloud架構分散式微服務
- 使用SpringBoot構建REST服務-什麼是REST服務Spring BootREST
- 分散式服務資料一致性-redis篇分散式Redis
- Spring Cloud構建微服務架構:分散式配置中心(加密解密)SpringCloud微服務架構分散式加密解密
- 整合spring cloud雲服務架構 - 企業分散式微服務雲架構構建SpringCloud架構分散式微服務
- 分散式服務框架 gRPC分散式框架RPC
- 使用zipKin構建NetCore分散式鏈路跟蹤NetCore分散式
- 基於Redis構建微服務的反應式架構 - bitsrcRedis微服務架構
- 架構設計:分散式結構下,服務部署釋出架構分散式
- spring cloud微服務分散式雲架構--服務註冊(consul)SpringCloud微服務分散式架構
- 如何在一週內使用Kafka+Redis構建分散式排行榜系統? - AritraKafkaRedis分散式
- JEESZ架構、分散式服務:Dubbo+Zookeeper+Proxy+Restful架構分散式REST
- 阿里分散式服務框架Dubbo的架構總結阿里分散式框架架構
- (四)整合spring cloud雲服務架構 - 企業分散式微服務雲架構構建SpringCloud架構分散式微服務
- 使用Spring Boot 2.0快速構建服務元件Spring Boot元件
- springmvc + mybatis + ehcache + redis 分散式架構SpringMVCMyBatisRedis分散式架構
- 建構函式和類函式
- 《使用Gin框架構建分散式應用》讀後感框架架構分散式
- spring cloud微服務分散式雲架構-服務消費者FeignSpringCloud微服務分散式架構
- 一個內建於專案的分散式快取服務分散式快取
- 類的建構函式和解構函式函式
- 天翼雲分散式快取服務(Redis)的幾個核心概念分散式快取Redis
- node.js 中使用redis實現分散式事務鎖Node.jsRedis分散式
- 分散式系統架構之構建你的任務排程中心分散式架構
- [分散式][Redis]Redis分散式框架搭建與整合分散式Redis框架
- 使用 Nginx 構建前端日誌統計服務Nginx前端
- spring cloud微服務分散式雲架構(三)-服務消費者(Feign)SpringCloud微服務分散式架構
- 構建dubbo分散式平臺-maven構建根專案分散式Maven
- JS 建構函式與類JS函式
- 跨Mysql、Redis、Mongo的分散式事務MySqlRedisGo分散式