【入門】Redis

# 背景

現在、自社サービスで Redis を利用していることもあり、あらためて勉強、まとめてみました。

今回は、Redis の概要・特徴・設定・レプリケーションの基礎的な部分を記載しています。

※間違いなどありましたら、ご指摘いただければ幸いです。

# Redis の概要

この章では Redis の概要を記載しています。


- Redis = REmote DIctionary Server の略です。
- Redis は、Key-Value型 の NoSQL データベースです。
- Redis は C 言語で書かれており、Open Source (BSD licensed) で開発されています。
- Redis は Client-Server モデル を採用した インメモリデータベースです。
   - すべてのデータをコンピュータのメインメモリ上に展開しながら動作します。
   - 他にも、インメモリDB として、 memcached が有名です。 
   - 対義語は ディスク型データベースです。（例: MySQL）
- 様々なデータ構造をサポートしています。

```
string
hash
list
set (要素の集合)
sorted set (ソート済みのset)
```

# Redis の特徴

この章では、Redis の特徴を列挙しています。

- 1 プロセス、1スレッドで動作します。
  - MySQLのように並列での処理はできないため、すべての動作が排他的になります。
- 複数のデータベースをIDで指定でき、各データベースは独立しています。 
- Redis はマルチマスタの運用ができません。
  - しかし、擬似的なマルチマスタ構成は可能で、Redis Cluster で実現可能です。(ver 3.0 以降)
  - 擬似的とは、実際にデータが各ノードに伝播しているわけではありません。
  - あるレコードをどのノードに保存するかを把握しておき、ノード間でリダイレクトすることによって、どのノードから接続しても指定するデータにたどり着ける仕組みとなっています。
- データの永続化 (RDB)
  - save コマンド (または設定ファイル) でスナップショットをディスクに保存することが可能です。
- データの永続化 (AOF: Append Only File)
  - サーバが受け付けた、書き込みコマンド の実行ログが格納されています。
  - イメージとしは、MySQL の バイナリログ のようなものです。
  - そのファイルから、ログをリプレイし、元のデータを再構成することが可能です。

#  Redis のインストールと動作確認

この章では ubuntu18 に Redis server をインストールしたときの手順を記載しています。

### Redis のインストールと起動確認

以下のコマンドで redis-server をインストールします。

```bash
$ sudo apt update
$ sudo apt install redis-server
$ redis-server --version
Redis server v=4.0.9 sha=00000000:0 malloc=jemalloc-3.6.0 bits=64 build=76095d16786fbcba
```

インストールが完了したら、 redis-server が起動していることを確認します。

```bash
$ sudo systemctl status redis-server.service
● redis-server.service - Advanced key-value store
   Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2019-02-24 06:39:38 UTC; 3min 48s ago
     Docs: http://redis.io/documentation,
           man:redis-server(1)
 Main PID: 17745 (redis-server)
    Tasks: 4 (limit: 2361)
   CGroup: /system.slice/redis-server.service
           └─17745 /usr/bin/redis-server 127.0.0.1:6379
```

### 動作確認

redis-server の起動が確認できたら、 redis-cli コマンドを利用して動作確認します。

```redis
$ redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set test "HELLO, World!!"
OK
127.0.0.1:6379> get test
"HELLO, World!!"
```

また、 INFO コマンドで現在の redis の状態や設定が確認ができます。

```bash
127.0.0.1:6379> INFO
# Server
redis_version:4.0.9
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:76095d16786fbcba
redis_mode:standalone
os:Linux 4.15.0-45-generic x86_64

(...snip...)

# Cluster
cluster_enabled:0

# Keyspace
db0:keys=1,expires=0,avg_ttl=0
```

#  Redis の基本的な設定

Redis の設定ファイルは、`/etc/redis/redis.conf` です。
設定ファイルを変更した際は、 redis-server を restart します。

この章では、Redis の代表的な設定を記載しています。

## 起動時の設定

### デーモン化

- daemonize はデーモンとして起動する設定になります。デフォルトでは yes です。
- daemonize を yes に設定した場合、 pidfile の path を指定する必要があります。

```
daemonize yes
pidfile /var/run/redis/redis-server.pid
```

### ポート

- Redis の デフォルトのポートは 6379 番です。
- bind は、特定の一つのインタフェースにバインドしたい場合に設定します。

```
port 6379
bind 127.0.0.1
```

netstat コマンドでプロセスを確認すると、6379 でLISTENしていることがわかります。

