私ならわかるRedis

## はじめに
勉強がてらまとめたものです

※ 私ならわかります

## Redisの概要
- インメモリデータベース
    - ディスクではなく高速なメインメモリにデータを格納
    - 高速かつ安定したパフォーマンスを提供
- 大容量のデータに不向き
- 部分的なデータ保存に向いている
- 定期的にディスクに書き込みデータを永続化する
- 別名データ構造サーバ
    - キーに紐づく値の一般的な関係はもちろん複雑なデータ構造も扱える

## 専門用語
- KVS
    - Key, Value Storeの頭文字
    - キーに紐づく値のこと
    - 例）Name(key): "Taro"(value)
    - 長いキーは推奨されていない
- データ・タイプ
    - String：シンプルなデータ
        - 例）Name: "Taro"
    - List：順番に並べた複数の要素
        - 例）時系列的なデータ
    - Set：順不同の複数の要素（重複を許さない）
        - 例）タグ，ソーシャルグラフ
    - Sorted Set：Setの特徴を持ちつつも個々の要素にスコアが付く
        - 例）ランキング
    - Hashe：連想配列
        - 例）わかりやすいラベルと値のセット

## 環境構築
### ローカルで利用

#### Mac

```bash
# Mac
$ brew install redis
```

#### Ubuntu
```bash
# Ubuntu
$ sudo apt install redis
```

#### 起動
```bash
# サーバーのスタート
$ redis-server

# 別ウインドウで下記コマンド入力によってサーバに入れる
$ redis-cli
```

#### 停止

```bash
# cliから抜ける
127.0.0.1:6379> exit

# サーバの停止
redis-serverは
Ctrl+Cで停止することができる
```

### Dockerで利用(推奨)

```yml:docker-compose.yml
version: '3'
services:
  redis:
    image: "redis:latest"
    ports:
      - "6379:6379"
    volumes:
      - "./data/redis:/data"
```

#### 起動

```bash
# コンテナの立ち上げ
$ docker-compose up -d

# 立ち上上げたredisコンテナに入ってcliを叩く
$ docker-compose exec redis redis-cli
```

#### 停止

```bash
# cliから抜ける
127.0.0.1:6379> exit

# コンテナの停止
$ docker-compose down
```

## データベースの操作

### データベースの選択
デフォルトで0番のデータベースが選択されている

```bash
127.0.0.1:6379> select 数字
```

0から15まで選択できる

### データの保存

```bash
127.0.0.1:6379> bgsave
```

コマンド実行後，ローカルに**dump.rdb**が作られる
先程保存したデータはこのファイルに書き出される


### Stringの操作

#### データの挿入
- 形式
`set key value`

- 例

```bash
127.0.0.1:6379> set name taro
OK
127.0.0.1:6379> set email taro@gmail.com
OK
127.0.0.1:6379> set age 20
OK
```


#### キーに紐づく値の表示
- 形式
単数：`get キー名`
複数：`mget キー名1 キー名2`

- 例

```bash
# 1つのみ表示
127.0.0.1:6379> get name
"taro"

# 複数の値を表示する場合はmgetを使ってkeyを羅列する
127.0.0.1:6379> mget name email age
1) "taro"
2) "taro@gmail.com"
3) "20"
```

#### 整数値の減少と増加の操作
値が整数値であった場合，インクリメントまたは，デクリメントすることができます．

- 形式
増加：`incr キー名`
減少：`decr キー名`

- 例

```bash
# 値の表示
127.0.0.1:6379> get age
"20"

# インクリメント
127.0.0.1:6379> incr age
(integer) 21

# デクリメント
127.0.0.1:6379> decr age
(integer) 20
```

増減を1以上に指定することもできます

- 形式
増加数の指定：`incrby 数字`
減少数の指定：`decrby 数字`

- 例

```bash
# 値の表示
127.0.0.1:6379> get age
"20"

# 増加数の指定
127.0.0.1:6379> incrby age 10
(integer) 30

# 減少数の指定
127.0.0.1:6379> decrby age 10
(integer) 20
```

### KEYの操作

#### keyの表示

- 形式
すべてのkeyの取得：`keys *`
条件指定：`keys 正規表現`

- 使える正規表現
`?` ・・・任意の1文字
`*` ・・・ 任意の文字列
`[ ]` ・・・ 角カッコ内の文字のどれか1文字

- 例

```bash
# すべてのキーを取得
127.0.0.1:6379> keys *
1) "age"
2) "email"
3) "name"

# 条件指定，先頭がaまたはnのキーすべて
127.0.0.1:6379> keys [an]*
1) "age"
2) "name"
```

#### keyの存在確認

- 形式
`exists キー`

- 例

```bash
# キーnameは存在するため1が返ってくる
127.0.0.1:6379> exists name
(integer) 1
# キーimageは存在しないため0が返ってくる
127.0.0.1:6379> exists image
(integer) 0
```

#### keyの名前を変更

- 形式
`rename 元のキー名 新しいキー名`

- 例

