可写的从服务器
从 Redis 2.6 版本开始,Redis 的从服务器在默认状态下只允许执行读命令。如果用户尝试对一个只读从服务器执行写命令,那么从服务器将返回以下错误信息:
127.0.0.1:12345> REPLICAOF 127.0.0.1 6379
OK
127.0.0.1:12345> SET msg "hello world"
(error) READONLY You can't write against a read only replica.
bash
Redis 之所以将从服务器默认设置为只读服务器,是为了确保从服务器只能通过与主服务器进行数据同步来得到更新,从而保证主从服务器之间的数据一致性。
但在某些情况下,我们可能想要将一些不太重要或者临时性的数据存储在从服务器中,或者不得不在从服务器中执行一些带有写性质的命令(比如 ZINTERSTORE
命令,它只能将计算结果存储在数据库中,不能直接返回计算结果)。这时我们可以通过将 replica-read-only
配置选项的值设置为 no
来打开从服务器的写功能:
replica-read-only <yes|no>
bash
比如,如果我们在启动服务器 127.0.0.1:12345 的时候,将 replica-read-only
配置选项的值设置为 no
:
$ redis-server --port 12345 --replica-read-only no
bash
那么即使它变成了一个从服务器,也能够正常地执行客户端发送的写命令:
127.0.0.1:12345> REPLICAOF 127.0.0.1 6379
OK
127.0.0.1:12345> SET msg "hello world again!"
OK
127.0.0.1:12345> GET msg
"hello world again!"
bash
使用可写从服务器的注意事项
在使用可写的从服务器时,用户需要注意以下几个方面:
-
在主从服务器都可写的情况下,程序必须将写命令发送到正确的服务器上,不能把需要在主服务器执行的写命令发送给从服务器执行,也不能把需要在从服务器执行的写命令发送给主服务器执行,否则就会出现数据错误。
-
从服务器执行写命令得到的数据,可能会被主服务器发送的写命令覆盖。比如,如果从服务器在执行了客户端发送的 SET msg "hello from client" 命令之后,又接收到了主服务器发送的 SET msg "hello from master" 命令,那么客户端写入的 msg 键将被主服务器写入的 msg 键覆盖。基于这个原因,客户端在从服务器上面执行写命令时,应该尽量避免与主服务器发生键冲突,换句话说,用户应该让客户端和主服务器分别对从服务器数据库中不同的键进行写入,而不要让客户端和主服务器都去写相同的键。
-
当从服务器与主服务器进行完整同步时,从服务器数据库包含的所有数据都将被清空,其中包括客户端写入的数据。
-
为了减少内存占用,降低键冲突发生的可能性,并确保主从服务器的数据同步操作可以顺利进行,客户端写入从服务器的数据应该在使用完毕之后尽快删除。一个比较简单的方法是在客户端向从服务器写入数据的同时,为数据设置一个比较短的过期时间,使得这些数据可以在使用完毕之后自动被删除。
示例:使用从服务器处理复杂计算操作
Redis 的数据相关命令基本上可以划分为两个种类:
-
第 1 种是简单的读写操作,比如 GET 命令、SET 命令、ZADD 命令等,这种命令只需要对数据库进行简单的读写,它们的执行速度一般都非常快。
-
第 2 种是比较复杂的计算操作,比如 SUNION 命令、ZINTERSTORE 命令、BITOP 命令等,这种命令需要对数据库中的元素进行聚合计算,并且随着元素数量的增加,计算耗费的时间也会增加,因此这种命令的执行速度一般都比较慢。
从效率的角度考虑,如果我们在同一个 Redis 服务器中同时处理以上两种命令,那么执行第 2 种命令产生的阻塞时间将导致第 1 种命令执行时的延迟值显著增加。
为此,我们可以通过复制功能创建出当前服务器的从服务器,然后让主服务器只处理第 1 种命令,而第 2 种命令则交给从服务器处理,如图 18-6 所示。

为了能够在从服务器执行诸如 ZINTERSTORE 这样的写命令,我们需要把从服务器的可写特性打开,并且在将计算结果存储到从服务器之后,为它设置一个比较短的过期时间,使得结果可以自动过期。或者在客户端获得结果之后,由客户端将结果写入主服务器进行保存。
如果只使用一个从服务器处理第 2 种命令的速度还不够快,我们可以继续增加从服务器,直到从服务器处理第 2 种命令的速度和延迟值达到我们的要求为止。