Redisの全データ型に触れてみた。キャッシュだけじゃない Redis 再入門。

Redis に触れたことがある方は多いと思いますが、キャッシュとして使うケースが多いのではないでしょうか？
僕も「スケーリングもできるキャッシュ」くらいな理解でしたが、ランキングなんかにも使えるらしいと聞いており、再入門してみました。

## 環境構築、テストデータ

公式の Docker を使います。
https://hub.docker.com/_/redis

ドキュメントどおりに以下で起動します。

```
docker run --name some-redis -d redis redis-server --appendonly yes
```

redis-cli を使えるようにしておきます。

```
brew install redis
```

## データを出し入れする

今回は基本に忠実に、redis-cli で Redis のサポートするデータ構造に触れてみます。

redis-cli を起動します。

```
redis-cli
```

Redis にはいくつかのデータ構造がありますので、それぞれ見ていきます。

### 文字列

シンプルに key, value を保持します。一番使われる用途はこれでしょう。
セッション ID をキーにユーザ情報を保持したり。

```
127.0.0.1:6379> SET user_id1 kaiba
OK
127.0.0.1:6379> get user_id1
"kaiba"
127.0.0.1:6379> SET user_id1 kaibadash
OK
127.0.0.1:6379> get user_id1
"kaibadash"
127.0.0.1:6379> SET user_id2 taro
OK
127.0.0.1:6379> SET user_id3 jiro
OK
```

```
127.0.0.1:6379> MGET user_id1 user_id2 user_id3
1) "kaibadash"
2) "taro"
3) "jiro"
```

一気に取得することもできるんですね。

### リスト

リストです。先頭、末尾に対して追加、削除できます。

```
127.0.0.1:6379> RPUSH members kaiba
(integer) 1
127.0.0.1:6379> RPUSH members taro
(integer) 2
127.0.0.1:6379> RPUSH members jiro
(integer) 3
127.0.0.1:6379> LLEN members
(integer) 3
127.0.0.1:6379> LPOP members
"kaiba"
127.0.0.1:6379> RPOP members
"jiro"
```

キュー構造を表現したりするのに良さそうですね。
Sidekiq とか Redis を使いますが、きっとこれを使っていたんですね。

### セット

順番がなく、重複を許さないデータ構造です。

```
127.0.0.1:6379> SADD users kaiba
(integer) 1
127.0.0.1:6379> SADD users taro
(integer) 1
127.0.0.1:6379> SADD users jiro
(integer) 1
127.0.0.1:6379> SADD users kaiba
(integer) 0 # すでにいるので追加されなかった
```

```
127.0.0.1:6379> SREM users kaiba
(integer) 1
127.0.0.1:6379> SREM users NONE
(integer) 0
127.0.0.1:6379> SREM users jiro
(integer) 1
```

ビンゴの当選者の管理のために、ダブルビンゴの人はカウントしない、とか良いかもしれませんね。
知らないと他のデータ構造で頑張ってしまうことも多そうですね。

### ソート済みセット

スコア値に従ってソートされるセットです。

```
127.0.0.1:6379> ZADD ranking 80 kaiba
(integer) 1
127.0.0.1:6379> ZADD ranking 100 dekisugi
(integer) 1
127.0.0.1:6379> ZADD ranking 10 nobita
(integer) 1
```

```
127.0.0.1:6379> ZRANK ranking nobita
(integer) 0
127.0.0.1:6379> ZRANK ranking kaiba
(integer) 1
127.0.0.1:6379> ZRANK ranking dekisugi
(integer) 2
```

スコアの昇順のようですね。
スコアなので感覚的には降順にしてほしい気がしますが…

```
127.0.0.1:6379> ZREVRANK ranking dekisugi
(integer) 0
127.0.0.1:6379> ZREVRANK ranking nobita
(integer) 2
```

ちゃんと昇順でとることもできます。

```
127.0.0.1:6379> ZRANGE ranking 0 2
1) "nobita"
2) "kaiba"
3) "dekisugi"
127.0.0.1:6379> ZREVRANGE ranking 0 2
1) "dekisugi"
2) "kaiba"
3) "nobita"
```

 リストで取得することもできます。
ランキングの完成じゃないか…！

### ハッシュ

Key Value 形式のデータ構造です。
はて、そもそも Redis は Key Value 形式のデータストアでは？と混乱しますが、
例えば以下のようなデータを保持できます。

```json
{
  "id": 1,
  "name": "kaiba",
  "tweet": "Hello!"
}
```

```
127.0.0.1:6379> HSET tweet1 name kaiba
(integer) 1
127.0.0.1:6379> HSET tweet1 id 1
(integer) 1
127.0.0.1:6379> HSET tweet1 tweet Hello!
(integer) 1
127.0.0.1:6379> HGET tweet1 tweet
"Hello!"
127.0.0.1:6379> HGET tweet1 name
"kaiba"
```

今までこれをしたい場合は json を文字列にして入れることが多かったのですが、文字列ではなく Key Value のオブジェクトとして挿入できるのは気持ちがいいですね。

## 向いていること

Redis は以下に向いていると感じました。

- シンプルな KVS としてキャッシュに
- SQL だとどうしても重たくなりがちなランキングに
- キュー構造を表現したい

## 向いていないこと

- Value 間に関係を持たせる
- 不正なデータが入らないようにする(制約)
- Value で検索したい

## まとめ

僕はキャッシュとしてしか使ったことがありませんでしたが、いくつかのデータ構造を表現できることを知りました。
特にランキングはシンプルながら強力だと感じました。
シンプルでとっつきやすいのも良いですね。