前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >redis之多机功能

redis之多机功能

原创
作者头像
墨紫羽墨
修改2023-07-28 14:04:45
1970
修改2023-07-28 14:04:45
举报
文章被收录于专栏:FutureTesterFutureTester

4、多机功能

4.1、复制

复制功能是Redis提供的多机功能中最基础的一个,这个功能是通过主从复制(master-slave replication)模式实现的,它允许用户为存储着目标数据库的服务器创建出多个拥有相同数据库副本的服务器,其中存储目标数据库的服务器被称为主服务器(master server),而存储数据库副本的服务器则被称为从服务器(slave server,或者称为replica)

redis-复制
redis-复制

在默认情况下,处于复制模式的主服务器既可以执行写操作也可以执行读操作,而从服务器则只能执行读操作。

redis-复制功能读写权限
redis-复制功能读写权限

Redis的复制功能可以从性能、安全性和可用性3个方面提升整个Redis系统:

  • 首先,在性能方面,Redis的复制功能可以给系统的读性能带来线性级别的提升。从理论上来说,用户每增加一倍数量的从服务器,整个系统的读性能就会提升一倍。
  • 其次,通过增加从服务器的数量,用户可以降低系统在遭遇灾难故障时丢失数据的可能性。具体来说,如果用户为Redis服务器(即主服务器)设置了从服务器,那么即使主服务器遭遇灾难故障,用户也可以通过从服务器访问数据库。从服务器的数量越多因为主服务器遭遇灾难故障而出现数据库丢失的可能性就越低。
  • 最后,通过同时使用Redis的复制功能和Sentinel功能,用户可以为整个Redis系统提供高可用特性。具有这一特性的Redis系统在主服务器停机时,将会自动挑选一个从服务器作为新的主服务器,以此来继续为客户提供服务,避免造成整个系统停机。
4.1.1、REPLICAOF:将服务器设置为从服务器

用户可以通过执行REPLICAOF命令,将接收这个命令的Redis服务器设置为另一个Redis服务器的从服务器:

REPLICAOF host port

命令的host参数用于指定主服务器的地址,而port参数则用于指定主服务器的端口号。因为Redis的复制操作是以异步方式进行的,所以收到REPLICAOF命令的服务器在记录主服务器的地址和端口之后就会向客户端返回OK,至于实际的复制操作则会在后台开始执行。

代码语言:shell
复制
-- 先启动一个端口为6379和端口为1234的redis-server
127.0.0.1:1234> REPLICAOF 127.0.0.1 6379  -- 把1234变成6379的从服务器
OK

127.0.0.1:6379> SET msg "hello world"
OK

127.0.0.1:6379> GET msg
"hello world"

127.0.0.1:1234> GET msg  -- 主服务器上的key从服务器也可以访问
"hello world"
通过配置选项设置从服务器

用户除了可以使用REPLICAOF命令将运行中的Redis服务器设置为从服务器之外,还可以修改redis.conf文件的replicaof选项

replicaof <masterip> <masterport>

还可以通过设置replicaof配置选项,在启动Redis服务器的同时将它设置为从服务器

redis-server --port 1234 --replicaof 127.0.0.1 6379

取消复制

在使用REPLICAOF命令或者replicaof配置选项将一个服务器设置为从服务器之后,我们可以通过执行以下命令,让从服务器停止复制,重新变回主服务器:

REPLICAOF no one

服务器在停止复制之后不会清空数据库,而是会继续保留复制产生的所有数据。

代码语言:shell
复制
127.0.0.1:1234> REPLICAOF no one
OK

127.0.0.1:1234> GET msg
"hello world"
4.1.2、ROLE:查看服务器的角色

用户可以通过执行ROLE命令来查看服务器当前担任的角色:ROLE

主服务器执行ROLE命令

如果执行ROLE命令的是主服务器,那么命令将返回一个由3个元素组成的数组作为结果:

  • 数组的第1个元素是字符串"master",它表示这个服务器的角色为主服务器
代码语言:shell
复制
127.0.0.1:6379> ROLE
1) "master" -- 这是一个主服务器
2) (integer) 2460 -- 它的复制偏移量为2460
3) 1) 1) "127.0.0.1" -- 第1个从服务器的IP地址为127.0.0.1
      2) "1234" -- 这个从服务器的端口号为1234
      3) "2460" -- 它的复制偏移量为2460
   2) 1) "127.0.0.1" -- 第2个从服务器的IP地址为127.0.0.1
      2) "1235" -- 端口号为1235
      3) "2460" -- 复制偏移量为2460
  • 数组的第2个元素是这个主服务器的复制偏移量(replication offset),它是一个整数,记录了主服务器目前向复制数据流发送的数据数量。
  • 数组的第3个元素是一个数组,它记录了这个主服务器属下的所有从服务器。这个数组的每个元素都由3个子元素组成,第1个子元素为从服务器的IP地址,第2个子元素为从服务器的端口号,而第3个子元素则为从服务器的复制偏移量。从服务器的复制偏移量记录了从服务器通过复制数据流接收到的复制数据数量,当从服务器的复制偏移量与主服务器的复制偏移量保持一致时,它们的数据就是一致的。
从服务器执行ROLE命令

