玩轉Redis叢集之Codis

面向Google程式設計發表於2018-11-23

近幾年,隨著網際網路的飛速發展,作為程式設計師,我們需要處理的資料規模也在不斷擴大。如果你使用Redis作為資料庫時,面臨大資料高併發的場景時,單個Redis例項就會顯得力不從心。這時Redis的叢集方案應運而生,他將眾多Redis例項綜合起來,共同應對大資料高併發的場景。

Codis是Redis叢集方案的一種。它是由豌豆莢的中介軟體團隊開發的,所以,它有一套詳細的中文版README,方便大家學習。

Codis架構

它的架構如上圖所示,由codis-proxy對外提供Redis的服務。ZooKeeper用來儲存資料路由表和codis-proxy節點的元資訊。codis-proxy會監聽所有的redis叢集,當Redis叢集處理能力達到上限時,可以動態增加Redis例項來實現擴容的需求。

元件介紹

  • Codis Proxy:像剛才所說的,它對外提供Redis服務,除了一些不支援的命令外(不支援的命令列表),表現的和原生的Redis沒有區別。由於它是無狀態的,所以我們可以部署多個節點,從而保證了可用性。
  • Codis Dashboard:叢集管理工具,支援Codis Proxy的新增刪除以及資料遷移等操作。對於一個Codis叢集,Dashboard最多部署一個
  • Codis Admin:叢集管理的命令列工具
  • Codis FE:叢集管理介面,多個Codis叢集可以共用一個Codis FE,通過配置檔案管理後端的codis-dashboard
  • Storage:為叢集提供外部儲存,目前支援ZooKeeper、Etcd、Fs三種。
  • Codis Server:基於3.2.8分支開發,增加額外的資料結構,用來支援slot有關的操作及資料遷移指令。

Codis分片原理

現在我們已經知道了Codis會將指定key的Redis命令轉發給下層的Redis。那麼Codis如何知道某個key在哪個Redis上呢。

Codis採用Pre-sharding的技術來實現資料分片,預設分為1024個slot(0-1023)。Codis在接收到命令時,先對key進行crc32運算,然後再對1024取餘,得到的結果就是對應的slot。然後就可以將命令轉發給slot對應的Redis例項進行處理了。

擴容操作

Codis的動態擴容/縮容能力是它的一大亮點之一。它可以對Redis客戶端透明。在擴容時,Codis提供了SLOTSSCAN指令,這個指令可以掃描指定的slot上的所有key,然後對每個key進行遷移。在擴容過程中,如果有新的key需要轉發到正在遷移的slot上,那麼codis會判斷這個key是否需要遷移,如果需要,則對指定的key進行強制遷移,遷移完成後,再將命令轉發到新的Redis上。

看了上面的介紹是不是覺得擴容是一件很麻煩的事情,Codis已經為我們考慮到這點了,它提供了自動均衡的功能,只需要在介面上點一下"Auto Rebalance"按鈕,就可以自動實現slot遷移(可以說非常貼心了)。縮容也比較簡單,只需要將需要下線的例項的slot遷移到其他例項上,然後刪除group就可以了。

Codis的缺點

當Redis Group的master掛掉時,codis不會自動將某個slave升為master,codis提供了一個叫做codis-ha的工具,這個工具通過dashboard提供RESTful API來實現自動主從切換。但是,當codis將某個slave升為master時,其他的slave並不會改變狀態,仍然會從舊的master上同步資料,這就導致了主從資料不一致。因此,當出現主從切換時,需要管理員手動建立新的sync action來完成資料同步。

此外,Codis還面臨一個比較尷尬的情況就是,由於它不是Redis“親生”的,因此,當Redis釋出了new feature時,它總會慢一步,因此,它需要在Redis釋出new feature後迅速趕上,以保持競爭力。

搭建Codis

  1. 安裝Go執行環境

Mac使用者可以參考這個,其他系統的使用者也可以看這個教程

安裝好以後,驗證一下是否安裝成功

$ go version
go version go1.11.2 darwin/amd64
複製程式碼
  1. 下載Codis原始碼

需要下載到指定目錄:$GOPATH/src/github.com/CodisLabs/codis

$ mkdir -p $GOPATH/src/github.com/CodisLabs
$ cd $_ && git clone https://github.com/CodisLabs/codis.git -b release3.2
複製程式碼
  1. 編譯原始碼

進入原始碼的codis目錄,直接執行make命令即可。編譯完成後,bin目錄下的結構應該是這樣的

$ ll bin 
total 178584
drwxr-xr-x  8 jackey  staff   256B 11 13 10:57 assets
-rwxr-xr-x  1 jackey  staff    17M 11 13 10:57 codis-admin
-rwxr-xr-x  1 jackey  staff    18M 11 13 10:56 codis-dashboard
-rw-r--r--  1 jackey  staff     5B 11 21 18:06 codis-dashboard.pid
-rwxr-xr-x  1 jackey  staff    16M 11 13 10:57 codis-fe
-rw-r--r--  1 jackey  staff     5B 11 21 18:24 codis-fe.pid
-rwxr-xr-x  1 jackey  staff    15M 11 13 10:57 codis-ha
-rwxr-xr-x  1 jackey  staff    19M 11 13 10:57 codis-proxy
-rw-r--r--  1 jackey  staff     5B 11 21 18:08 codis-proxy.pid
-rwxr-xr-x  1 jackey  staff   1.1M 11 13 10:56 codis-server
-rwxr-xr-x  1 jackey  staff    98K 11 13 10:56 redis-benchmark
-rwxr-xr-x  1 jackey  staff   161K 11 13 10:56 redis-cli
-rwxr-xr-x  1 jackey  staff   1.1M 11 13 10:56 redis-sentinel
-rw-r--r--  1 jackey  staff   170B 11 13 10:56 version
複製程式碼