```bash
# ageからnenraiにキー名を変更
127.0.0.1:6379> rename age nenrai
OK
# 変更されたので元の名前では検索がマッチしません
127.0.0.1:6379> keys age
(empty array)
# 新しいキー名に変わっていることがわかります
127.0.0.1:6379> keys nenrai
1) "nenrai"
```

#### keyの削除
- 形式
`del キー名`

- 例

```bash
127.0.0.1:6379> del nenrai
(integer) 1
127.0.0.1:6379> keys nenrai
(empty array)
```

#### 一時的なキーの作成
- 形式
`expire キー名 秒数`

- 例

```bash
# ageに20をセット
127.0.0.1:6379> set age 20
OK
# セットされていることの確認
127.0.0.1:6379> get age
"20"
# 有効期限を10秒にセット
127.0.0.1:6379> expire age 10
(integer) 1
# 5秒後，まだ値はある
127.0.0.1:6379> get age
"20"
# 10秒後値がキーが消えていることがわかる
127.0.0.1:6379> get age
(nil)
```

#### ソート
- 形式

数字のソート：`sort キー名 [desc asc]`
文字列のソート：`sort キー名 alpha [desc asc]`

後述するSetやListにも対応

- 例

```bash
127.0.0.1:6379> sort students alpha asc
1) "jiro"
2) "saburo"
3) "shiro"
4) "taro"
```


### Listの操作
#### リストにデータを追加

- 形式
先頭に要素を追加：`lpush キー名 値1 値2 値3．．．`
末尾に要素を追加：`rpush キー名 値1 値2 値3．．．`

- 例

```bash
127.0.0.1:6379> rpush students taro
(integer) 1
# 複数同時に追加することも可能
127.0.0.1:6379> rpush students jiro saburolpop
(integer) 3
```

#### リストの表示

- 形式
指定した要素の表示：`lindex キー名 要素番号`
指定した範囲の要素を表示`lrange キー名 start stop`

- 例

```bash
# 指定した要素の表示
127.0.0.1:6379> lindex students 1
"jiro"

# 先頭の0から2までの格納された値を表示
127.0.0.1:6379> lrange students 0 2
1) "taro"
2) "jiro"
3) "saburo"

# -1は末尾を表している
127.0.0.1:6379> lrange students 0 -1
1) "taro"
2) "jiro"
3) "saburo"
```

#### 要素数の表示
- 形式

`llen キー名`

```bash
127.0.0.1:6379> llen students
(integer) 4
```

#### リストのデータを削除 
- 形式
先頭の要素削除：`lpop キー名`
末尾の要素削除：`rpop キー名`
指定した範囲以外削除：`ltrim キー名 start stop`
- 例

```bash
127.0.0.1:6379> lrange students 0 -1
1) "taro"
2) "jiro"
3) "saburo"

# 先頭の要素を削除
127.0.0.1:6379> lpop students
"taro"
127.0.0.1:6379> lrange students 0 -1
1) "jiro"
2) "saburo"

# 末尾の要素を削除
127.0.0.1:6379> rpop students
"saburo"
127.0.0.1:6379> lrange students 0 -1
1) "jiro"

# 指定した範囲以外を削除
127.0.0.1:6379> ltrim students 0 2
OK
127.0.0.1:6379> lrange students 0 -1
1) "taro"
2) "jiro"
3) "saburo"
```
### Setの操作

#### データの追加
- 形式
 
`sadd キー名 値1 値2 値3．．．`

- 例

```bash
# 値の追加
127.0.0.1:6379> sadd classroom taro
(integer) 1

# 複数の値をセットすることができる
127.0.0.1:6379> sadd classroom jiro saburo
(integer) 2
```
#### データ一覧の表示
- 形式

`smembers キー名`

- 例

```bash
# Listと違い順不同で追加されていることがわかる
127.0.0.1:6379> smembers classroom
1) "jiro"
2) "saburo"
3) "taro"
```

#### データの削除
- 形式
`srem キー名 値`

- 例

```bash
# jiroを削除
127.0.0.1:6379> srem classroom jiro
(integer) 1

# 消えていることがわかる
127.0.0.1:6379> smembers classroom
1) "saburo"
2) "taro"
```

#### 集合

- 形式
和集合：`sunion キー名1 キー名2`
差集合：`sdiff キー名1 キー名2`
積集合：`sinter キー名1 キー名2`
和集合のSetを作成：`sunionstore キー名 比較する集合1 比較する集合2`
差集合のSetを作成：`sdiffstore キー名 比較する集合1 比較する集合2`
積集合のSetを作成：`sinterstore キー名 比較する集合1 比較する集合2`

- 例

```bash
# 2つのSetを作成
127.0.0.1:6379> smembers classroom
1) "saburo"
2) "taro"
127.0.0.1:6379> sadd classroom2 taro jiro shiro
(integer) 3
127.0.0.1:6379> smembers classroom2
1) "jiro"
2) "taro"
3) "shiro"

# 和集合
127.0.0.1:6379> sunion classroom classroom2
1) "jiro"
2) "taro"
3) "saburo"
4) "shiro"

# 差集合
127.0.0.1:6379> sdiff classroom classroom2
1) "saburo"

# 積集合
127.0.0.1:6379> sinter classroom classroom2
1) "taro"

# 和集合のSetを作成
127.0.0.1:6379> sunionstore unionclassroom classroom classroom2
(integer) 4
127.0.0.1:6379> smembers unionclassroom
1) "jiro"
2) "taro"
3) "saburo"
4) "shiro"
```