如果执行ROLE命令的是从服务器,那么命令将返回一个由5个元素组成的数组作为结果:

  • 数组的第1个元素是字符串"slave",它表示这个服务器的角色是从服务器。127.0.0.1:1234> role 1) "slave" -- 这是一个从服务器 2) "127.0.0.1" -- 主服务器的IP地址 3) (integer) 6379 -- 主服务器的端口号 4) "connected" -- 主从服务器已经进入在线更新状态 5) (integer) 2474 --这个从服务器的复制偏移量为2474
  • 数组的第2个元素和第3个元素记录了这个从服务器正在复制的主服务器的IP地址和端口号。
  • 数组的第4个元素是主服务器与从服务器当前的连接状态,这个状态的值及其表示的意义如下:
  • "none":主从服务器尚未建立连接。
  • "connect":主从服务器正在握手。
  • "connecting":主从服务器成功建立了连接。
  • "sync":主从服务器正在进行数据同步。
  • "connected":主从服务器已经进入在线更新状态。
  • "unknown":主从服务器连接状态未知。
  • 数组的第5个元素是从服务器当前的复制偏移量。
4.1.3、数据同步

当用户将一个服务器设置为从服务器,让它去复制另一个服务器的时候,主从服务器需要通过数据同步机制来让两个服务器的数据库状态保持一致。

完整同步

当一个Redis服务器接收到REPLICAOF命令,开始对另一个服务器进行复制的时候,主从服务器会执行以下操作:

1) 主服务器执行BGSAVE命令,生成一个RDB文件,并使用缓冲区存储起在BGSAVE命令之后执行的所有写命令。

2) 当RDB文件创建完毕,主服务器会通过套接字将RDB文件传送给从服务器。

3) 从服务器在接收完主服务器传送过来的RDB文件之后,就会载入这个RDB文件,从而获得主服务器在执行BGSAVE命令时的所有数据。

4) 当从服务器完成RDB文件载入操作,并开始上线接受命令请求时,主服务器就会把之前存储在缓存区中的所有写命令发送给从服务器执行。

在进行数据同步时重用RDB文件

为了提高数据同步操作的执行效率,如果主服务器在接收到REPLICAOF命令之前已经完成了一次RDB创建操作,并且它的数据库在创建RDB文件之后没有发生过任何变化,那么主服务器将直接向从服务器发送已有的RDB文件,以此来避免无谓的RDB文件生成操作。

此外,如果在主服务器创建RDB文件期间,有多个从服务器向主服务器发送数据同步请求,那么主服务器将把发送请求的从服务器全部放入队列中,等到RDB文件创建完毕之后,再把它发送给队列中的所有从服务器,以此来复用RDB文件并避免多余的RDB文件创建操作。

在线更新

主从服务器在执行完完整同步操作之后,它们的数据就达到了一致状态,但这种一致并不是永久的:每当主服务器执行了新的写命令之后,它的数据库就会被改变,这时主从服务器的数据一致性就会被破坏。

为了让主从服务器的数据一致性可以保持下去,让它们一直拥有相同的数据,Redis会对从服务器进行在线更新:

  • 每当主服务器执行完一个写命令之后,它就会将相同的写命令或者具有相同效果的写命令发送给从服务器执行。
  • 因为完整同步之后的主从服务器在执行最新出现的写命令之前,两者的数据库是完全相同的,而导致两者数据库出现不一致的正是最新被执行的写命令,因此从服务器只要接收并执行主服务器发来的写命令,就可以让自己的数据库重新与主服务器数据库保持一致。
异步更新引起的数据不一致

需要注意的是,因为在线更新是异步进行的,所以在主服务器执行完写命令之后,直到从服务器也执行完相同写命令的这段时间里,主从服务器的数据库将出现短暂的不一致,因此要求强一致性的程序可能需要直接读取主服务器而不是读取从服务器。

此外,因为主服务器可能在执行完写命令并向从服务器发送相同写命令的过程中因故障而下线,所以从服务器在主服务器下线之后可能会丢失主服务器已经执行的一部分写命令,导致从服务器的数据库与下线之前的主服务器数据库处于不一致状态。

部分同步

当因故障下线的从服务器重新上线时,主从服务器的数据通常已经不再一致,因此它们必须重新进行同步,让两者的数据库再次回到一致状态。

为了解决这个问题,Redis从2.8版本开始使用新的重同步功能去代替原来的重同步功能:

  • 当一个Redis服务器成为另一个服务器的主服务器时,它会把每个被执行的写命令都记录到一个特定长度的先进先出队列中。
  • 当断线的从服务器尝试重新连接主服务器的时候,主服务器将检查从服务器断线期间,被执行的那些写命令是否仍然保存在队列里面。如果是,那么主服务器就会直接把从服务器缺失的那些写命令发送给从服务器执行,从服务器通过执行这些写命令就可以重新与主服务器保持一致,这样就避免了重新进行完整同步的麻烦。
  • 如果从服务器缺失的那些写命令已经不存在于队列当中,那么主从服务器将进行一次完整同步。

因为新的重同步功能需要使用先进先出队列来记录主服务器执行过的写命令,所以这个队列的体积越大,它能够记录的写命令就越多,从服务器断线之后能够快速地重新回到一致状态的机会也就越大。Redis为这个队列设置的默认大小为1MB,用户也可以根据自己的需要,通过配置选项repl-backlog-size来修改这个队列的大小。

4.1.4、无须硬盘的复制

