TL;DR
書き込みコマンド(SET、DEL、INCRなど)をRedisのレプリカノードに送信しました。レプリカはデフォルトで読み取り専用です——書き込みを受け付けません。代わりにマスターノードに接続してください。または、特別な理由がある場合は、CONFIG SET replica-read-only noでレプリカへの書き込みを一時的に許可できます。
このエラーが発生する理由
Redisのレプリケーションは一方向です:マスター → レプリカ。レプリカはマスターからデータを継続的に同期し、すべての書き込みコマンドを以下のエラーで拒否します:
(error) DENY COMMANDS ASKING WRITE. REPLICA can't execute write commands.
よくある原因はいくつかあります:
- アプリの設定にレプリカのIP/ポートをハードコードしており、フェイルオーバー後に更新し忘れた。
- Redis Sentinelが新しいマスターに昇格させたが、クライアントはまだ古いノード——今はレプリカ——を指している。
- 間違ったポートに接続している。一般的な2ノード構成では
6379がマスターで6380がレプリカだが、環境によって異なる。 - コネクションプールがどのノードがマスターか把握せずにノード間でラウンドロビンしている。
接続中のノードを確認する
redis-cliでROLEを実行すると、ノードの役割をすぐに確認できます:
redis-cli -h <host> -p <port> ROLE
レプリカの場合、次のように応答します:
1) "slave"
2) "192.168.1.10"
3) (integer) 6379
4) "connected"
5) (integer) 12345
マスターの場合、次のように応答します:
1) "master"
2) (integer) 12345
3) 1) 1) "192.168.1.11"
2) "6379"
3) "12345"
出力にslaveが表示されていますか?書き込みには間違ったノードに接続しています。
修正1:マスターノードに接続する(推奨)
各ノードでROLEを実行してマスターを見つけるか、INFO replicationを使用します:
redis-cli -h <host> -p <port> INFO replication
role:masterの行を探してください。そのホストとポートがすべての書き込みの送信先です。
Redis Sentinelの構成
IPのハードコードは完全に避けましょう。Sentinelに現在のマスターノードを問い合わせます:
redis-cli -h <sentinel-host> -p 26379 SENTINEL get-master-addr-by-name mymaster
フェイルオーバー後でも、現在のマスターのIPとポートが返されます。さらに良いのは、Sentinel対応クライアントを使用することで、アプリが手動で問い合わせる必要がなくなります:
# Python (redis-py)
from redis.sentinel import Sentinel
sentinel = Sentinel(
[('sentinel-host', 26379)],
socket_timeout=0.1
)
master = sentinel.master_for('mymaster', socket_timeout=0.1)
master.set('key', 'value') # 常に現在のマスターにルーティングされる
Redis Clusterの構成
Cluster対応クライアントはスロットからマスターへのルーティングを自動的に処理します。手動でノードを選択する必要はありません:
# Python (redis-py cluster)
from redis.cluster import RedisCluster
rc = RedisCluster(host='node1', port=6379, decode_responses=True)
rc.set('key', 'value') # このキーを担当するシャードマスターをクライアントが自動判断する
修正2:レプリカへの書き込みを許可する(テスト用のみ)
ローカルテストやワンタイムのマイグレーション作業など、レプリカへの書き込みが本当に必要な場合があります。実行時に読み取り専用モードを無効にします:
redis-cli -h <replica-host> -p <port> CONFIG SET replica-read-only no
Redis 5.0より古いバージョンでは、設定キーの名前が異なります:
redis-cli -h <replica-host> -p <port> CONFIG SET slave-read-only no
**警告:**レプリカに直接書き込んだデータは、次回マスターが同期するときに消去されます。これによりデータの乖離が発生します。明確な計画なしに本番環境では絶対に使用しないでください。
再起動後も変更を維持するには、レプリカのredis.confを編集します:
replica-read-only no
その後、Redisを再起動します:
sudo systemctl restart redis
修正3:フェイルオーバー後——アプリの設定を更新する
SentinelまたはClusterがフェイルオーバーをトリガーしました。アプリはまだ古いマスターのアドレス——現在はレプリカ——にハードコードされています。設定を更新します:
# フェイルオーバー前(古い設定——レプリカを指している)
REDIS_HOST=192.168.1.10
REDIS_PORT=6379
# フェイルオーバー後(正しい設定——新しいマスター)
REDIS_HOST=192.168.1.11
REDIS_PORT=6379
これで短期的には解決します。長期的には、SentinelまたはClusterクライアントに切り替えましょう。アプリが将来のフェイルオーバーを自動的に処理するため、設定の変更は不要になります。
修正を確認する
マスターを指しましたか?簡単な書き込み/読み取り/削除のサイクルを実行して確認します:
redis-cli -h <master-host> -p 6379 SET testkey "hello"
# 期待される結果: OK
redis-cli -h <master-host> -p 6379 GET testkey
# 期待される結果: "hello"
redis-cli -h <master-host> -p 6379 DEL testkey
# 期待される結果: (integer) 1
レプリケーション自体が正常かどうかも確認します:
redis-cli -h <master-host> -p 6379 INFO replication | grep -E 'role|connected_slaves|slave'
出力にrole:masterと少なくとも1つの接続済みスレーブが表示されていることを確認してください。
クイックリファレンス:どの構成を使用していますか?
- スタンドアロンレプリカ:各ノードで
ROLEを実行してマスターを見つけ、直接接続する。 - Redis Sentinel:
SENTINEL get-master-addr-by-nameを使用するか、さらに良いのはSentinel対応クライアントライブラリを使用する。 - Redis Cluster:Cluster対応クライアントを使用する。書き込みを正しいシャードマスターに自動的にルーティングする。