### Sorted Setの操作
ランキングデータでよく使われる

#### データの追加
- 形式
`zadd キー名 ランク 値`
ソート時ランクが比較される

- 例

```bash
# 年齢をランクとしてセットしている
127.0.0.1:6379> zadd age 80 taro
(integer) 1
127.0.0.1:6379> zadd age 63 jiro
(integer) 1
127.0.0.1:6379> zadd age 49 saburo
(integer) 1
127.0.0.1:6379> zadd age 23 shiro
(integer) 1
```

#### データの表示
- 形式

昇順に表示：`zrange キー名 start stop`
降順に表示：`zrevrange キー名 start stop`


```bash
# 先頭から末尾まで昇順に表示
127.0.0.1:6379> zrange age 0 -1
1) "shiro"
2) "saburo"
3) "jiro"
4) "taro"

# 範囲は自由に指定できる
127.0.0.1:6379> zrange age 1 2
1) "saburo"
2) "jiro"

# 先頭から末尾まで降順に表示
127.0.0.1:6379> zrevrange age 0 -1
1) "taro"
2) "jiro"
3) "saburo"
4) "shiro"
```

#### ランクの表示
- 形式

昇順時のランク：`zrank キー名 値`
降順のランク：`zrevrank キー名 値`

- 例

```bash
# 昇順時のランク
127.0.0.1:6379> zrank age taro
(integer) 3

# 降順時のランク
127.0.0.1:6379> zrevrank age taro
(integer) 0
```

#### 削除
- 形式

`zrem キー名 値`

- 例

```bash
# shiroを削除
127.0.0.1:6379> zrem age shiro
(integer) 1

# shiroがいなくなっている事がわかる
127.0.0.1:6379> zrange age 0 -1
1) "saburo"
2) "jiro"
3) "taro"
```

### Hashの操作
Pythonで言う辞書
Rubyで言うHash
Bashで言う連想配列

#### hashの追加と更新
- 形式

追加：`hset hash名 キー名 値`
更新：`hset hash名 キー名 更新する値`

- 例

```bash
# 追加
127.0.0.1:6379> hset teammember name taro
(integer) 1
127.0.0.1:6379> hset teammember age 20
(integer) 1
127.0.0.1:6379> hset teammember sex man
(integer) 1

# ageを20から25に更新
127.0.0.1:6379> hset teammember age 25
(integer) 0
```
#### 値の表示
- 形式

１つのキーを指定して表示：`hget hash名 キー名`
複数のキーを指定して表示：`hmget hash名 キー名1 キー名2 キー名3`
すべての値を表示：`hvals hash名`

```bash
# １つだけ表示
127.0.0.1:6379> hget teammember name
"taro"

# 複数表示
127.0.0.1:6379> hmget teammember name age sex
1) "taro"
2) "20"
3) "man"

# すべて表示
127.0.0.1:6379> hvals teammember
1) "taro"
2) "20"
3) "man"
```
#### キーがいくつあるか表示
- 形式
`hlen hash名`


- 例

```bash
# teammemberは3つのキーで構成されている事がわかる
127.0.0.1:6379> hlen teammember
(integer) 3
```

#### キーの一覧表示
- 形式

`hkeys hash名`

- 例

```
# teammemberを構成するキー一覧を表示
127.0.0.1:6379> hkeys teammember
1) "name"
2) "age"
3) "sex"
```

#### キーと値を同時に表示
- 形式
`hgetall hash名`

- 例

```bash
# 1~3はキー名，4~6は対応する値
127.0.0.1:6379> hgetall teammember
1) "name"
2) "taro"
3) "age"
4) "20"
5) "sex"
6) "man"
```


#### キーを指定して削除
- 形式

`hdel hash名 キー名`

- 例

```bash
# 性別の削除
127.0.0.1:6379> hdel teammember sex
(integer) 1

# 削除されていることがわかる
127.0.0.1:6379> hgetall teammember
1) "name"
2) "taro"
3) "age"
4) "25"
```

#### hashの削除
- 形式

`del hash名`

- 例

```bash
127.0.0.1:6379> del teammember
(integer) 1
127.0.0.1:6379> hgetall teammember
(empty array)
```

## データの排他的処理
複数の処理を同時に行う
処理の間は他のアクセスを許さない

- 注意

ロールバック出来ないことに注意
データの整合性が取れない場合がある


- 形式

処理開始：`multi`

処理の実行：`exec`

処理の強制終了：`discard`

- 例

```bash
# 処理の開始
127.0.0.1:6379> multi
OK

# 処理を一つ一つキューに入れるイメージ
# 処理1
127.0.0.1:6379> incr score
QUEUED

#処理2
127.0.0.1:6379> incr pass
QUEUED

# 処理の実行
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1
```