到這裡為止,我們的準備工作已經完成了。接下來我們來看一下如何在單機環境啟動測試叢集。

  1. 啟動codis-dashboard

進入admin目錄,執行codis-dashboard-admin.sh指令碼

$ ./codis-dashboard-admin.sh start
/Users/jackey/Documents/go_workspace/src/github.com/CodisLabs/codis/admin/../config/dashboard.toml
starting codis-dashboard ... 
複製程式碼

然後檢視日誌,觀察是否啟動成功

$ tail -100 ../log/codis-dashboard.log.2018-11-21
2018/11/21 18:06:57 main.go:155: [WARN] option --pidfile = /Users/jackey/Documents/go_workspace/src/github.com/CodisLabs/codis/bin/codis-dashboard.pid
2018/11/21 18:06:57 topom.go:429: [WARN] admin start service on [::]:18080
2018/11/21 18:06:57 fsclient.go:195: [INFO] fsclient - create /codis3/codis-demo/topom OK
2018/11/21 18:06:58 topom_sentinel.go:169: [WARN] rewatch sentinels = []
2018/11/21 18:06:58 main.go:179: [WARN] [0xc000374120] dashboard is working ...
複製程式碼
  1. 啟動codes-proxy

執行codis-proxy-admin.sh指令碼

$ ./codis-proxy-admin.sh start                   
/Users/jackey/Documents/go_workspace/src/github.com/CodisLabs/codis/admin/../config/proxy.toml
starting codis-proxy ...
複製程式碼

檢視是否啟動成功

$ tail -100 ../log/codis-proxy.log.2018-11-21
2018/11/21 18:08:34 proxy_api.go:44: [WARN] [0xc0003262c0] API call /api/proxy/start/212d13827c84455d487036d4bb07ce15 from 10.1.201.43:58800 []
2018/11/21 18:08:34 proxy_api.go:44: [WARN] [0xc0003262c0] API call /api/proxy/sentinels/212d13827c84455d487036d4bb07ce15 from 10.1.201.43:58800 []
2018/11/21 18:08:34 proxy.go:293: [WARN] [0xc0003262c0] set sentinels = []
2018/11/21 18:08:34 main.go:343: [WARN] rpc online proxy seems OK
2018/11/21 18:08:35 main.go:233: [WARN] [0xc0003262c0] proxy is working ...
複製程式碼
  1. 啟動codis-server

執行codis-server-admin.sh指令碼

$ ./codis-server-admin.sh start
/Users/jackey/Documents/go_workspace/src/github.com/CodisLabs/codis/admin/../config/redis.conf
starting codis-server ... 
複製程式碼

檢視是否啟動成功

$ tail -100 /tmp/redis_6379.log
12854:M 21 Nov 18:09:29.172 * Increased maximum number of open files to 10032 (it was originally set to 256).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 3.2.11 (de1ad026/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 12854
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

12854:M 21 Nov 18:09:29.187 # Server started, Redis version 3.2.11
12854:M 21 Nov 18:09:29.187 * The server is now ready to accept connections on port 6379
複製程式碼

如果執行報錯,請先確認使用的使用者是否有/tmp/redis_6379.log檔案的讀寫許可權。

這裡我為了測試Codis的Auto Rebalance功能,所以啟動了兩個例項。方法很簡單,只需要分別將admin/codis-server-admin.sh和config/redis.conf這兩個檔案複製一份,修改檔案中的埠等資訊,然後再以同樣的方法執行一下新的指令碼。

  1. 啟動codis-fe

執行codis-fe-admin.sh指令碼

$ ./codis-fe-admin.sh start

starting codis-fe ... 
複製程式碼

檢視是否執行成功

$ tail -100 ../log/codis-fe.log.2018-11-21 
2018/11/21 18:24:33 main.go:101: [WARN] set ncpu = 4
2018/11/21 18:24:33 main.go:104: [WARN] set listen = 0.0.0.0:9090
2018/11/21 18:24:33 main.go:120: [WARN] set assets = /Users/jackey/Documents/go_workspace/src/github.com/CodisLabs/codis/bin/assets
2018/11/21 18:24:33 main.go:162: [WARN] set --filesystem = /tmp/codis
2018/11/21 18:24:33 main.go:216: [WARN] option --pidfile = /Users/jackey/Documents/go_workspace/src/github.com/CodisLabs/codis/bin/codis-fe.pid
複製程式碼

全部啟動成功之後,就可以訪問http://127.0.0.1:9090,開始設定叢集了。

  1. 新增group

剛剛我們啟動了兩個codis-server,因此,我們可以new兩個group,然後分別將codis-server加入到兩個group中

codis-group

  1. 初始化slot

一開始所有的slot都是offline狀態。

codis-slot-init

點選下方的Rebalance All Slots按鈕,codis會自動把1024個slot分配給兩個group(每個分512個)。

codis-slot

當然,也可以手動分配slot,比如,我們將group-1的10個slot分配給group-2,只需要點選Migrate Some按鈕即可。

codis-slot-manual

小結

Codis的動態擴容能力簡直好用到爆 ,不過目前也存在一些問題(前面我們也介紹過了)。所以你的叢集是否要使用Codis還需要看具體的需求。最後還是要為Codis的開發團隊點贊,另外他們還開發出了一套分散式資料庫——TiDB。有興趣的同學可以學習一下。

相關文章