“READONLY You can't write against a read only replica” 几乎都出现在故障切换之后。这篇用 Redis 8.10.2 一主一从在本机复现:哪些命令被拒、客户端的老连接在角色变化时会怎样、降级前写进旧主的数据去哪了、replica-read-only no 为什么不是修法,以及 Redis Cluster 里同名的 READONLY 命令。
从库角色:slave,replica-read-only = yes
> SET x 1 (error) READONLY You can't write against a read only replica.
> INCR cnt (error) READONLY You can't write against a read only replica.
> DEL k (error) READONLY You can't write against a read only replica.
> EXPIRE k 10 (error) READONLY You can't write against a read only replica.
> LPUSH l b (error) READONLY You can't write against a read only replica.
> FLUSHALL (error) READONLY You can't write against a read only replica.
> GET k "v"
> LRANGE l 0 -1 1) "a"
> EVAL return redis.call('SET','a','1') 0 (error) READONLY You can't write against a read only replica. script: 88c923858e73954cede0
> EVAL return redis.call('GET','k') 0 "v"
> EVAL_RO return redis.call('GET','k') 1 k "v"
> PUBLISH ch hi (integer) 0
> CONFIG SET maxmemory 100mb "OK"
> BGSAVE "Background saving started"
作为对照,同样的写命令发给主库 > SET x 1 -> "OK"写命令被拒;PUBLISH、CONFIG SET、BGSAVE 在从库上并不被拒。
> REPLICAOF NO ONE
"OK"
提升后新主角色:master;旧主这时仍然是:master
降级之前:客户端仍在往旧主写,100 次写入成功 100 次(这就是脑裂窗口:新主上没有这些 key)
新主上有多少个 split* key:0
> REPLICAOF 127.0.0.1 8171
"OK"
旧主降级后角色:slave,master_link_status = up
> SET after 1
(error) READONLY You can't write against a read only replica.
> INCR counter
(error) READONLY You can't write against a read only replica.
同一条连接是否还活着:PING -> "PONG"(没有被断开,只是写被拒)
旧主上降级前写入的 100 个 split* key 现在还剩:0 个(同步后以新主的数据为准,它们丢了)
新主从旧主那里收到的 key:before 存在 = 1老连接不会断,写得到 READONLY;降级前的脑裂窗口写入会丢。
> CONFIG SET replica-read-only no (在旧主/现从库 8170 上) -> "OK"
> SET local_only written-on-replica
"OK"
现在 8170 上 GET local_only = "written-on-replica";新主 8171 上 = (nil)
> REPLICAOF NO ONE
"OK"
> REPLICAOF 127.0.0.1 8171
"OK"
重新全量同步后 8170 上 GET local_only = (nil)从库本地写不传给任何人,下次全量同步就没了。
从库 127.0.0.1:8193(主库 8190)
> GET key2
(error) MOVED 4998 127.0.0.1:8190
> READONLY
"OK"
> GET key2
"v"
> SET key2 w
(error) MOVED 4998 127.0.0.1:8190
> READWRITE
"OK"
> GET key2
(error) MOVED 4998 127.0.0.1:8190从库默认 MOVED,发 READONLY 后这条连接才能读;写仍要去主库。