```bash
$ sudo netstat -alntp | grep redis
tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN      3634/redis-server 1
```

### ログレベルとログファイル

- loglevel で出力するログレベルを変更できます。設定は次の 4 種類です。
  - debug
  - verbose
  - notice
  - warning
- logfile で出力先のログファイルを指定できます。

```
loglevel notice
logfile /var/log/redis/redis-server.log
```

- 出力例

```
4565:C 21 Mar 10:06:12.771 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
4565:C 21 Mar 10:06:12.771 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=4565, just started
4565:C 21 Mar 10:06:12.771 # Configuration loaded
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 4.0.9 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 4578
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'
```

### タイムアウト

- 指定された時間を過ぎるとコネクションがcloseされます。
  - 単位は 秒 で 0 はdisableとなります。
  - 指定された時間とは、コネクションは張られているが、コマンドが送られていない時間です。

```
timeout 0
```

**注意点**

`timeout 0` とした場合、 redis-server に接続する client 側でコネクションを明示的に close してもらうことを想定しています。client 側でなんらかの異常があり close 処理がされないと、コネクションが残りつづけるので、注意が必要です。

### データベース

- database は Redis のデータベースの個数(番号)を指定します。
- 例えば、 `databases 16` を設定すると、16個のDBが利用可能になります。

```
databases 16
```

- デフォルトで Redis は 0番 のデータベースを利用します。
- `select <dbid>` で異なる DB を選択することもできます。

```bash
$ redis-cli
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> SELECT 15
OK
127.0.0.1:6379[15]> SELECT 16
(error) ERR DB index is out of range
```

※ [] の中がDBの番号。0は表示されません。

## 永続化の設定

ここでは、RDBとAOFの2種類の永続化の設定を記載しています。

###【RDB】 ディスクへの保存

- save でデータベースをディスクに保存するタイミングを設定することが可能です。
  - 文法は、 `save <seconds> <changes>` です。
- save を一つも設定しない場合は、保存されません。

```
save 900 1
save 300 10
save 60 10000
```

上記の設定の説明:

- もし最低1回、キーの変更が発生すると、900秒(15分)後 に保存します。
- もし最低10回、キーの変更が発生すると、300秒(5分)後 に保存します。
- もし最低10000回、キーの変更が発生すると、60秒(1分)後 に保存します。


### 【RDB】 保存先

- rdbcompression は rdb を圧縮保存する設定で、デフォルトは 圧縮 (yes) するようになっています。
- dbfilename で rdb のファイル名を指定できます。

```

rdbcompression yes
dbfilename dump.rdb
dir /var/lib/redis
```

上記の場合は、 dir 配下のディレクトリに dump.rdb というファイル名で保存されます。

### 【AOF】 ディスクへの保存

- appendonly で AOF の設定が可能です。デフォルトでは利用しない (no) 設定になっています。
- appdnfsync でディスクへ書き込みする際のモードを選択できます。
  - always: Redisに書き込みが発生するたびにfsync（ディスクとの同期）を行います。低速。
  - everysec: 毎秒fsyncを行います。中速。
  - no: fsync のタイミングをOSに任せます。高速。

```
appendonly no
appendfsync everysec
```

### 【AOF】 保存先

- appendfilename で aof のファイル名を指定できます。

```
appendfilename "appendonly.aof"
dir /var/lib/redis
```

上記の場合は、 dir 配下のディレクトリに appendonly.aof というファイル名で保存されます。


## リソース制限

### maxclients

- maxclients は同時に接続できるクライアント数の最大値を設定できます。デフォルトでは無制限です。

```
maxclients 10000
```

max clients に達した場合、 `ERR max number of clients reached` エラーとなります。

### maxmemory

- redis が使用できるメモリサイズの最大値です。

```bash
maxmemory 500MB
```

