bigywater.tech
Redis · 56

从库只读限制

从库默认是只读的。这条限制看起来只是「防止误写」,但关掉它之后会发生什么,很少有人真的试过。这篇把它试了一遍:哪些命令被拦、放开之后数据怎么分叉、重同步时本地写入会不会被保留。

READONLY
默认从库对所有写命令的错误(包括 EVAL 里的写)
分叉
可写从库上的写入不会到主库,主从各有各的数据
全量 vs 部分
全量重同步清掉本地写入;部分重同步保留

默认:哪些命令被拦

实测 · 本机执行输出
> CONFIG GET replica-read-only
1) "replica-read-only"
2) "yes"
> GET a
"1"
> SET a 2
(error) READONLY You can't write against a read only replica.
> DEL a
(error) READONLY You can't write against a read only replica.
> EXPIRE a 10
(error) READONLY You can't write against a read only replica.
> INCR n
(error) READONLY You can't write against a read only replica.
> FLUSHALL
(error) READONLY You can't write against a read only replica.
> EVAL "return redis.call('SET','z',1)" 0
(error) READONLY You can't write against a read only replica. script: 6e61eb62b39720e12a8a2743b63a1fc84f2149fd, on @user_script:1.
> PUBLISH ch x
(integer) 0
> EVAL_RO "return redis.call('GET','a')" 0
"1"

replica-read-only 默认是 yes。SET / DEL / EXPIRE / INCR / FLUSHALL 全部报 READONLY You can't write against a read only replica;EVAL 脚本里调用写命令同样被拒;PUBLISH 和只读的 EVAL_RO 可以执行(PUBLISH 返回 0 是因为这个从库上没有订阅者)。

关掉之后:分叉

实测 · 本机执行输出
> CONFIG SET replica-read-only no
"OK"
> SET local only-on-replica
"OK"
> SET a changed-on-replica
"OK"
主库 GET a = 1   主库 GET local = None
从库 GET a = changed-on-replica   从库 GET local = only-on-replica

把 replica-read-only 改成 no 之后,从库上的写入成功,但主库看不到——主库的 a 还是 1,也没有 local。这不是「双向同步」,只是两边各有各的数据。

实测 · 本机执行输出
从库 GET a = from-master   从库 GET local = only-on-replica
dbsize 主/从: 1 2

主库之后再写同一个 key a,从库上本地的修改被覆盖;而 local 只在从库上,主库从来没有写过,复制流不会去碰它,所以一直留着,dbsize 变成 主 1 / 从 2。

可写从库上的 TTL

实测 · 本机执行输出
600 ms 后从库 GET ttl_local = None

在可写从库上写带过期时间的 key:600 ms 后 GET 返回 nil。本地写入的 key,是从库自己按 TTL 过期的(我没有在这里对比只读从库上主库同步过来的 key 的过期行为,那部分见第 28 篇)。

重同步会怎样

实测 · 本机执行输出
主库 sync_full +1, partial_ok +0
从库 GET local = None   dbsize 主/从: 1001 1001

用一个可断开的 TCP 转发制造断线。断线期间主库写了约 100 KB、超过 backlog(16 KB),恢复后全量同步,从库的 local 被清掉,dbsize 主从一致(1001)。

实测 · 本机执行输出
主库 sync_full +0, partial_ok +1
从库 GET local2 = v   主库 GET local2 = None

如果断线期间只写了一点,恢复后是部分重同步,从库的 local2 还在,主库却没有。所以:本地写入是否被保留,取决于这次重同步是全量还是部分——不可预测。这也是默认只读、不建议放开的原因。

分步演示:从库只读与数据分叉

从库只读限制
本步涉及的数据

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。主从之间放了一个可断开的 TCP 转发用来制造断线
    • 没有验证:可写从库配合 Sentinel / Cluster 的行为、多层级联复制下的本地写入、被拦的写命令是否有遗漏(只测了几个常见命令)
    京ICP备2026067502号-1京公网安备11010502063487号