主服务器在进行完整同步的时候,需要在本地创建RDB文件,然后通过套接字将这个RDB文件传送给从服务器。创建RDB文件引起的大量硬盘写入将对主服务器的性能造成影响,并导致复制进程变慢。为了解决这个问题,Redis从2.8.18版本开始引入无须硬盘的复制特性(diskless replication):启用了这个特性的主服务器在接收到REPLICAOF命令时将不会再在本地创建RDB文件,而是会派生出一个子进程,然后由子进程通过套接字直接将RDB文件写入从服务器。

要使用无须硬盘的复制特性,我们只需要将repl-diskless-sync配置选项的值设置为yes即可:

repl-diskless-sync <yes|no>

代码语言:text
复制
$ redis-server --repl-diskless-sync yes

无须硬盘的复制特性只是避免了在主服务器上创建RDB文件,但仍然需要在从服务器上创建RDB文件。

4.1.5、降低数据不一致情况出现的概率

因为复制的在线更新操作以异步方式进行,所以当主从服务器之间的连接不稳定,或者从服务器未能收到主服务器发送的更新命令时,主从服务器就会出现数据不一致的情况。

为了尽可能地降低数据不一致的出现概率,Redis从2.8版本开始引入了两个以min-replicas开头的配置选项:

min-replicas-max-lag <seconds>

min-replicas-to-write <numbers>

用户设置了这两个配置选项之后,主服务器只会在从服务器的数量大于等于min-replicas-to-write选项的值,并且这些从服务器与主服务器最后一次成功通信的间隔不超过min-replicas-max-lag选项的值时才会执行写命令。

通过使用这两个配置选项,我们可以让主服务器只在主从服务器连接良好的情况下执行写命令。因为在线更新的异步性质,min-replicas-max-lagmin-replicas-to-write并没有办法完全地杜绝数据不一致的情况出现,但它们可以有效地减少因为主从服务器连接不稳定而导致的数据不一致,并降低因为没有从服务器可用而导致数据丢失的可能性。

4.1.6、可写的从服务器

从Redis 2.6版本开始,Redis的从服务器在默认状态下只允许执行读命令。如果用户尝试对一个只读从服务器执行写命令,那么从服务器将返回以下错误信息:

代码语言:shell
复制
127.0.0.1:1234> REPLICAOF 127.0.0.1 6379
OK
127.0.0.1:1234> SET msg "hello world"
(error) READONLY You can't write against a read only replica.

Redis之所以将从服务器默认设置为只读服务器,是为了确保从服务器只能通过与主服务器进行数据同步来得到更新,从而保证主从服务器之间的数据一致性。

但在某些情况下,我们可能想要将一些不太重要或者临时性的数据存储在从服务器中,或者不得不在从服务器中执行一些带有写性质的命令(比如ZINTERSTORE命令,有序集合求交集,它只能将计算结果存储在数据库中,不能直接返回计算结果)。这时我们可以通过将replica-read-only配置选项的值设置为no来打开从服务器的写功能:

replica-read-only <yes|no>

比如,如果我们在启动服务器127.0.0.1:1234的时候,将replica-read-only配置选项的值设置为no:

代码语言:text
复制
$ redis-server --port 1234 --replica-read-only no

那么即使它变成了一个从服务器,也能够正常地执行客户端发送的写命令:

代码语言:shell
复制
127.0.0.1:1234> REPLICAOF 127.0.0.1 6379
OK
127.0.0.1:1234> SET msg "hello world again!"
OK
127.0.0.1:1234> GET msg
"hello world again!"
使用可写从服务器的注意事项

在使用可写的从服务器时,用户需要注意以下几个方面:

  • 在主从服务器都可写的情况下,程序必须将写命令发送到正确的服务器上,不能把需要在主服务器执行的写命令发送给从服务器执行,也不能把需要在从服务器执行的写命令发送给主服务器执行,否则就会出现数据错误。
  • 从服务器执行写命令得到的数据,可能会被主服务器发送的写命令覆盖。比如,如果从服务器在执行了客户端发送的SET msg "hello from client"命令之后,又接收到了主服务器发送的SET msg "hello from master"命令,那么客户端写入的msg键将被主服务器写入的msg键覆盖。基于这个原因,客户端在从服务器上面执行写命令时,应该尽量避免与主服务器发生键冲突,换句话说,用户应该让客户端和主服务器分别对从服务器数据库中不同的键进行写入,而不要让客户端和主服务器都去写相同的键。
  • 当从服务器与主服务器进行完整同步时,从服务器数据库包含的所有数据都将被清空,其中包括客户端写入的数据。
  • 为了减少内存占用,降低键冲突发生的可能性,并确保主从服务器的数据同步操作可以顺利进行,客户端写入从服务器的数据应该在使用完毕之后尽快删除。一个比较简单的方法是在客户端向从服务器写入数据的同时,为数据设置一个比较短的过期时间,使得这些数据可以在使用完毕之后自动被删除。
4.1.7、脚本复制
脚本传播模式

处于脚本传播模式的主服务器会将被执行的脚本及其参数(也就是EVAL命令本身)复制到AOF文件以及从服务器中。因为带有副作用的函数在不同服务器上运行时可能会产生不同的结果,从而导致主从服务器不一致,所以在这一模式下执行的脚本必须是纯函数,换句话说,对于相同的数据集,相同的脚本以及参数必须产生相同的效果。

