Redisのデータ永続性についての検証

# Redisについて

redisはインメモリーのKVSで高速なデータ参照が可能というのは、いろんなサイトで紹介させている。
インメモリー？ ということは、起動中はメモリー上のみで完結？　停止時にデータはどうなるか？
なんとなくわかるが、知りたいことについて確実な解説にピンポイントで到達しないので、個人的に検証してみる。

## Redisのセットアップ

CentOS7系のサーバーにitamaeのレシピを使用してセットアップする

```shell-session
cd ~/itamae_cookbooks
mkdir redis
cd redis
```
```ruby:redis.rb
package "redis" do
    action :install
end
```
```shell-session
# 事前確認
itamae local redis.rb --dry-run

# itamae実行
itamae local redis.rb

# 起動
systemctl start redis.service
```

## 検証

### redis-cliでデータを追加後、redisサービスを正常終了->正常起動させ、データを確認する

データ追加

```shell-session
# cli起動
redis-cli 

# データ追加
127.0.0.1:6379> SET key1 val1
#=> OK

# データ確認
127.0.0.1:6379> GET key1
#=> "val1"

# データ確認2
127.0.0.1:6379> GET key2
#=> (nil)

# データ確認3
127.0.0.1:6379> GET key3
#=> (nil)

```

redis正常終了->正常起動

```shell-session
# 停止
systemctl stop redis.service

# 確認
ps ax|grep redis
#=> 14223 pts/0    S+     0:00 grep --color=auto redis

# 再びredis起動
systemctl start redis.service

# 確認
ps ax|grep redis
#=> 14232 ?        Ssl    0:00 /usr/bin/redis-server *:6379
#=> 14236 pts/0    S+     0:00 grep --color=auto redis

```

redis上でデータの確認

```shell-session
# cli起動
redis-cli

# データ確認
127.0.0.1:6379> GET key1
#=> "val1"

# データ確認2
127.0.0.1:6379> GET key2
#=> (nil)

# データ確認3
127.0.0.1:6379> GET key3
#=> (nil)

```

永続化された

### redis-cliでデータを追加後、killでredisプロセスを強制終了->正常起動させ、データを確認する

データ追加

```shell-session
# cli起動
redis-cli 

# データ追加2
127.0.0.1:6379> SET key2 val2
#=> OK

# データ確認
127.0.0.1:6379> GET key1
#=> "val1"

# データ確認2
127.0.0.1:6379> GET key2
#=> "val2"

# データ確認3
127.0.0.1:6379> GET key3
#=> (nil)

```

redis強制終了->正常起動

```shell-session
# 強制終了
ps ax|grep redis|grep -v grep|awk '{print $1}'|xargs kill -9

# 確認
ps ax|grep redis
#=> 14280 pts/0    S+     0:00 grep --color=auto redis

# 再びredis起動
systemctl start redis.service

# 確認
ps ax|grep redis
#=> 14287 ?        Ssl    0:00 /usr/bin/redis-server *:6379
#=> 14291 pts/0    S+     0:00 grep --color=auto redis

```

redis上でデータの確認

```shell-session
# cli起動
redis-cli

# データ確認
127.0.0.1:6379> GET key1
#=> "val1"

# データ確認2
127.0.0.1:6379> GET key2
#=> (nil)

# データ確認3
127.0.0.1:6379> GET key3
#=> (nil)

```

消失した

### redis-cliでデータを追加、SAVEコマンドを実行、killでredisプロセスを強制終了->正常起動させ、データを確認する

データ追加

```shell-session
# cli起動
redis-cli 

# データ確認
127.0.0.1:6379> GET key1
#=> "val1"

# データ確認2
127.0.0.1:6379> GET key2
#=> (nil)

# データ確認3
127.0.0.1:6379> GET key3
#=> (nil)

# データ追加3
127.0.0.1:6379> SET key3 val3
#=> OK

# SAVE実行
127.0.0.1:6379> SAVE
#=> OK

# データ追加2
127.0.0.1:6379> SET key2 val2
#=> OK

# データ確認
127.0.0.1:6379> GET key1
#=> "val1"

# データ確認2
127.0.0.1:6379> GET key2
#=> "val2"

# データ確認3
127.0.0.1:6379> GET key3
#=> "val3"

```

redis強制終了->正常起動

```shell-session
# 強制終了
ps ax|grep redis|grep -v grep|awk '{print $1}'|xargs kill -9

# 確認
ps ax|grep redis
#=> 14309 pts/0    S+     0:00 grep --color=auto redis

# 再びredis起動
systemctl start redis.service

# 確認
ps ax|grep redis
#=> 14316 ?        Ssl    0:00 /usr/bin/redis-server *:6379
#=> 14320 pts/0    S+     0:00 grep --color=auto redis

```

redis上でデータの確認

```shell-session
# cli起動
redis-cli

# データ確認
127.0.0.1:6379> GET key1
#=> "val1"

# データ確認2
127.0.0.1:6379> GET key2
#=> (nil)

# データ確認3
127.0.0.1:6379> GET key3
#=> "val3"

```

永続化と、消失が発生した

### まとめ

例えば、RDSのoracleを例にとると、データ領域をリアルタイム更新するのではなく、REDOログやUNDOバッファー等で更新データを集積して、遅延書き込みを採用している。そのため、プロセスが異常停止した際などの復旧時には、ロールフォワード(データ領域に未記録のデータをREDOログより反映させる)やロールバック(未コミットまで反映させた分をコミット済みまでに戻す)が発生してデータの一貫性をまもるようになっている。

一方、Redisの標準設定では、基本的にインメモリーで動作する。つまり、プロセス自体に不具合が生じると、基本的にはデータは消失する。但し、正常終了やSAVEコマンドや自動SAVEのタイミングで、(RDSの遅延書き込みのように)Diskにsnapshotファイルとして書き出させることができ、再起動時にsnapshotからデータを読み込むため、データは永続化されると言える。

#### 追加

redis.confで永続性設定が変更できる
saveという項目で、自動Disk書き込み設定が変更可能。

```text:redis.conf
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving completely by commenting out all "save" lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save ""

save 900 1
save 300 10
save 60 10000

```
また、appendonlyという項目がある。
これは、appendonly.aofという(REDOログファイルのような)書き出し専用ファイルに、更新処理を記録させることで、snapshotに未記録な更新処理があっても、再起動時に再構成して、データの消失を防ぐようになっている。
(appendonlyといってもDB上のデータは更新できるので問題ない)

```text:redis.conf

############################## APPEND ONLY MODE ###############################

# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.

appendonly no

# The name of the append only file (default: "appendonly.aof")

appendfilename "appendonly.aof"
```


さらにappendfsyncという項目で、appendonly.aofへの書き込みタイミングの設定が可能。

```text:redis.conf

# The fsync() call tells the Operating System to actually write data on disk
# instead of waiting for more data in the output buffer. Some OS will really flush
# data on disk, some other OS will just try to do it ASAP.
#
# Redis supports three different modes:
#
# no: don't fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log. Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is "everysec", as that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that's snapshotting),
# or on the contrary, use "always" that's very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use "everysec".

# appendfsync always
appendfsync everysec
# appendfsync no

```

またaofファイルが無限に肥大化しないように自動書き換えの設定もある。

```text:redis.conf

# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

```