redis学习笔记
键空间通知(keyspace notification)
键空间通知,客户端可以通过订阅频道或者模式来接收redis改动的数据集。
命令产生的通知
DEL
命令为每个被删除的键产生一个del
通知。RENAME
产生两个通知:为来源键(source key)产生一个rename_from
通知,并为目标键(destination key)产生一个rename_to
通知。EXPIRE
和EXPIREAT
在键被正确设置过期时间时产生一个expire
通知。当EXPIREAT
设置的时间已经过期,或者EXPIRE
传入的时间为负数值时,键被删除,并产生一个del
通知。SORT
在命令带有STORE
参数时产生一个sortstore
事件。如果STORE
指示的用于保存排序结果的键已经存在,那么程序还会发送一个del
事件。SET
以及它的所有变种(SETEX
、SETNX
和GETSET
)都产生set
通知。其中SETEX
还会产生expire
通知。MSET
为每个键产生一个set
通知。SETRANGE
产生一个setrange
通知。INCR
、DECR
、INCRBY
和DECRBY
都产生incrby
通知。INCRBYFLOAT
产生incrbyfloat
通知。APPEND
产生append
通知。LPUSH
和LPUSHX
都产生单个lpush
通知,即使有多个输入元素时,也是如此。RPUSH
和RPUSHX
都产生单个rpush
通知,即使有多个输入元素时,也是如此。RPOP
产生rpop
通知。如果被弹出的元素是列表的最后一个元素,那么还会产生一个del
通知。LPOP
产生lpop
通知。如果被弹出的元素是列表的最后一个元素,那么还会产生一个del
通知。LINSERT
产生一个linsert
通知。LSET
产生一个lset
通知。LTRIM
产生一个ltrim
通知。如果LTRIM
执行之后,列表键被清空,那么还会产生一个del
通知。RPOPLPUSH
和BRPOPLPUSH
产生一个rpop
通知,以及一个lpush
通知。两个命令都会保证rpop
的通知在lpush
的通知之前分发。如果从键弹出元素之后,被弹出的列表键被清空,那么还会产生一个del
通知。HSET
、HSETNX
和HMSET
都只产生一个hset
通知。HINCRBY
产生一个hincrby
通知。HINCRBYFLOAT
产生一个hincrbyfloat
通知。HDEL
产生一个hdel
通知。如果执行HDEL
之后,哈希键被清空,那么还会产生一个del
通知。SADD
产生一个sadd
通知,即使有多个输入元素时,也是如此。SREM
产生一个srem
通知,如果执行SREM
之后,集合键被清空,那么还会产生一个del
通知。SMOVE
为来源键(source key)产生一个srem
通知,并为目标键(destination key)产生一个sadd
事件。SPOP
产生一个spop
事件。如果执行SPOP
之后,集合键被清空,那么还会产生一个del
通知。SINTERSTORE
、SUNIONSTORE
和SDIFFSTORE
分别产生sinterstore
、sunionostore
和sdiffstore
三种通知。如果用于保存结果的键已经存在,那么还会产生一个del
通知。ZINCRBY
产生一个zincr
通知。(译注:非对称,请注意。)ZADD
产生一个zadd
通知,即使有多个输入元素时,也是如此。ZREM
产生一个zrem
通知,即使有多个输入元素时,也是如此。如果执行ZREM
之后,有序集合键被清空,那么还会产生一个del
通知。ZREMRANGEBYSCORE
产生一个zrembyscore
通知。(译注:非对称,请注意。)如果用于保存结果的键已经存在,那么还会产生一个del
通知。ZREMRANGEBYRANK
产生一个zrembyrank
通知。(译注:非对称,请注意。)如果用于保存结果的键已经存在,那么还会产生一个del
通知。ZINTERSTORE
和ZUNIONSTORE
分别产生zinterstore
和zunionstore
两种通知。如果用于保存结果的键已经存在,那么还会产生一个del
通知。- 每当一个键因为过期而被删除时,产生一个
expired
通知。- 每当一个键因为
maxmemory
政策而被删除以回收内存时,产生一个evicted
通知。
过期通知的发送时间
Redis 删除过期键的方式:
- 当访问一个键时,redis会对这个键进行检查,如果过期则删除。
- 底层系统会渐进地查找并删除那些过期的键,处理掉那些已经过期,单不被访问的键。
当上述任一条件触发时,redis会将该键删除,并发出一个 expired
通知。
Redis 并不保证生存时间(TTL)变为 0 的键会立即被删除: 如果程序没有访问这个过期键, 或者带有生存时间的键非常多的话, 那么在键的生存时间变为 0 , 直到键真正被删除这中间, 可能会有一段比较显著的时间间隔。
因此, Redis 产生 expired 通知的时间为过期键被删除的时候, 而不是键的生存时间变为 0 的时候。
事务(transaction)
MULTI
、 EXEC
、 DISCARD
、WATCH
是redis事务的基础命令。
事务由一条或多条命令组成,事务具备如下特点:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
注意事项:
- 如果客户端使用
MULTI
命令开启了一个事务,单却因为某种原因导致没有执行EXEC
命令;那么事务中的所有命令均不会被执行。- 如果执行
MULTI
命令后连接中断了,那么事务中的所有命令都会被依次执行。
当使用 AOF 方式做持久化的时候, Redis 会使用单个 write(2) 命令将事务写入到磁盘中。
意外中断导致的问题和解决方案:
- 如果 Redis 服务器因为某些原因被管理员杀死,或者遇上某种硬件故障,那么可能
只有部分事务命令会被成功写入到磁盘中
。- 如果 Redis 在重新启动时发现 AOF 文件出了这样的问题,那么它会退出,并汇报一个错误。
使用 redis-check-aof
程序可以修复上述问题:它会移除 AOF
文件中不完整事务的信息,确保服务器可以顺利启动。
用法
开启事务: MULTI
事务命令。。。
事务命令。。。
执行事务: EXEC / 放弃事务: DISCARD
127.0.0.1:6379> set test 100
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr test
QUEUED
127.0.0.1:6379> get test
QUEUED
127.0.0.1:6379> exec
1) (integer) 101
2) "101"
127.0.0.1:6379>
EXEC 命令的回复是一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 其中, 回复元素的先后顺序和命令发送的先后顺序一致。
事务中的错误处理机制
事务可能会遇到如下两种错误:
EXEC
执行前发生的错误:也就是入队时发生的错误。该种情况下事务会取消执行
。
1、2.6.5 以前会执行入队成功的命令。
2、2.6.5 以后会记录入队错误的命令,在exec执行时放弃执行事务。EXEC
执行后发生的错误:也就是执行exec以后发生的命令执行错误。该种情况下事务会执行,只是忽略了那些执行错误的命令。
为什么 Redis 不支持回滚(roll back)
- Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
- 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
通常情况下,回滚是由于编程错误导致的,即使回滚也于事无补;然而这种情况通常不会在生产环境中出现,所以 Redis 选择了更简单、更快速的无回滚方式来处理事务。
使用 WATCH
check-and-set
操作实现乐观锁
WATCH
命令可以为 Redis 事务提供 check-and-set (CAS)行为。
127.0.0.1:6379> watch test
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr test
QUEUED
127.0.0.1:6379> get test
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379>
进入事务前执行了watch命令,监听 test 键。另外的客户端改变了 test 的值,在执行事务时,由于检测到test的值发生了改变,因此事务被放弃执行了。
注意事项:
- 如果你使用 WATCH 监视了一个带过期时间的键, 那么即使这个键过期了, 事务仍然可以正常执行。
- WATCH 命令可以被调用多次。 对键的监视从 WATCH 执行之后开始生效, 直到调用 EXEC 为止。
- 当 EXEC 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消。
- 当客户端断开连接时, 该客户端对键的监视也会被取消。
- UNWATCH 命令可以手动取消对所有键的监视。
WATCH 可以用于创建 Redis 没有内置的原子操作。
Redis 脚本和事务
- 从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。
发布与订阅(pub/sub)
发布: publish
订阅: subscribe
取消订阅: unsubscribe
订阅模式
redis 支持模式匹配(pattern matching): 客户端可以订阅一个带 * 号的模式, 如果某个/某些频道的名字和这个模式匹配, 那么当有信息发送给这个/这些频道的时候, 客户端也会收到这个/这些频道的信息。
PSUBSCRIBE
和 PUNSUBSCRIBE
订阅总数
执行 SUBSCRIBE 、 UNSUBSCRIBE 、 PSUBSCRIBE 和 PUNSUBSCRIBE 命令时, 返回结果的最后一个元素
是客户端目前仍在订阅的频道和模式总数。
复制(Replication)
Redis 支持简单且易用的主从复制(master-slave replication)功能, 该功能可以让从服务器(slave server)成为主服务器(master server)的精确复制品。
redis 复制功能的几个重要方面:
- Redis 使用异步复制。 从 Redis 2.8 开始, 从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。
- 一个主服务器可以有多个从服务器。
不仅主服务器可以有从服务器, 从服务器也可以有自己的从服务器, 多个从服务器之间可以构成一个图状结构。
- 复制功能不会阻塞主服务器: 即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。
- 复制功能也不会阻塞从服务器: 只要在 redis.conf 文件中进行了相应的设置, 即使从服务器正在进行初次同步, 服务器也可以使用旧版本的数据集来处理命令查询。
- 不过, 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内, 连接请求会被阻塞。
- 你还可以配置从服务器, 让它在与主服务器之间的连接断开时, 向客户端发送一个错误。
- 复制功能可以单纯地用于数据冗余(data redundancy), 也可以通过让多个从服务器处理只读命令请求来提升扩展性(scalability): 比如说, 繁重的 SORT 命令可以交给附属节点去运行。
- 可以通过复制功能来让主服务器免于执行持久化操作: 只要关闭主服务器的持久化功能, 然后由从服务器去执行持久化操作即可。
复制功能的运作原理
Title:Redis复制原理
Redis从服务器->Redis主服务器:发送 SYNC 命令(一个或多个 SYNC 命令)
Redis主服务器->Redis主服务器:执行 BGSAVE 命令
Redis主服务器->Redis主服务器:将所有新执行的写入命令都保存到一个缓冲区里面
Redis主服务器->Redis主服务器: BGSAVE 执行成功
Redis主服务器->Redis从服务器: 主服务器将执行保存操作所得的 .rdb 文件发送给从服务器
Redis从服务器->Redis从服务器: 将 .rdb 文件中的数据载入到内存中
Redis主服务器->Redis从服务器: 以 Redis 命令协议的格式, 将写命令缓冲区中积累的所有内容都发送给从服务器。
部分重同步
从服务器可以在主从服务器之间的连接断开时进行自动重连:
- 在 Redis 2.8 版本之前, 断线之后重连的从服务器总要执行一次完整重同步(full resynchronization)操作
- 但是从 Redis 2.8 版本开始, 从服务器可以根据主服务器的情况来选择执行完整重同步还是部分重同步(partial resynchronization)。
配置从服务器
- 配置文件中添加: slaveof 192.168.1.1 6379
- 命令行中执行:
127.0.0.1:6379> SLAVEOF 192.168.1.1 10086
只读从服务器
从 Redis 2.6 开始, 从服务器支持只读模式, 并且该模式为从服务器的默认模式
。
配置:
redis.conf
中添加slave-read-only
选项控制。- 通过
CONFIG SET
命令来开启或关闭。
从服务器配置
如果主服务器设置了需要密码访问,那么从服务器也需要为从服务器进行相应的身份验证设置。
- 从服务器中运行:
config set masterauth <password>
- 在从服务器配置文件中添加:
masterauth <password>
主服务器只在有至少 N 个从服务器的情况下,才执行写操作
- 从 Redis 2.8 开始, 为了保证数据的安全性, 可以通过配置, 让主服务器只在有至少 N 个当前已连接从服务器的情况下, 才执行写命令。
- 采用异步复制数据的方式,因此会导致数据的丢失。
Title:主从服务器数据同步
从服务器->主服务器:以每秒一次的频率 PING 主服务器一次,\n并报告复制流的处理情况
主服务器->主服务器:记录各个从服务器最后一次 PING 的时间
主服务器->主服务器:如果从服务器满足条件(从服务器数量 > min-slaves-to-write && 延迟值 < min-slaves-max-lag)
主服务器->主服务器:执行写操作
主服务器->主服务器:如果从服务器不满足条件(从服务器数量 > min-slaves-to-write && 延迟值 < min-slaves-max-lag)
主服务器->从服务器:返回错误码
min-slaves-to-write <number of slaves> 和 min-slaves-max-lag <number of seconds> 可在配置文件中进行配置。
通信协议(protocol)
网络层
客户端和服务器发送的命令或数据一律以 \r\n
(CRLF)结尾。
请求
一个多条批量回复以 *<argc>\r\n
为前缀, 后跟多条不同的批量回复, 其中 argc 为这些批量回复的数量。
新版统一请求协议
回复
回复类型(回发数据的第一个字节):
- 状态回复(status reply)的第一个字节是 "+"
- 错误回复(error reply)的第一个字节是 "-"
- 整数回复(integer reply)的第一个字节是 ":"
- 批量回复(bulk reply)的第一个字节是 "$"
- 多条批量回复(multi bulk reply)的第一个字节是 "*"
持久化(persistence)
Redis持久化
redis提供了多种不同级别的持久化方式:
- RDB:
指定的时间间隔
内生成数据集的时间点快照(point-in-time snapshot)。- AOF: 记录服务器的所有写操作命令,并在服务器启动时重新执行执行这些命令来还原数据。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
- AOF 和 RDB 持久化:但此方式重启时会
优先使用 AOF 文件来还原数据集
,因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。- 可以关闭持久化功能,让数据只在服务器运行时存在。
RDB 的优点
RDB
是一个非常紧凑(compact)的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份: 比如说,你可以在最近的 24 小时内,每小时备份一次 RDB 文件,并且在每个月的每一天,也备份一个 RDB 文件。 这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。RDB
非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的数据中心,或者亚马逊 S3 中。RDB
可以最大化Redis
的性能:父进程在保存RDB
文件时唯一要做的就是fork
出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作
。RDB
在恢复大数据集时的速度比AOF
的恢复速度要快。
RDB 的缺点
- 有可能造成数据丢失,两次RDB保存操作间的数据有可能会丢失。
- 如果数据量很大时有可能导致停顿。
- 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,
数据的耐久性都不会有任何损失
。
AOF 的优点
- 使用 AOF 持久化可以尽可能避免数据的丢失。你可以设置不同的 fsync 策略:
AOF 的默认策略为每秒钟 fsync 一次
。在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)。- AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek , 即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等),
redis-check-aof
工具也可以轻易地修复这种问题。- Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
- AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
AOF 的缺点
- 对于相同的数据集来说,
AOF 文件的体积通常要大于 RDB 文件的体积
。- 根据所使用的 fsync 策略,
AOF 的速度可能会慢于 RDB
。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。- AOF 在过去曾经发生过这样的 bug : 因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。 (举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。) 测试套件里为这种情况添加了测试: 它们会自动生成随机的、复杂的数据集, 并通过重新载入这些数据来确保一切正常。 虽然这种 bug 在 AOF 文件中并不常见, 但是对比来说, RDB 几乎是不可能出现这种 bug 的。
RDB 和 AOF ,我应该用哪一个?
- 如果对安全性要求非常苛刻,那么可以同时使用两种持久化功能。
- 如果对数据安全性要求很高,但仍可接受几分钟以内数据的丢失,那么使用
RDB
的方式持久化即可。- 不推荐单独使用
AOF
的方式进行持久化
不推荐单独使用AOF方式的原因:
- 定时生成
RDB
快照便于进行数据库备份。RDB
数据集恢复的速度比AOF
快。RDB
方式可以避免AOF
程序的bug。
RDB快照
redis快照保存的名字为 dump.rdb
- 可以设置
N秒内数据集达到或多于M次改动时
执行一次保存操作。- 可以通过 SAVE 和 BGSAVE 手动保存数据集。
快照的运作方式
redis 保存 dump.rdb 文件时的操作:
Title:redis RDB方式保存快照工作流程
Redis主进程->Redis子进程:fork()
Redis子进程->Redis子进程:将数据写入RDB临时文件
Redis子进程->Redis子进程:写入RDB文件完成
Redis子进程->Redis主进程
Redis主进程->Redis主进程:用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件
只进行追加操作的文件(append-only file,AOF)
由于 RDB 的方式会导致写入 redis 但未保存到快照中的数据丢失。
设置方式:
- 配置文件中添加
appendonly yes
AOF 重写
由于每条redis命令都会追加一条记录到AOF文件末尾,因此会导致许多不必要的命令。AOF的文件也会不断累积而变得越来越大。为了解决这个问题,redis可以在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。
执行 BGREWRITEAOF 命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。
AOF持久化时间间隔
Redis 将数据 fsync 到磁盘的方式:
- 每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
- 每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。 ---->
推荐
- 从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。
AOF 文件出错解决方案
- 为现有的 AOF 文件创建一个备份。
- 使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复。
$ redis-check-aof --fix
- (可选)使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
- 重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。
AOF 的运作方式
AOF 重写和 RDB 创建快照类似,都巧妙地利用了写时复制机制:
Title:redis RDB方式保存快照工作流程
Redis主进程->Redis子进程:fork()
Redis主进程->Redis主进程:将命令累积到一个内存缓存中
Redis子进程->Redis子进程:将数据写入AOF临时文件(即使宕机也仍然是安全的)
Redis子进程->Redis子进程:写入AOF文件完成
Redis子进程->Redis主进程
Redis主进程->Redis主进程:用新 AOF 文件替换原来的 AOF 文件,并删除旧的 AOF 文件
Redis主进程->Redis子进程:所有命令都会直接追加到新 AOF 文件的末尾
RDB 持久化切换到 AOF 持久化
- 为最新的 dump.rdb 文件创建一个备份。
- 将备份放到一个安全的地方。
- 执行以下两条命令:
redis-cli> CONFIG SET appendonly yes
redis-cli> CONFIG SET save "" // 可选操作,两种方式同时使用时,不用此设置。
- 确保命令执行之后,数据库的键的数量没有改变。
- 确保写命令会被正确地追加到 AOF 文件的末尾。
RDB 和 AOF 之间的相互作用
在版本号大于等于 2.4 的 Redis 中:
- BGSAVE 执行的过程中, 不可以执行 BGREWRITEAOF 。
- 反过来说, 在 BGREWRITEAOF 执行的过程中, 也不可以执行 BGSAVE 。
这样可以避免两个Redis后台进程同时对磁盘进行大量的 I/O 操作。
client->server:发送BGSAVE
server->BGSAVE
server->server:执行BGSAVE
client->server:发送BGREWRITEAOF
server->BGREWRITEAOF
server->client:ok(BGREWRITEAOF 已经被预定执行)
BGSAVE->server:执行完毕
server->BGREWRITEAOF
BGREWRITEAOF->client:继续执行
备份 Redis 数据
- 创建一个定期任务(cron job), 每小时将一个 RDB 文件备份到一个文件夹, 并且每天将一个 RDB 文件备份到另一个文件夹。
- 确保快照的备份都带有相应的日期和时间信息, 每次执行定期任务脚本时, 使用 find 命令来删除过期的快照: 比如说, 你可以保留最近 48 小时内的每小时快照, 还可以保留最近一两个月的每日快照。
- 至少每天一次, 将 RDB 备份到你的数据中心之外, 或者至少是备份到你运行 Redis 服务器的物理机器之外。
容灾备份
异地容灾备份。
Sentinel 哨兵##
Redis的Sentinel(哨兵)管理着多个Redis的实例,其包含三个任务:
- 监控(Monitoring): Sentinel 会不间断地检测Redis主服务器和从服务器是否正常运作。
- 提醒(Notification): 当监控发现主服务器或从服务器出现问题时,Sentinel 可以通过API或者其它应用程序发送通知。
- 自动故障迁移(Automatic failover): 当一个主服务器无法正常工作时,Sentinel 会进行故障转移操作;它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
Sentinel->Master: ping
Sentinel->Slave: ping
Sentinel->Slave2: ping
Master->Master: 主观下线(SDOWN)\n 客观下线(ODOWN)
Sentinel->Master: ping
Master-->Sentinel: 主观下线(SDOWN)\n 客观下线(ODOWN)
Sentinel->Slave: failover,投票胜出将Slave转换成master
Slave-->Sentinel: 成功
Sentinel->Slave2: 通知
Slave2-->Slave: 从新master中复制数据
启动 Sentinel
- redis-sentinel 方式启动:redis-sentinel /path/to/sentinel.conf
- redis-server 方式启动:redis-server /path/to/sentinel.conf --sentinel
配置 Sentinel
Sentinel 所需的最少配置:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
- 一个 Sentinel 都需要获得系统中多数(majority) Sentinel 的支持,才能发起一次自动故障迁移,在只有少数(minority) Sentinel 进程正常运作的情况下, Sentinel 是不能执行自动故障迁移的。
- 服务断线的判断依据:
down-after-milliseconds
选项,断线所需的毫秒数
。- 最多可以有多少个从服务器同时对新的主服务器进行同步:
parallel-syncs
选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步,这个数字越小, 完成故障转移所需的时间就越长
。
主观下线和客观下线
- 主观下线(Subjectively Down, 简称
SDOWN
)指的是单个 Sentinel 实例对服务器做出的下线判断。- 客观下线(Objectively Down, 简称
ODOWN
)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。 (一个 Sentinel 可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线。)
客观下线条件只适用于主服务器
: 对于任何其他类型的 Redis 实例, Sentinel 在将它们判断为下线前不需要进行协商, 所以从服务器或者其他 Sentinel 永远不会达到客观下线条件。
- 只要一个 Sentinel 发现某个主服务器进入了客观下线状态, 这个 Sentinel 就可能会被其他 Sentinel 推选出, 并对失效的主服务器执行自动故障迁移操作。
每个Sentinel定期执行的任务
Sentinel->Master: ping
Master-->Sentinel: +PONG \n -LOADING \n -MASTERDOWN
Sentinel->Sentinel: sleep 10s
Sentinel->Master: ping
Sentinel2->Master: ping
Master-->Sentinel2: -MASTERDOWN(master主动下线)
Sentinel2->Sentinel2: sleep 1s
Sentinel2->Master: ping
Master-->Sentinel2
Sentinel3->Master: ping
Master-->Sentinel3: -MASTERDOWN(master主动下线)
Sentinel3->Sentinel3: sleep 1s
Sentinel3->Master: ping
Master-->Sentinel3
- 每个 Sentinel 以每秒钟一次的频率向它所知的主服务器、从服务器以及其他 Sentinel 实例发送一个 PING 命令。
- 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 那么这个实例会被 Sentinel 标记为主观下线。 一个有效回复可以是: +PONG 、 -LOADING 或者 -MASTERDOWN 。
- 如果一个主服务器被标记为主观下线, 那么正在监视这个主服务器的所有 Sentinel 要以每秒一次的频率确认主服务器的确进入了主观下线状态。
- 如果一个主服务器被标记为主观下线, 并且有足够数量的 Sentinel (至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断, 那么这个主服务器被标记为客观下线。
- 在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有主服务器和从服务器发送 INFO 命令。 当一个主服务器被 Sentinel 标记为客观下线时, Sentinel 向下线主服务器的所有从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
- 当没有足够数量的 Sentinel 同意主服务器已经下线, 主服务器的客观下线状态就会被移除。 当主服务器重新向 Sentinel 的 PING 命令返回有效回复时, 主服务器的主管下线状态就会被移除。
自动发现 Sentinel 和从服务器
一个 Sentinel 可以与其他多个 Sentinel 进行连接, 各个 Sentinel 之间可以互相检查对方的可用性, 并进行信息交换。
Sentinel 可以通过发布与订阅功能来自动发现正在监视相同主服务器的其他 Sentinel , 这一功能是通过向频道 __sentinel__:hello
发送信息来实现的。
不必手动列出主服务器属下的所有从服务器, 因为 Sentinel 可以通过询问主服务器来获得所有从服务器的信息。
- 每个 Sentinel 会以每两秒一次的频率, 通过发布与订阅功能, 向被它监视的所有主服务器和从服务器的 sentinel:hello 频道发送一条信息, 信息中包含了 Sentinel 的 IP 地址、端口号和运行 ID (runid)。
- 每个 Sentinel 都订阅了被它监视的所有主服务器和从服务器的 sentinel:hello 频道, 查找之前未出现过的 sentinel (looking for unknown sentinels)。 当一个 Sentinel 发现一个新的 Sentinel 时, 它会将新的 Sentinel 添加到一个列表中, 这个列表保存了 Sentinel 已知的, 监视同一个主服务器的所有其他 Sentinel 。
- Sentinel 发送的信息中还包括完整的主服务器当前配置(configuration)。 如果一个 Sentinel 包含的主服务器配置比另一个 Sentinel 发送的配置要旧, 那么这个 Sentinel 会立即升级到新配置上。
- 在将一个新 Sentinel 添加到监视主服务器的列表上面之前, Sentinel 会先检查列表中是否已经包含了和要添加的 Sentinel 拥有相同运行 ID 或者相同地址(包括 IP 地址和端口号)的 Sentinel , 如果是的话, Sentinel 会先移除列表中已有的那些拥有相同运行 ID 或者相同地址的 Sentinel , 然后再添加新 Sentinel 。
Sentinel API
默认端口: 26379
与 Sentinel
进行通信的方式:
- 第一种方法是通过直接发送命令来查询被监视 Redis 服务器的当前状态, 以及 Sentinel 所知道的关于其他 Sentinel 的信息, 诸如此类。
- 另一种方法是使用发布与订阅功能, 通过接收 Sentinel 发送的通知: 当执行故障转移操作, 或者某个被监视的服务器被判断为主观下线或者客观下线时, Sentinel 就会发送相应的信息。
Sentinel 命令
- PING :返回 PONG 。
- SENTINEL masters :列出所有被监视的主服务器,以及这些主服务器的当前状态。
- SENTINEL slaves :列出给定主服务器的所有从服务器,以及这些从服务器的当前状态。
- SENTINEL get-master-addr-by-name : 返回给定名字的主服务器的 IP 地址和端口号。 如果这个主服务器正在执行故障转移操作, 或者针对这个主服务器的故障转移操作已经完成, 那么这个命令返回新的主服务器的 IP 地址和端口号。
- SENTINEL reset : 重置所有名字和给定模式 pattern 相匹配的主服务器。 pattern 参数是一个 Glob 风格的模式。 重置操作清楚主服务器目前的所有状态, 包括正在执行中的故障转移, 并移除目前已经发现和关联的, 主服务器的所有从服务器和 Sentinel 。
- SENTINEL failover : 当主服务器失效时, 在不询问其他 Sentinel 意见的情况下, 强制开始一次自动故障迁移 (不过发起故障转移的 Sentinel 会向其他 Sentinel 发送一个新的配置,其他 Sentinel 会根据这个配置进行相应的更新)。