为了保证脚本的纯函数性质,Redis对处于脚本传播模式的Lua脚本设置了以下限制:

  • 脚本不能访问Lua的时间模块、内部状态或者除给定参数之外的其他外部信息。
  • 在Redis的命令当中,存在着一部分带有随机性质的命令,这些命令对于相同的数据集以及相同的参数可能会返回不同的结果。如果脚本在执行这类带有随机性质的命令之后,尝试继续执行写命令,那么Redis将拒接执行该命令并返回一个错误。带有随机性质的Redis命令分别为:SPOP、SRANDMEMBER、SCAN、SSCAN、ZSCAN、HSCAN、 RANDOMKEY、LASTSAVE、PUBSUB、TIME。
  • 当用户在脚本中调用SINTER、SUNION、SDIFF、SMEMBERS、HKEYS、HVALS、KEYS这7个会以随机顺序返回结果元素的命令时,为了消除其随机性质,Lua环境在返回这些命令的结果之前会先对结果中包含的元素进行排序,以此来确保命令返回的元素总是有序的。
  • Redis会确保每个被执行的脚本都拥有相同的随机数生成器种子,这意味着如果用户不主动修改这一种子,那么所有脚本在默认情况下产生的伪随机数列都将是相同的。脚本传播模式是Redis复制脚本时默认使用的模式。如果用户在执行脚本之前没有修改过相关的配置选项,那么Redis将使用脚本传播模式来复制脚本。
命令传播模式

处于命令传播模式的主服务器会将执行脚本产生的所有写命令用事务包裹起来,然后将事务复制到AOF文件以及从服务器中。因为命令传播模式复制的是写命令而不是脚本本身,所以即使脚本本身包含副作用,主服务器给所有从服务器复制的写命令仍然是相同的,因此处于命令传播模式的主服务器能够执行带有副作用的非纯函数脚本。

除了脚本可以不是纯函数之外,与脚本传播模式相比,命令传播模式对Lua环境还有以下放松:

·用户可以在执行RANDOMKEY、SRANDMEMBER等带有随机性质的命令之后继续执行写命令。

·脚本的伪随机数生成器在每次调用之前,都会随机地设置种子。换句话说,被执行的每个脚本在默认情况下产生的伪随机数列都是不一样的。

除了以上两点之外,命令传播模式与脚本传播模式的Lua环境限制是一样的,比如,即使在命令传播模式下,脚本还是无法访问Lua的时间模块以及内部状态。

为了开启命令传播模式,用户在使用脚本执行任何写操作之前,需要先在脚本中调用以下函数:

redis.replicate_commands()

redis.replicate_commands()只对调用该函数的脚本有效:在使用命令传播模式执行完当前脚本之后,服务器将自动切换回默认的脚本传播模式。

如果我们在主服务器执行以下命令:

代码语言:shell
复制
EVAL "redis.replicate_commands();redis.call('SET', KEYS[1], 'hello world');redis.call('SET', KEY
S[2], 10086);redis.call('SADD', KEYS[3], 'apple', 'banana', 'cherry')" 3 'msg' 'number' 'fruits'

那么主服务器将向从服务器复制以下命令:

代码语言:shell
复制
MULTI
SET "msg" "hello world"
SET "number" "10086"
SADD "fruits" "apple" "banana" "cherry"
EXEC
选择性命令传播

为了进一步提升命令传播模式的作用,Redis允许用户在脚本中选择性地打开或者关闭命令传播功能,这一点可以通过在脚本中调用redis.set_repl()函数并向它传入以下4个值来完成:

  • redis.REPL_ALL ——默认值,将写命令传播至AOF文件以及所有从服务器。
  • redis.REPL_AOF ——只将写命令传播至AOF文件。
  • redis.REPL_SLAVE ——只将写命令传播至所有从服务器。
  • redis.REPL_NONE ——不传播写命令。

redis.set_repl()函数也只对执行该函数的脚本有效。用户可以通过这一功能来定制被传播的命令序列,以此来确保只有真正需要的命令才会被传播至AOF文件以及从服务器。

存储并集计算结果的脚本

代码语言:lua
复制
--打开目录传播模式
--以便在执行SRANDMEMBER之后继续执行DEL
redis.replicate_commands()
--集合键
local set_a = KEYS[1]
local set_b = KEYS[2]
local result_key = KEYS[3]
--随机元素的数量
local count = tonumber(ARGV[1])
--计算并集,随机选出指定数量的并集元素,然后删除并集
redis.call('SUNIONSTORE', result_key, set_a, set_b)
local elements = redis.call('SRANDMEMBER', result_key, count)
redis.call('DEL', result_key)
--返回随机选出的并集元素
return elements

如果我们使用以下方式执行这个脚本:

代码语言:text
复制
redis-cli --eval union_random.lua set_a set_b union_random , 3

那么主服务器将向从服务器复制以下写命令:

代码语言:shell
复制
MULTI
SUNIONSTORE "union_random" "set_a" "set_b"
DEL "union_random"
EXEC

但仔细地思考一下就会发现,SUNIONSTORE命令创建的union_random实际上只是一个临时集合,脚本在取出并集元素之后就会使用DEL命令将其删除,因此主服务器即使不将SUNIONSTORE命令和DEL命令复制给从服务器,主从服务器包含的数据也是相同的。

修改之后得出的新脚本,新旧两个脚本在执行时将得到相同的结果,但主服务器在执行新脚本时将不会向从服务器复制任何命令。

