概要
Redisの冗長構成は
の3種類がありますが、そのうちのReplicationについて説明します。
環境
- Redis 5.0.7
Replication
Replicationはmasterからデータをreplicaにコピーし、read系コマンドをreplicaから行うことでスケーラビリティを向上させる冗長構成です。
システム図
Replicationのシステム図は以下です。
5.0からslaveという言葉はやめてreplicaという呼び方に変わっているので注意してください。
- The slave name was removed from logs and documentation, now replica is used instead.
ref: https://raw.githubusercontent.com/antirez/redis/5.0/00-RELEASENOTES
また多段(multi level)でもreplicationを組むことが可能です。
これはmasterに多くのreplicaが紐づくと、トラフィックがmasterに集中してしまいよくないため、replicaでも伝播できるようにするという仕組みです。
検証
実際にローカルで構築して動作検証してみます。
こんな感じの構成にします。
redis.conf
redis.conf
は公式サイトの以下のページから各バージョンごとに取得できます。
今回は↑を少しいじって利用します。
# Redis configuration file example. # ################################## NETWORK ##################################### # bind 127.0.0.1 protected-mode no port 6379 tcp-backlog 511 timeout 0 tcp-keepalive 300 ################################# GENERAL ##################################### daemonize no supervised no pidfile /var/run/redis_6379.pid loglevel notice logfile "" databases 16 always-show-logo yes ################################ SNAPSHOTTING ################################ save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir ./ ################################# REPLICATION ################################# replica-serve-stale-data yes replica-read-only yes # repl-diskless-sync yes # repl-diskless-sync-delay 5 repl-disable-tcp-nodelay no replica-priority 100 ################################### CLIENTS #################################### maxclients 10000 ############################## MEMORY MANAGEMENT ################################ maxmemory 128mb maxmemory-policy noeviction ############################## APPEND ONLY MODE ############################### appendonly yes appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes aof-use-rdb-preamble yes ################################## SLOW LOG ################################### slowlog-log-slower-than 10000 slowlog-max-len 128 ############################### ADVANCED CONFIG ############################### hash-max-ziplist-entries 512 hash-max-ziplist-value 64 list-max-ziplist-size -2 list-compress-depth 0 set-max-intset-entries 512 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 hll-sparse-max-bytes 3000 stream-node-max-bytes 4096 stream-node-max-entries 100 activerehashing yes client-output-buffer-limit normal 0 0 0 client-output-buffer-limit replica 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60
色々設定がありますが、デフォルトと違う点は以下です。
bind
をコメントアウトしてどこからでもアクセス化protected-mode no
にしてbind設定を無効化可能にappendonly yes
にして永続化部分をmixed RDB+AOFに。mixedは4.0から使用可能です。maxmemory
を128mb
に
docker-compose.yaml
docker-composeの設定です。
version: '3' services: master: &base image: redis:alpine ports: - 6379:6379 volumes: - $PWD/redis.conf:/usr/local/etc/redis/redis.conf command: redis-server /usr/local/etc/redis/redis.conf replica_1: <<: *base ports: - 6380:6379 command: redis-server /usr/local/etc/redis/redis.conf --replicaof master 6379 replica_2: <<: *base ports: - 6381:6379 command: redis-server /usr/local/etc/redis/redis.conf --replicaof master 6379 replica_3: <<: *base ports: - 6382:6379 command: redis-server /usr/local/etc/redis/redis.conf --replicaof replica_1 6379
ポイントは以下です。
redis.conf
をマウントして指定--slaveof
でなく--replicaof
でmasterを指定- 多段レプリケーションのため
replica_3
はreplica_1
をmasterとして指定
動作検証
レプリケーションの確認
master
データを書き込みます。
127.0.0.1:6379> set foo 1 OK 127.0.0.1:6379> set bar 2 OK
replica_1
masterに紐付いていることが確認できます。
$ redis-cli -p 6380 127.0.0.1:6380> info replication # Replication role:slave master_host:master master_port:6379 master_link_status:up master_last_io_seconds_ago:1 master_sync_in_progress:0 slave_repl_offset:585 slave_priority:100 slave_read_only:1 connected_slaves:1 slave0:ip=172.22.0.5,port=6379,state=online,offset=571,lag=1 master_replid:a584a10254d68bd8467a923da89d59d950ead3d2 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:585 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:585
masterで書いたデータが読み込めます。
127.0.0.1:6380> get foo "1" 127.0.0.1:6380> get bar "2"
しかしreplicaなので書き込みはできません。
127.0.0.1:6380> set buz 3 (error) READONLY You can't write against a read only replica.
replica_2
こちらも同様
127.0.0.1:6381> info replication # Replication role:slave master_host:master master_port:6379 master_link_status:up master_last_io_seconds_ago:8 master_sync_in_progress:0 slave_repl_offset:658 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:cac819ca2d30046c83cbd82f1a37ef3628a70135 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:658 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:658
replica_3
replica_3
はreplica_1
を親としています。
$ redis-cli -p 6382 127.0.0.1:6382> info replication # Replication role:slave master_host:replica_1 // ←replica_1になってる master_port:6379 master_link_status:up master_last_io_seconds_ago:2 master_sync_in_progress:0 slave_repl_offset:697 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:a584a10254d68bd8467a923da89d59d950ead3d2 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:697 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:15 repl_backlog_histlen:683
masterダウン時の可用性
masterがダウンしてもreplicaからread系コマンドは実行できます。
$ docker stop replication_master_1
replication_master_1
ログも以下のように出ます。
master_1 | 1:signal-handler (1583749670) Received SIGTERM scheduling shutdown... master_1 | 1:M 09 Mar 2020 10:27:51.037 # User requested shutdown... master_1 | 1:M 09 Mar 2020 10:27:51.037 * Calling fsync() on the AOF file. master_1 | 1:M 09 Mar 2020 10:27:51.037 * Saving the final RDB snapshot before exiting. master_1 | 1:M 09 Mar 2020 10:27:51.040 * DB saved on disk master_1 | 1:M 09 Mar 2020 10:27:51.040 * Removing the pid file. master_1 | 1:M 09 Mar 2020 10:27:51.040 # Redis is now ready to exit, bye bye... replica_1_1 | 1:S 09 Mar 2020 10:27:51.059 # Connection with master lost. replica_1_1 | 1:S 09 Mar 2020 10:27:51.059 * Caching the disconnected master state. replica_2_1 | 1:S 09 Mar 2020 10:27:51.059 # Connection with master lost. replica_2_1 | 1:S 09 Mar 2020 10:27:51.059 * Caching the disconnected master state. replication_master_1 exited with code 0 replica_1_1 | 1:S 09 Mar 2020 10:27:51.946 * Connecting to MASTER master:6379 replica_2_1 | 1:S 09 Mar 2020 10:27:51.950 * Connecting to MASTER master:6379 replica_2_1 | 1:S 09 Mar 2020 10:27:51.978 # Unable to connect to MASTER: Resource temporarily unavailable replica_1_1 | 1:S 09 Mar 2020 10:27:51.978 # Unable to connect to MASTER: Resource temporarily unavailable
replicaでコマンドを実行してみます。
replication $ redis-cli -p 6380 127.0.0.1:6380> get foo "1"
取得できました。
replicaの昇格
一般にreplicationはreplica(slave)は昇格できないと言われています。
しかしこれは自動的に昇格しないだけであって、replicaであることをやめればmasterとして変わることが可能です。
現在はslaveになっていますが、
$ redis-cli -p 6380 127.0.0.1:6380> info replication # Replication role:slave master_host:master master_port:6379 master_link_status:up master_last_io_seconds_ago:7 master_sync_in_progress:0 slave_repl_offset:125 slave_priority:100 slave_read_only:1 connected_slaves:1 slave0:ip=172.21.0.4,port=6379,state=online,offset=125,lag=0 master_replid:907ebbc8e196a8489b8d696caa1b28837ca0f326 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:125 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:125
replicaof no one
とするとreplicaであることを止めます。
127.0.0.1:6380> replicaof no one OK
以下のようにrole:master
になっていることが確認できます。
127.0.0.1:6380> info replication # Replication role:master connected_slaves:1 slave0:ip=172.21.0.4,port=6379,state=online,offset=290,lag=0 master_replid:2ee1d02a91695469f5b59708dd3129d0f51cbadc master_replid2:907ebbc8e196a8489b8d696caa1b28837ca0f326 master_repl_offset:290 second_repl_offset:140 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:290
書き込みもでき、
127.0.0.1:6380> set piyo 3 OK 127.0.0.1:6380> keys * 1) "piyo" 2) "fuga" 3) "hoge"
元々のmasterには影響せず、
127.0.0.1:6379> keys * 1) "fuga" 2) "hoge"
自分の下にいるreplicaにはきちんと伝播されます。
127.0.0.1:6382> keys * 1) "fuga" 2) "hoge" 3) "piyo"
Full resyncの挙動
Replicationではmasterとreplicaの接続が途切れてから復旧すると、masterからデータをfull resyncする挙動になっています。
その際BGSAVEが実行されてデータがダンプされますが、メモリを結構使う(最大30~45%程度)ため、Redis masterで使用するメモリ使用量は50%程度にしておかないとダンプ時にメモリが枯渇します。
通常の場合
ダンプファイルを作成し、各replicaがコピーする挙動です。
Disc I/Oが遅いマシンや、トラフィックが遅いとこの処理に非常に時間がかかります。
replicaをstop -> startしたときの挙動は以下です。
master_1 | 1:M 09 Mar 2020 13:11:35.841 * Replica 172.25.0.3:6379 asks for synchronization master_1 | 1:M 09 Mar 2020 13:11:35.841 * Full resync requested by replica 172.25.0.3:6379 master_1 | 1:M 09 Mar 2020 13:11:35.841 * Starting BGSAVE for SYNC with target: disk master_1 | 1:M 09 Mar 2020 13:11:35.841 * Background saving started by pid 14 master_1 | 14:C 09 Mar 2020 13:11:35.844 * DB saved on disk master_1 | 14:C 09 Mar 2020 13:11:35.844 * RDB: 0 MB of memory used by copy-on-write master_1 | 1:M 09 Mar 2020 13:11:35.895 * Background saving terminated with success replica_2_1 | 1:S 09 Mar 2020 13:11:35.896 * MASTER <-> REPLICA sync: receiving 196 bytes from master master_1 | 1:M 09 Mar 2020 13:11:35.896 * Synchronization with replica 172.25.0.3:6379 succeeded
disklessの場合
disklessの場合はダンプファイルを作るのではなくソケット通信によってreplicaにデータを転送します。
################################# REPLICATION ################################# replica-serve-stale-data yes replica-read-only yes repl-diskless-sync yes # yesに repl-diskless-sync-delay 5 # アンコメント repl-disable-tcp-nodelay no replica-priority 100
replicaをstop -> startしたときの挙動は以下です。
master_1 | 1:M 09 Mar 2020 13:06:08.663 * Replica 172.25.0.3:6379 asks for synchronization master_1 | 1:M 09 Mar 2020 13:06:08.663 * Full resync requested by replica 172.25.0.3:6379 master_1 | 1:M 09 Mar 2020 13:06:08.663 * Delay next BGSAVE for diskless SYNC replica_2_1 | 1:S 09 Mar 2020 13:06:14.941 * Full resync from master: 0e0799975d8758bae0e3d2b41623412c854326c8:70 master_1 | 1:M 09 Mar 2020 13:06:14.941 * Starting BGSAVE for SYNC with target: replicas sockets master_1 | 1:M 09 Mar 2020 13:06:14.941 * Background RDB transfer started by pid 13 replica_2_1 | 1:S 09 Mar 2020 13:06:14.942 * MASTER <-> REPLICA sync: receiving streamed RDB from master master_1 | 13:C 09 Mar 2020 13:06:14.941 * RDB: 0 MB of memory used by copy-on-write replica_2_1 | 1:S 09 Mar 2020 13:06:14.942 * MASTER <-> REPLICA sync: Flushing old data replica_2_1 | 1:S 09 Mar 2020 13:06:14.943 * MASTER <-> REPLICA sync: Loading DB in memory replica_2_1 | 1:S 09 Mar 2020 13:06:14.943 * MASTER <-> REPLICA sync: Finished with success replica_2_1 | 1:S 09 Mar 2020 13:06:14.943 * Background append only file rewriting started by pid 12 replica_2_1 | 1:S 09 Mar 2020 13:06:14.973 * AOF rewrite child asks to stop sending diffs. replica_2_1 | 12:C 09 Mar 2020 13:06:14.973 * Parent agreed to stop sending diffs. Finalizing AOF... replica_2_1 | 12:C 09 Mar 2020 13:06:14.973 * Concatenating 0.00 MB of AOF diff received from parent. replica_2_1 | 12:C 09 Mar 2020 13:06:14.974 * SYNC append only file rewrite performed replica_2_1 | 12:C 09 Mar 2020 13:06:14.974 * AOF rewrite: 0 MB of memory used by copy-on-write replica_2_1 | 1:S 09 Mar 2020 13:06:15.041 * Background AOF rewrite terminated with success replica_2_1 | 1:S 09 Mar 2020 13:06:15.041 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB) replica_2_1 | 1:S 09 Mar 2020 13:06:15.041 * Background AOF rewrite finished successfully master_1 | 1:M 09 Mar 2020 13:06:15.042 * Background RDB transfer terminated with success master_1 | 1:M 09 Mar 2020 13:06:15.042 # Slave 172.25.0.3:6379 correctly received the streamed RDB file. master_1 | 1:M 09 Mar 2020 13:06:15.042 * Streamed RDB transfer with replica 172.25.0.3:6379 succeeded (socket). Waiting for REPLCONF ACK from slave to enable streaming master_1 | 1:M 09 Mar 2020 13:06:15.751 * Synchronization with replica 172.25.0.3:6379 succeeded
こちらはパフォーマンスは良い(メモリ使用量が少なくresync時間も少ない)ですが、1台ずつしかresyncできません。
大量のデータを保つ場合はdisklessにした方が良いです。
サンプルコード
今回のサンプルコードはこちらです。
ソース
- Replication – Redis 日本語訳
- redis.confの設定内容 - Qiita
- Redis 5.0 Update解説 - Qiita
- Redis 4.0の目玉機能解説 - Qiita
- 大量データを持つRedisのレプリケーションを作るときにやったこと
- Redis 3.0 diskless replicationの検証 - Qiita
- 作者:Josiah L. Carlson
- 発売日: 2013/12/27
- メディア: 大型本