maxmemory に到達すると、maxmemory-policy の設定に従って key が削除されます。
ポリシーの詳細は [こちら](https://redis.io/topics/lru-cache) を参照してください。


## レプリケーションの設定

slave 側の redis.conf にのみ slaveof を設定します。

### slaveof

- 文法は `slaveof <masterip> <masterport>` です。

```
slaveof 192.168.1.10 6379
```

**注意点**

redis.conf はデフォルトで bind の設定が 127.0.0.1 となっています。
master と slaveで Redis を別サーバで構成する場合、デフォルトの設定では slave から master へ
接続することができず、レプリケーションに失敗します。
その場合は、 0.0.0.0 などに設定してください。


# 実践レプリケーション

ここでは、master/slave の2台構成で実際にレプリケーションをやってみたいと思います。
なお、OS, Redis のインストールは既に完了しているものとします。
また、各サーバのIPを以下のように設定しています。

```
master : 192.168.4.10
slave  : 192.168.4.20
```

### master 側の設定

master 側で　redis.conf に以下の設定をします。

```
bind 0.0.0.0
```

※redis.conf を編集した場合は、 redis を restart してください。

### slave 側の設定

slave 側で以下の設定をします。

```
bind 0.0.0.0
slaveof 192.168.1.10 6379
```
※redis.conf を編集した場合は、 redis を restart してください。

以下のコマンドで slave から master へ接続確認してみます。

```bash
$ redis-cli -h 192.168.4.10
192.168.4.10:6379>
```

slave 側の INFO を確認すると、

```bash
$ redis-cli info Replication
# Replication
role:slave
master_host:192.168.4.10
master_port:6379
master_link_status:up
master_last_io_seconds_ago:10
master_sync_in_progress:0
slave_repl_offset:182
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:d862cdb1414b23983c67b8bb1dcd9f171b13b3fd
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:182
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:182
```

role が slave で、master 側の IP (master_host) が見えています。
これで、レプリケーションは完了です。

### 動作確認

master 側で key-value をセットした後、

```bash
$ redis-cli
127.0.0.1:6379> set test "HELLO, World!!"
OK
127.0.0.1:6379> set test2 "This is test"
OK
```

slave 側に伝播しているか get で確認します。

```
$ redis-cli
127.0.0.1:6379> get test
"HELLO, World!!"
127.0.0.1:6379> get test2
"This is test"
```

うまくレプリケーションができていることが確認できました。


### 補足: master の info

レプリケーションの設定が完了後、 master 側の INFO を確認してみます。

```bash
$ redis-cli info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.4.20,port=6379,state=online,offset=308,lag=0
master_replid:16826abb8a01545df01331b733eb2bc135793d45
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:308
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:308
```

master 側からも slave が接続されている(slave0)ことが確認できました。
ちなみに、複数の slave を master に接続した場合、slave0, slave1, ... と増加します。


# Appendix:　key の有効期限

Redis は key に対して、有効期限 (EXPIRE) が設定でき、期限が到来すると削除されます。


> Normally Redis keys are created without an associated time to live. The key will simply live forever, unless it is removed by the user in an explicit way, for instance using the DEL command.
>
> The EXPIRE family of commands is able to associate an expire to a given key, at the cost of some additional memory used by the key. When a key has an expire set, Redis will make sure to remove the key when the specified amount of time elapsed.

[EXPIRE – Redis](https://redis.io/commands/expire)


```bash
127.0.0.1:6379> set test "hoge"     # test key に hoge を set
OK
127.0.0.1:6379> expire test 10     # expire を 10sec に指定
(integer) 1
127.0.0.1:6379> get test     # expire が到来していないので、 get可能。
"hoge"
127.0.0.1:6379> ttl test     # ttl コマンドで残りの秒数を確認可能
(integer) 4
127.0.0.1:6379> ttl test
(integer) 1
127.0.0.1:6379> ttl test     # expire で設定した時間が経過すると -2 がかえる。
(integer) -2
127.0.0.1:6379> get test     # 削除されたので、 nil がかえる。
(nil)
```


# 参考

以下のサイトを参考にさせていただきました。ありがとうございました。

[GitHub](https://github.com/antirez/redis)
[Introduction to Redis – Redis](https://redis.io/topics/introduction)
[設定ファイル — redis 2.0.3 documentation](http://redis.shibu.jp/admin/config.html)
[Redis - Wikipedia](https://ja.wikipedia.org/wiki/Redis)
[Redis勉強会資料(2015/06 update)](https://www.slideshare.net/yujiotani16/redis-26851700)
[Redis Cluster の構築と利用（Redis 3.0.0） \| ALBERT Official Blog](https://blog.albert2005.co.jp/2015/04/28/redis-cluster-%E3%81%AE%E6%A7%8B%E7%AF%89%E3%81%A8%E5%88%A9%E7%94%A8%EF%BC%88redis-3-0-0%EF%BC%89-2/)
[Redis の永続化 — Redis Documentation (Japanese Translation)](https://redis-documentasion-japanese.readthedocs.io/ja/latest/topics/persistence.html)
[redisをAutoScaling環境で使う場合はtimeoutの設定に注意 - まめ畑](https://d.conma.me/entry/2013/03/21/114044)