带有选择性命令传播特性的脚本

代码语言:lua
复制
--打开目录传播模式
--以便在执行SRANDMEMBER之后继续执行DEL
redis.replicate_commands()
--因为这个脚本即使不向从服务器传播SUNIONSTORE命令和DEL命令
--也不会导致主从服务器数据不一致,所以我们可以把命令传播功能关掉
redis.set_repl(redis.REPL_NONE)
--集合键
local set_a = KEYS[1]
local set_b = KEYS[2]
local result_key = KEYS[3]
--随机元素的数量
local count = tonumber(ARGV[1])
--计算并集,随机选出指定数量的并集元素,然后删除并集
redis.call('SUNIONSTORE', result_key, set_a, set_b)
local elements = redis.call('SRANDMEMBER', result_key, count)
redis.call('DEL', result_key)
--返回随机选出的并集元素
return elements

需要注意的是,虽然选择性复制功能非常强大,但用户如果没有正确地使用这个功能,就可能导致主从服务器的数据出现不一致,因此用户在使用这个功能的时候必须慎之又慎。

模式的选择

既然存在着两种不同的脚本复制模式,那么如何选择正确的模式来复制脚本就显得至关重要了。一般来说,用户可以根据以下情况来判断应该使用哪种复制模式:

  • 如果脚本的体积不大,执行的计算也不多,但却会产生大量命令调用,那么使用脚本传播模式可以有效地节约网络资源。
  • 相反,如果一个脚本的体积非常大,执行的计算非常多,但是只会产生少量命令调用,那么使用命令传播模式可以通过重用已有的计算结果来节约计算资源以及网络资源。

举个例子,假设我们正在开发一个游戏系统,该系统的其中一项功能就是在节日给符合条件的一批用户增加指定数量的金币。为此,我们可能会写出包含以下代码的脚本,并在执行这个脚本的时候,通过KEYS变量将数量庞大的用户余额键名传递给脚本:

代码语言:lua
复制
local user_balance_keys = KEYS
local increment = ARGV[1]
--遍历所有给定的用户余额键,对它们执行INCRBY操作
for i = 1, #user_balance_keys do
 redis.call('INCRBY', user_balance_keys[i], increment)
end

很明显,这个脚本将产生相当于传入键数量的INCRBY命令:在用户数量极其庞大的情况下,使用命令传播模式对这个脚本进行复制将耗费大量网络资源,但使用脚本传播模式复制这个脚本则会是一件非常容易的事。

现在,考虑另一种情况,假设我们正在开发一个数据聚合脚本,它包含了一个需要进行大量聚合计算以及大量数据库读写操作的aggregate_work()函数:

代码语言:lua
复制
local result_key = KEYS[1]
local aggregate_work =
function()
 --省略大量代码
end
redis.call('SET', result_key, aggregate_work())

因为执行aggregate_work()函数需要耗费大量计算资源,所以如果我们直接复制整个脚本,那么相同的操作就要在每个从服务器上面都执行一遍,这对于宝贵的计算资源来说无疑是一种巨大的浪费;相反,如果我们使用命令传播模式来复制这个脚本,那么主服务器在执行完这个脚本之后,就可以通过SET命令直接将函数的计算结果复制给各个从服务器。

4.2、Sentinel(哨岗、哨兵)

Sentinel(哨岗、哨兵)是redis的高可用性解决方案:有一个或多个sentinel实例组成的sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后有新的主服务器代替已下线的主服务器继续处理命令请求。

redis-shutdown保存
redis-shutdown保存
4.2.1、启动sentinel

Redis Sentinel的程序文件名为redis-sentinel,它通常和普通Redis服务器redis-server位于同一个文件夹。因为用户需要在配置文件中指定想要被Sentinel监视的主服务器,并且Sentinel也需要在配置文件中写入信息以记录主从服务器的状态,所以用户在启动Sentinel的时候必须传入一个可写的配置文件作为参数,就像这样:

$ redis-sentinel /etc/sentinel.conf

一个Sentinel配置文件至少需要包含以下选项,用于指定Sentinel要监视的主服务器:

sentinel monitor <master-name> <ip> <port> <quorum>

修改sentinel.conf文件里面的选项:

sentinel monitor mymaster 127.0.0.1 1235 1

因为我们目前只启动了一个Sentinel,所以quorum参数的值被设置成了1。也就是说,只要有一个Sentinel认为mymaster主服务器下线了,Sentinel就可以对mymaster实施故障转移了。

因为Sentinel开始监视一个主服务器之后,就会去获取被监视主服务器的从服务器名单,并根据名单对各个从服务器实施监视,整个过程是完全自动的,所以用户只需要输入待监视主服务器的地址就可以了,并不需要输入从服务器的地址。除此之外,Sentinel还会对每个被监视的主从服务器实施心跳检测,并记录各个服务器的在线状态、响应速度等信息,当Sentinel发现被监视的主服务器进入下线状态时,它就会开始对下线的主服务器实施故障转移。

redis-sentinel和redis-server之间的关系

因为Redis Sentinel实际上就是一个运行在特殊模式下的Redis服务器,所以用户也可以使用命令redis-server sentinel.conf --sentinel去启动一个Sentinel:这里的--sentinel参数用于指示Redis服务器进入Sentinel模式,从而变成一个Redis Sentinel而不是普通的Redis服务器。

同时监视多个主服务器

