[実演]Redis Clusterの仕組みを完全に理解する


この記事は、[株式会社CAM Advent Calendar 2019 ](https://qiita.com/advent-calendar/2019/cam-inc)16日目の記事です:golf:
前回は @tosaka07 さんによる、 [最適化から見る、Swiftの / 2 と * 0.5 - Qiita](https://qiita.com/tosaka07/items/6cf6385ba59ea7360b01)でした。

# はじめに
業務でredisを使っていて、使い方を知っているだけではなくちゃんとどのように動いているかを理解したい！と思ってredis clusterについてまとめてみました！
実際にクラスターを組んでみるので、かなりわかりやすいかなーと思います！

# クラスターとは
クラスター（Cluster）は「房」「集団」「群れ」の意味。ネットワークに接続した複数のコンピューターを連携して1つのコンピューターシステムに統合し、処理や運用を効率化するシステムのこと

# Redis Clusterとは
- redisインスタンスをクラスタリングすることができる機能
- クラスター全体であるデータがどのノード(後述)に保存されるかを把握している
  - ノード間でリダイレクトすることによって、どのノードから接続しても指定するデータにたどり着ける
- マルチマスター構成を採用していて、データは複数のRedisサーバに自動的に分散される(シャーディング)
- マスターがタヒんでも自動的にスレーブがマスターに昇格する(自動フェイルオーバー)

# 用語解説
今回出てくる用語を解説していきます。
redisに関わらずデータベースのレプリケーション全般に共通する単語なので、ぜひ覚えましょう！

## ノード
redisインスタンスそのもの

## マスターノード
2種類のノードのうち、読み込み・書き込みができノード
マスターノードは0個以上のスレーブノードを持てる
スレーブノードとデータを共有している

## スレーブノード
2種類のノードのうち、読み込み専用ノード
マスターノードとデータを共有している
マスターが死んだら自動でフェイルオーバーする

## シャード
ノードをまとめているグループ
1つのシャード内に複数のノードが存在することで、シャード内のノード間でデータが同期(レプリケーション)される。

## 構成例
下の図では3つのシャードの中にマスターノードとスレーブノードが1つずつ構成されています。
![れでぃす くらすたーのしくみのかいせつ (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318540/410a089d-7cdf-e2c5-f41c-38220e56a94a.png)


# 実際にクラスター構成を組んでみる

すでにredisをインストールしている前提で進んでいきます。
検証に使ったバージョンは`5.0.5`です。

例で示した構成と同じ構成でクラスターを組んでいきます。
ローカルで立てるのでIPアドレスはどれも`127.0.0.1`です

|  ポート  | 役割  |
| ---- | ---- |
|  7000  |  Master  |
|  7001  |  Master  |
|  7002  |  Master  |
|  7003  |  Slave  |
|  7004  |  Slave  |
|  7005  |  Slave  |

## ディレクトリを準備
```
$ mkdir ~/sample-redis-cluster
$ cd ~/sample-redis-cluster
$ mkdir 700{0,1,2,3,4,5}
```
## 作成した各ディレクトリに設定ファイルを用意
```
$ cd 7000
$ vim redis.conf
```
```conf:redis.conf
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes 
```
```
$ cd ../7001
$ vim redis.conf
```
```conf:redis.conf
port 7001
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes 
```
みたいな感じに各ディレクトリに設定ファイルを用意します。
portだけ変えてあとは同じです。

## サーバーを起動する
コマンドラインツールの画面分割を利用しながら7000~7005の各ディレクトリ内でサーバーを起動します。
起動するとnodes.confというファイルが生成されます.

`$ redis-server redis.conf`
・
・
・
*注意:* 
各ディレクトリに入らずに`$ redis-server 7000/redis.conf`のように起動するとsample-redis-cluster内にnodes.confが生成されます。1台目は問題なく起動できるのですが、2台目を起動しようとすると
下記のようなエラーが出ます。

```
 Sorry, the cluster configuration file nodes.conf is already used by a different Redis Cluster node. Please make sure that different nodes use different cluster configuration files.
```
内容としてはすでにnodes.confは使用されているよー的な内容です。
なので必ず各ディレクトリ内でサーバーを立てましょう。

## クラスターを構築
さらに別のコマンドラインを使って構築していきます。
下記のコマンドを叩いてください。

```
$ redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1
```

`--cluster-replicas 1` は1ノードにつき最低何個のレプリカを割り当てるかのオプションです。今回は1つ割り当てます。

これを叩くと

```
Can I set the above configuration? (type 'yes' to accept):
```
と聞かれるのでyesと打ったらクラスタ構築完了です。

## 触ってみる
クラスタのインスタンスに入って、`CLUSTER NODES`というコマンドを叩いてみましょう

```
$ redis-cli -p 7000 -c
127.0.0.1:7000> cluster nodes
1ce9bfaffd38e89209fc946d3c184d4e89c5d358 127.0.0.1:7005@17005 slave 765030e4d8c4ad2a90aef5a18c09bd10d8a634b3 0 1575817912219 6 connected
b67a6e13e4aefad72d18a2d40476d6eb14783ec0 127.0.0.1:7001@17001 master - 0 1575817910171 2 connected 5461-10922
743c3c84ce696199da0419880eefda902b4fe509 127.0.0.1:7003@17003 slave b67a6e13e4aefad72d18a2d40476d6eb14783ec0 0 1575817911597 4 connected
24f6a74a5cbe2f754f5453f73f88899c0ae0fc16 127.0.0.1:7004@17004 slave 3538af0b31d0275ee617963863356c0cdbab5e82 0 1575817912000 5 connected
3538af0b31d0275ee617963863356c0cdbab5e82 127.0.0.1:7002@17002 master - 0 1575817911189 3 connected 10923-16383
765030e4d8c4ad2a90aef5a18c09bd10d8a634b3 127.0.0.1:7000@17000 myself,master - 0 1575817911000 1 connected 0-5460
```

このコマンドでどんなクラスター構成が組まれているのかが確認できます。
7000, 7001, 7002がmasterで7003, 7004, 7005がslaveだということが確認できますね
slaveの横にidが書かれていますが、これはどのmasterノードに対するslaveノードであるかということです。
今回の例だと7005は7000のslaveであることがわかりますね。

# slot
もう一度`CLUSTER NODES`で出力された結果をみてみましょう。

```
$ redis-cli -p 7000 -c
127.0.0.1:7000> cluster nodes
1ce9bfaffd38e89209fc946d3c184d4e89c5d358 127.0.0.1:7005@17005 slave 765030e4d8c4ad2a90aef5a18c09bd10d8a634b3 0 1575817912219 6 connected
b67a6e13e4aefad72d18a2d40476d6eb14783ec0 127.0.0.1:7001@17001 master - 0 1575817910171 2 connected 5461-10922
743c3c84ce696199da0419880eefda902b4fe509 127.0.0.1:7003@17003 slave b67a6e13e4aefad72d18a2d40476d6eb14783ec0 0 1575817911597 4 connected
24f6a74a5cbe2f754f5453f73f88899c0ae0fc16 127.0.0.1:7004@17004 slave 3538af0b31d0275ee617963863356c0cdbab5e82 0 1575817912000 5 connected
3538af0b31d0275ee617963863356c0cdbab5e82 127.0.0.1:7002@17002 master - 0 1575817911189 3 connected 10923-16383
765030e4d8c4ad2a90aef5a18c09bd10d8a634b3 127.0.0.1:7000@17000 myself,master - 0 1575817911000 1 connected 0-5460
```
マスターノードの情報をみてください。
一番右に`0-5460`や`5461-10922`のような数字があることが確認できます。
これは各シャードに割り振られたスロットを指し示しています。

## slotとは
redis clusterにはslot(スロット)という概念があります。
スロットは0番~16383番まであり、
redis clusterで値を格納するとき、格納するデータを0~16383の番号を返すアルゴリズムにかけます。
その返ってきた番号のスロットがあるノードにデータが格納されます。
このようにしてredis clusterは水平分割(シャーディング)をしているのです！

クラスターを組む時、特に指定しなければ `16384/マスターノードの数`個のスロットが各マスターノードに割り振られます。
上記では7000番ポートのノードには0~5460番、7001番ポートのノードには5461~10922番、7002番ポートのノードには10923~16383番のスロットが割り振られているということになります。

### 格納する値がかけられるアルゴリズム
一応紹介しておきます。

```
HASH SLOT  = CRC16(key) mod 16384
```

### データを書き込んでみる
それでは実際にデータを書き込んでslotを体験しましょう！

```
$ redis-cli -p 7000 -c
127.0.0.1:7000> set hoge 'hoge'
OK
127.0.0.1:7000>
```
はい、hogeというキーで`hoge`という文字列をセットしてみました。
特に変わったことはなく、slotを体験している気になれませんね。
それでは次に下記を実行してみましょう。

```
$ redis-cli -p 7000 -c
127.0.0.1:7000> set fuga 'fuga'
-> Redirected to slot [9097] located at 127.0.0.1:7001
OK
127.0.0.1:7001>
```
fugaというキーで`fuga`という文字列をセットしました。
先ほどは出力されていなかった`Redirected to slot [9097] located at 127.0.0.1:7001`というメッセージが出力されています。
ここでは以下のことが順に実行されています。

- 7000番ポートのノードでset fuga 'fuga'が実行される
- redis clusterは fugaをアルゴリズムにかける
- 結果として9097がでる
- 9097番スロットは7000番ポートのノードでは対応していないので、redis clusterがクライアントの向き先を7000番ポートのノードから7001番ポートのノードに変える
- 値をセットする
- セットできたらクライアントに`OK`と返す

図で表すとこんな感じです。
![れでぃす くらすたーのしくみのかいせつ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318540/a22ad0ec-7555-e953-5c0b-74d096da689a.jpeg)


ちなみにredis-cliコマンドで-cオプションをつけないと7000番ポートでfugaは登録できません。

```
$ redis-cli -p 7000
127.0.0.1:7000> set fuga 'fuga'
(error) MOVED 9097 127.0.0.1:7001
127.0.0.1:7000>
```

# 終わりに
今回はredis clusterについて説明していきました。
実はredisの冗長構成は三種類あって、clusterはそのうちの一つにすぎません。
他にはReplicationやSentinelという構成もあるので、次はそれらを紹介できたらなーと思います！

# 索引
[Redis 5.0 を用いたクラスタ構築と検証 - shirobrak’s blog](https://shirobrak.hatenablog.com/entry/2018/10/28/205733)
[Redis Cluster構築 - Qiita](https://qiita.com/ono-soic/items/56661d468989c6036852)
[Redis](https://redis.io/)
[クラスター \| IT用語辞典 | 大塚商会](https://www.otsuka-shokai.co.jp/words/cluster.html)