一个Sentinel可以监视任意数量的主服务器,而不是仅仅监视一个主服务器。如果用户想要使用Sentinel去监视多个主服务器,那么只需要在配置文件中指定多个sentinel monitor选项,并为每个被监视的主服务器设置不同的名字即可.

处理重新上线的旧主服务器

Sentinel在对下线的主服务器实施故障转移之后,仍然会继续对它进行心跳检测,当这个服务器重新上线的时候,Sentinel将把它转换为当前主服务器的从服务器。

设置从服务器优先级

用户可以通过replica-priority配置选项来设置各个从服务器的优先级,优先级较高的从服务器在Sentinel选择新主服务器的时候会优先被选择。

replica-priority的默认值为100,这个值越小,从服务器的优先级就越高。

通过这个配置选项,用户可以给性能较高的从服务器设置较高的优先级来尽可能地保证新主服务器的性能。因为主服务器在下线并重新上线之后也会变成从服务器,所以用户也应该为主服务器设置相应的优先级。

replica-priority值为0的从服务器永远不会被选为主服务器,用户可以通过这一设置将不适合用作主服务器的从服务器排除在新主服务器的候选名单之外。

新主服务器的挑选规则

当Sentinel需要在多个从服务器中选择一个作为新的主服务器时,首先会根据以下规则从候选名单中剔除不符合条件的从服务器:

1) 否决所有已经下线以及长时间没有回复心跳检测的疑似已下线从服务器。

2) 否决所有长时间没有与主服务器通信,数据状态过时的从服务器。

3) 否决所有优先级为0的从服务器。

然后根据以下规则,在剩余的候选从服务器中选出新的主服务器:

1) 优先级最高的从服务器获胜。

2) 如果优先级最高的从服务器有两个或以上,那么复制偏移量最大的那个从服务器获胜。

3) 如果符合上述两个条件的从服务器有两个或以上,那么选出它们当中运行ID(运行ID是服务器启动时自动生成的随机ID,这条规则可以确保条件完全相同的多个从服务器最终得到一个有序的比较结果)最小的那一个。

单个Sentinel监视主从服务的弊端:

·单个Sentinel可能会形成单点故障,当唯一的Sentinel出现故障时,针对主从服务器的自动故障转移将无法实施。如果同时有多个Sentinel对主服务器进行监视,那么即使有一部分Sentinel下线了,其他Sentinel仍然可以继续进行故障转移工作。

·单个Sentinel可能会因为网络故障而无法获得主服务器的相关信息,并因此错误地将主服务器判断为下线,继而执行实际上并无必要的故障转移操作。如果同时有多个Sentinel对主服务器进行监视,那么即使有一部分Sentinel与主服务器的连接中断了,其他Sentinel仍然可以根据自己对主服务器的检测结果做出正确的判断,以免执行不必要的故障转移操作。

4.2.2、Sentinel网络
组建Sentinel网络

组建Sentinel网络的方法非常简单,与启动单个Sentinel时的方法一样:用户只需要启动多个Sentinel,并使用sentinel monitor配置选项指定Sentinel要监视的主服务器,那些监视相同主服务器的Sentinel就会自动发现对方,并组成相应的Sentinel网络。

sentinel.conf

代码语言:c
复制
//...
port 26379
sentinel monitor mymaster 127.0.0.1 6379 2
//...

$ ./redis-sentinel sentinel.conf --port 26380

我们只需要修改这个命令的端口,以免造成端口冲突,然后就可以启动多个Sentinel。

将Sentinel和被监视的服务器放到不同机器上运行

用户应该将Sentinel和被监视的Redis服务器放到不同的机器上运行,并且各个Sentinel也应该放到不同的机器上运行,这样Sentinel网络才能够更准确、有效地判断出服务器的实际状态。

4.2.3、Sentinel管理命令
SENTINEL masters:获取所有被监视主服务器的信息

通过向Sentinel发送以下命令,用户可以获得Sentinel正在监视的所有主服务器的相关信息:

SENTINEL masters

代码语言:shell
复制
$ ./redis-cli -p 26379
127.0.0.1:26379> sentinel masters
1)  1) "name"
    2) "mymaster"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "1235"
    7) "runid"
    8) "1f558be473579a3cf1dc8c89b899afbc14286e9a"
    9) "flags"
   10) "master"
   11) "link-pending-commands"
   12) "0"
   13) "link-refcount"
   14) "1"
   15) "last-ping-sent"
   16) "0"
   17) "last-ok-ping-reply"
   18) "49"
   19) "last-ping-reply"
   20) "49"
   21) "down-after-milliseconds"
   22) "30000"
   23) "info-refresh"
   24) "1512"
   25) "role-reported"
   26) "master"
   27) "role-reported-time"
   28) "98411085"
   29) "config-epoch"
   30) "1"
   31) "num-slaves"
   32) "2"
   33) "num-other-sentinels"
   34) "0"
   35) "quorum"
   36) "1"
   37) "failover-timeout"
   38) "180000"
   39) "parallel-syncs"
   40) "1"

因为这个Sentinel目前只监视了127.0.0.1:26379一个主服务器,所以SENTINEL masters命令只返回了一个主服务器的信息。

redis-sentinel-masters字段意义
redis-sentinel-masters字段意义
SENTINEL master:获取指定被监视主服务器的信息

如果你只想获取特定主服务器的信息而不是全部主服务器的信息,可以使用以下命令代替SENTINEL masters命令:

SENTINEL master <master-name>

SENTINEL slaves:获取被监视主服务器的从服务器信息

通过使用以下命令,用户可以让Sentinel返回指定主服务器属下所有从服务器的相关信息:

SENTINEL slaves <master-name>

在SENTINEL slaves命令返回的字段当中,大部分字段都与SENTINEL masters命令返回的字段相同,下面是未介绍过的从服务器专有字段。

redis-sentinel-slaves字段意义
redis-sentinel-slaves字段意义
SENTINEL sentinels:获取其他Sentinel的相关信息

用户可以通过执行以下命令,获取监视同一主服务器的其他所有Sentinel的相关信息:

SENTINEL sentinels <master-name>

SENTINEL sentinels命令返回的大部分信息项都与SENTINEL masters命令以及SENTINEL slaves命令相同,下面是未介绍过的Sentinel专有信息项。

redis-sentinel-sentinels字段意义
redis-sentinel-sentinels字段意义
SENTINEL get-master-addr-by-name:获取给定主服务器的IP地址和端口号

用户可以通过执行以下命令,通过给定主服务器的名字来获取该服务器的IP地址以及端口号:

SENTINEL get-master-addr-by-name <master-name>

代码语言:shell
复制
127.0.0.1:26379> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"
SENTINEL reset:重置主服务器状态

SENTINEL reset命令接受一个glob风格的模式作为参数,接收到该命令的Sentinel将重置所有与给定模式相匹配的主服务器:

SENTINEL reset <pattern>

代码语言:shell
复制
127.0.0.1:26379> SENTINEL reset website_db
(integer) 1   --有一个主服务器被重置了

127.0.0.1:26379> SENTINEL reset website_*   -- 对所有带有website_前缀的主服务器进行重置的方法
(integer) 1

因为SENTINEL reset命令可以让Sentinel忘掉主服务器之前的记录,并重新开始对主服务器进行监视,所以它通常只会在Sentinel网络或者被监视主从服务器的结构出现重大变化时使用。

复杂度:O(N),其中N为被重置的主服务器数量。

SENTINEL failover:强制执行故障转移

通过执行以下命令,用户可以强制对指定的主服务器实施故障转移,就好像它已经下线了一样:

SENTINEL failover <master-name>

接收到这一命令的Sentinel会直接对主服务器执行故障转移操作,而不会像平时那样,先在Sentinel网络中进行投票,然后再根据投票结果决定是否执行故障转移操作。

代码语言:shell
复制
127.0.0.1:26379> SENTINEL failover website_db
OK
SENTINEL ckquorum:检查可用Sentinel的数量

用户可以通过执行以下命令,检查Sentinel网络当前可用的Sentinel数量是否达到了判断主服务器客观下线并实施故障转移所需的数量:

SENTINEL ckquorum <master-name>

代码语言:shell
复制
127.0.0.1:26379> SENTINEL ckquorum website_db
OK 3 usable Sentinels. Quorum and failover authorization can be reached

SENTINEL ckquorum命令一般用于检查Sentinel网络的部署是否成功。

比如,如果我们在部署了3个Sentinel之后,却发现SENTINEL ckquorum只能识别到2个可用的Sentinel,那就说明有什么地方出错了。

SENTINEL flushconfig:强制写入配置文件

用户可以通过向Sentinel发送以下命令,让Sentinel将它的配置文件重新写入硬盘中:

SENTINEL flushconfig

因为Sentinel在被监视服务器的状态发生变化时就会自动重写配置文件,所以这个命令的作用就是在配置文件基于某些原因或错误而丢失时,立即生成一个新的配置文件。此外,当Sentinel的配置选项发生变化时,Sentinel内部也会使用这个命令创建新的配置文件来替换原有的配置文件。

代码语言:shell
复制
$ ls -l
total 8
-rw-r--r-- 1 huangz staff 550 1 21 21:30 sentinel.conf
$ rm sentinel.conf 
$ ls -l
$
然后在客户端中执行重写命令:
127.0.0.1:26379> SENTINEL flushconfig
OK
这样Sentinel将重新生成一个配置文件:
$ ls -l
total 8
-rw-r--r-- 1 huangz staff 549 1 21 21:44 sentinel.conf

最后要注意的是,只有接收到SENTINEL flushconfig命令的Sentinel才会重写配置文件,Sentinel网络中的其他Sentinel并不会受到这个命令的影响。

4.2.4、在线配置Sentinel
SENTINEL monitor:监视给定主服务器

通过执行以下命令,用户可以让Sentinel开始监视一个新的主服务器:

SENTINEL monitor <master-name> <ip> <port> <quorum>

SENTINEL monitor命令本质上就是SENTINEL monitor配置选项的命令版本,当我们想要让Sentinel监视一个新的主服务器,但是又不想重启Sentinel并手动修改Sentinel配置文件时就可以使用这个命令。

代码语言:shell
复制
127.0.0.1:26379> SENTINEL monitor message_queue_db 127.0.0.1 10086 2
OK

在此之后,如果我们执行SENTINEL masters命令,就会看到Sentinel现在除了监视着website_db主服务器之外,还监视着message_queue_db主服务器:

代码语言:shell
复制
127.0.0.1:26379> SENTINEL masters
1) 1) "name"
   2) "message_queue_db"
   3) "ip"
   4) "127.0.0.1"
   5) "port"
   6) "10086"
   ...
   39) "parallel-syncs"
   40) "1"
2) 1) "name"
   2) "website_db"
   3) "ip"
   4) "127.0.0.1"
   5) "port"
   6) "6379"
   ...
   39) "parallel-syncs"
   40) "1"
SENTINEL remove:取消对给定主服务器的监视

当用户想要在线取消Sentinel对某个主服务器的监视时,可以使用以下命令:

SENTINEL remove <masters-name>

接收到这个命令的Sentinel会停止对给定主服务器的监视,并删除Sentinel内部以及Sentinel配置文件中与给定主服务器有关的所有信息,然后返回OK表示操作执行成功。

我们通过使用SENTINEL monitor命令,让Sentinel开始监视message_queue_db主服务器。现在,我们也可以通过执行以下命令,让Sentinel取消对essage_queue_db主服务器的监视:

代码语言:shell
复制
127.0.0.1:26379> SENTINEL remove message_queue_db
OK

在执行了这个命令之后,message_queue_db主服务器就会从Sentinel的监视名单中消失,现在Sentinel只会继续监视website_db主服务器:

代码语言:shell
复制
127.0.0.1:26379> SENTINEL masters
1) 1) "name"
   2) "website_db"
   3) "ip"
   4) "127.0.0.1"
   5) "port"
   6) "6379"
   ...
   39) "parallel-syncs"
   40) "1"
SENTINEL set:修改Sentinel配置选项的值

通过使用以下命令,用户可以在线修改Sentinel配置文件中与主服务器相关的配置选项值:

SENTINEL set <master-name> <option> <value>

只要是Sentinel配置文件中与主服务器有关的配置选项,都可以使用SENTINEL set命令在线进行配置。命令在成功修改给定的配置选项值之后将返回OK作为结果。

代码语言:shell
复制
127.0.0.1:26379> SENTINEL masters
1) 1) "name"
   2) "website_db"
   ...
   35) "quorum"
   36) "2"
   ...
   39) "parallel-syncs"
   40) "1"

现在,如果我们想要将这个选项的值修改为3,那么可以执行以下命令:

代码语言:shell
复制
127.0.0.1:26379> SENTINEL set website_db quorum 3
OK

在执行以上命令之后,我们可以通过再次查看website_db的配置选项来确认修改已经发生:

代码语言:shell
复制
127.0.0.1:26379> SENTINEL masters
1) 1) "name"
   2) "website_db"
   ...
   35) "quorum"
   36) "3"
   ...
   39) "parallel-syncs"
   40) "1"
使用在线配置命令的注意事项

需要注意的是,以上介绍的各个在线配置命令只会对接收到命令的单个Sentinel生效,但并不会对同一个Sentinel网络的其他Sentinel产生影响。为了将新的配置选项传播给整个Sentinel网络,用户需要对同一个Sentinel网络中的所有Sentinel都执行相同的命令。

我们通过向Sentinel 26379发送SENTINEL set website_db quorum 3命令,把该Sentinel对于website_db主服务器的quorum值修改成了3,但这个修改只对Sentinel 26379有效,Sentinel网络中的其他两个Sentinel——Sentinel 26380和Sentinel 26381的quorum值仍然是2。为了让整个Sentinel网络的所有Sentinel都拥有相同的quorum值,我们必须向剩下的两个Sentinel发送相同的SENTINEL set命令:

代码语言:shell
复制
127.0.0.1:26380> SENTINEL set website_db quorum 3
OK
127.0.0.1:26381> SENTINEL set website_db quorum 3
OK

现在,Sentinel网络中的3个Sentinel都把自身对于website_db主服务器的quorum值设置成了3。

Sentinel网络中的各个Sentinel可以拥有不同的quorum值

Redis Sentinel允许用户为Sentinel网络中的每个Sentinel分别设置主服务器的quorum值,而不是让所有Sentinel都共享同一个quorum值,这种做法使得用户可以在有需要时,灵活地根据各个Sentinel所处的环境来调整自己的quorum值。

如果一部分Sentinel与被监视主服务器的网络连接情况较好,或者两者在网络上的距离较近,那么这些Sentinel对于主服务器的下线判断就会更为准确,用户就可以把它们的quorum值调得小一些,使得这些Sentinel可以快速地对下线的主服务器进行故障转移。

相反,如果一部分Sentinel与被监视主服务器的网络连接情况较差,或者两者在网络上的距离较远,那么这些Sentinel对于主服务器的下线判断的准确性就会差一些,如果把它们的quorum值设置得太小,可能会错误地触发故障转移操作。为此,用户可以把这些Sentinel的quorum值调大一些,确保只有在多个Sentinel都认为主服务器已下线时,才执行故障转移操作。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 4、多机功能
    • 4.1、复制
      • 4.1.1、REPLICAOF:将服务器设置为从服务器
      • 4.1.2、ROLE:查看服务器的角色
      • 4.1.3、数据同步
      • 4.1.4、无须硬盘的复制
      • 4.1.5、降低数据不一致情况出现的概率
      • 4.1.6、可写的从服务器
      • 4.1.7、脚本复制
    • 4.2、Sentinel(哨岗、哨兵)
      • 4.2.1、启动sentinel
      • 4.2.2、Sentinel网络
      • 4.2.3、Sentinel管理命令
      • 4.2.4、在线配置Sentinel
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档