什么是Redis

Redis 是用 C 语言开发的一个开源的高性能键值对(key-value)数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止 Redis 支持的键值数据类型如下:

  • 字符串类型
  • 散列类型
  • 列表类型
  • 集合类型
  • 有序集合类型

Redis的使用场景

  • 缓存(数据查询、短连接、新闻内容、商品内容等等)
  • 分布式集群架构中的 session 分离
  • 聊天室的在线好友列表
  • 任务队列(秒杀、抢购、12306 等等)
  • 应用排行榜
  • 网站访问统计
  • 数据过期处理(可以精确到毫秒)

Redis高可用概述

Web 服务器中,高可用 是指服务器可以 正常访问 的时间,衡量的标准是在 多长时间 内可以提供正常服务(99.9%99.99%99.999% 等等)。在 Redis 层面,高可用 的含义要宽泛一些,除了保证提供 正常服务(如 主从分离快速容灾技术 等),还需要考虑 数据容量扩展数据安全 等等。

Redis 中,实现 高可用 的技术主要包括 持久化复制哨兵集群,下面简单说明它们的作用,以及解决了什么样的问题:

  • 持久化:持久化是 最简单的 高可用方法。它的主要作用是 数据备份,即将数据存储在 硬盘,保证数据不会因进程退出而丢失。
  • 复制:复制是高可用 Redis 的基础,哨兵集群 都是在 复制基础 上实现高可用的。复制主要实现了数据的多机备份以及对于读操作的负载均衡和简单的故障恢复。缺陷是故障恢复无法自动化、写操作无法负载均衡、存储能力受到单机的限制。
  • 哨兵:在复制的基础上,哨兵实现了 自动化故障恢复。缺陷是 写操作 无法 负载均衡存储能力 受到 单机 的限制。
  • 集群:通过集群,Redis 解决了 写操作 无法 负载均衡 以及 存储能力 受到 单机限制 的问题,实现了较为 完善高可用方案

Redis 的安装

  1. 去官网下载redis源文件 ,上传到Linux服务器上 , 解压到安装目录
  2. 进入到安装目录执行make命令编译源文件
  3. 执行命令make install 安装配置redis环境变量

启动Redis

前台启动方式

  1. redis-server

后台启动方式

redis-server &

查看redis进程 ps -ef | grep redis

[root@lemon ~]# ps -ef | grep redis
root      11719   7346  0 19:47 pts/0    00:00:00 redis-server *:6379
root      11724   7346  0 19:48 pts/0    00:00:00 grep --color=auto redis

使用redis的命令行客户端连接redis服务器redis-cli

[root@lemon ~]# redis-cli
127.0.0.1:6379> set name xiejinchi
OK
127.0.0.1:6379> get name
"xiejinchi"

关闭Redis

  • 关闭方式一 : 使用redis客户端向服务器发送命令关闭 (推荐使用)
redis-cli shutdown
  • 关闭方式二 : 先用 命令ps -ef | grep redis查出进程号, 再用 kill pid关闭
[root@lemon ~]# ps -ef | grep redis
root      11798   7346  0 20:06 pts/0    00:00:00 redis-server *:6379
root      11803   7346  0 20:06 pts/0    00:00:00 grep --color=auto redis
[root@lemon ~]# kill 11798
[root@lemon ~]# 11798:signal-handler (1560082112) Received SIGTERM scheduling shutdown...
11798:M 09 Jun 2019 20:08:32.240 # User requested shutdown...
11798:M 09 Jun 2019 20:08:32.240 * Saving the final RDB snapshot before exiting.
11798:M 09 Jun 2019 20:08:32.241 * DB saved on disk
11798:M 09 Jun 2019 20:08:32.241 # Redis is now ready to exit, bye bye...

Redis基本操作命令

1) 沟通命令,查看状态

输入ping , 返回PONG表示redis服务正常运行

127.0.0.1:6379> ping
PONG

2) 查看当前数据库中 key 的数目 : dbsize

127.0.0.1:6379> set name xiejinchi
OK
127.0.0.1:6379> dbsize
(integer) 1

3) Reids默认使用16个库

Redis 默认使用16个库,从0到15. 对数据库个数的修改 , 在redis.conf 文件中

# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16

4) 切换库

默认是使用第0个库, 可切换到其他库 select index

127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]>

5) 清空当前库中的数据

flushdb将当前库中的数据全部删除

127.0.0.1:6379> flushdb
OK

6) 关闭当前redis连接

redis自带的客户端退出当前redis连接 : exit 或 quit

127.0.0.1:6379> exit
[root@lemon ~]#

Key的操作命令

1) keys

语法: keys pattern

作用: 查找所有符合模式pattern的key , pattern可以使用通配符.

通配符 ;

    • : 表示0~多个字符, 例如: keys * 查询所有的key .
  1. ? : 表示单个字符, 例如 : wo?d ,匹配word , wood

2) exists

语法 : exists key[…]

作用 : 判断 key 是否存在

返回值 : 整数 , 存在key返回1 , 其他返回0 , 使用多个 key ,返回存在 key 的数量

3) expire

语法 : expire key seconds

作用 : 设置key的存活时间 , 超过时间的 key 自动删除 , 单位是秒

返回值 : 设置成功返回数字 1, 其他情况是 0

4) ttl

语法 : ttl key

作用 : 以秒为单位 , 返回key 的剩余生存时间(ttl : time to live)

返回值 :

  1. -1 : 没有设置 key 的生存时间 , key 永不过期.
  2. -2 : key 不存在
  3. 数字 : key的剩余时间 , 秒为单位

5) type

语法 : type key

作用 : 查看 key 所存储值得数据类型

返回值 : 字符串表示数据类型

  1. none (key不存在)
  2. string(字符串)
  3. list (列表)
  4. set (集合)
  5. zset (有序集)
  6. hsh(哈希表)

6) del

语法 : del key [key…]

作用 : 删除存在得 key , 不存在的 key 忽略 .

返回值 : 数字, 删除 的 key 的数量

Redis 的五种数据类型

1) 字符串 : String

字符串类型是redis 中最基本的数据类型 , 它能存储任何形式的字符串 , 包括二进制数据, 序列化后的数据 , JSON化的对象甚至是一张图片 , 最大512M

2) 哈希 hash

redis hash 是一个String类型的field 和 value 的映射表 , hash特别适合用于存储对象.

3) 列表 list

Redis 列表是简单的字符串列表 , 按照插入的顺序排序 , 你可以添加一个元素到列表的头部(左边) 或者尾部 (右边)

4) 集合 set

Redis 的set 是String类型的无序集合 , 集合成员是唯一的, 即集合中不能出现重复的数据

5) 有序集合 zset

redis 有序集合zset 和集合 set 一样也是String类型元素的集合, 且不允许重复的成员 . 不同的是 zset 的每个元素都会关联一个分数 ( 分数可以重复) , redis 通过分数来为集合中的成员进行从小到大的排序 .

数据类型操作

1) 操作字符串类型

基本命令

  1. set

将字符串值 value 设置到 key 中

127.0.0.1:6379> set name xiejinchi
OK
  1. get key 查询 key 对应的 value
127.0.0.1:6379> get name
"xiejinchi"
  1. incr

将 key 中储存的数字值加 1 ,如果key 不存在则创建 , 初始值为 0 , 再执行incr 操作 (只对数字类型的数据操作) , 原子性的操作 , 是线程安全的

127.0.0.1:6379> set i 2
OK
127.0.0.1:6379> incr i
(integer) 3
  1. decr

将key中存储的数字值减1 , 如果不存在则创建 ,初始值为 0 ,再执行 decr 操作 (只对数字类型的数据操作)

127.0.0.1:6379> decr de
(integer) -1
  1. append

语法 : append key value
说明: 如果 key 存在, 则将 value 追加到 key 原来旧值的末尾 , 如果 key 不存在 , 则将 key 设置为value

返回值 : 返回追加后的字符长度

常用命令

  1. strlen

语法 : strlen key

说明 : 返回 key 所储存的字符串长度

返回值 : 如果 key 存在 ,返回字符的长度 , 如果不存在返回0

  1. getrange

语法 : getrange key start end

作用 : 获取 key 中字符串值从start开始 到 end 结束 的子字符串 , 包括start 和end , 负数 表示从字符串的末尾开始 , -1表示最后一个字符

返回值 : 截取的子字符串 .

  1. setrange

语法 : setrange key offset value

说明 : 用value 覆盖(替换) key 的存储的值从offset开始 , 不存在的key 做空白字符串.

127.0.0.1:6379> set hello helloword
OK
127.0.0.1:6379> setrange hello 8 ld
(integer) 10
127.0.0.1:6379> get hello
"helloworld"
  1. mset

语法 : mset key value [key value…]

说明 : 同时设置一个或多个 key-value 对

返回值 : ok

127.0.0.1:6379> mset k1 v1 k2 v2
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k2
"v2"
  1. mget

语法 : mget key [key…]

作用 : 获取所有(一个或多个)给定 key 的值

返回值 : 包含所有key 对应的列表

2) 操作哈希类型

redis hash 是一个String类型的field 和value 的映射表. hash 特别合适用于存储对象.

基本命令

  1. hset

语法 : hset hash 表的key field value

作用 : 将哈希表key 中的域 field 的值设置为value , 如果key不存在, 则新建hash表, 再进行赋值, 如果有 field ,则覆盖值

返回值: 如果field是hash表中新field,且赋值成功 ,返回 1 . 如果field已经存在, 旧值被新值覆盖, 返回0

  1. hget

语法 : hget key field

作用 : 获取哈希表 key 中给定域field 的值

返回值 : field 域的值, 如果key 不存在或者field不存在返回nil

  1. hmset

语法 : hmset key field value [filed value…]

说明 : 同时将多个 filed-value(域-值) 设置到哈希表 key中, 此命令会覆盖已经存在的field, 如果hash 表 key 不存在 , 创建空的hash 表, 执行 hmset .

返回值 : 设置成功返回ok ,如果失败返回一个错误

  1. hmget

语法 : hmget key field [field…]

作用 : 获取哈希表 key 中一个或多个给定域的值

返回值 : 返回和field顺序对应的值 , 如果field不存在 , 返回nil

127.0.0.1:6379> hmget adriss imooc baidu
1) "www.imooc.com"
2) "www.baidu.com"
  1. hgetall

语法 : hgetall key

作用 : 获取哈希表 kye 中所有的域和值

返回值: 以列表形式返回hash中域和域的值 , key 值不存在, 返回空hash

127.0.0.1:6379> hgetall adriss
1) "imooc"
2) "www.imooc.com"
3) "baidu"
4) "www.baidu.com"
  1. hdel

语法 : hdel key field [field…]

作用 : 删除哈希表 key 中的一个或多个指定域field , 不存在field直接忽略

返回值 : 成功删除的field的数量

常用命令

  1. hkeys

语法 : hkeys key

作用 : 查看哈希表 key 中的所有filed 域

返回值 : 包含所有 field 的列表, key 不存在返回空列表

  1. hvals key

语法 : hvals key

作用 : 返回哈希表中所有域的值

返回值 : 包含哈希表所有域值得列表 , key 不存在返回空列表

  1. hexists

语法 : hexists key field

作用 : 查看哈希表 key 中, 给定 域 field 是否存在

返回值 : 如果field存在, 返回1 ,其他返回 0

3) 列表 list

redis 列表是简单得字符串列表 , 按照插入顺序排序 . 你可以添加一个元素列表的头部(左边)或者尾部(右边)

基本命令

  1. lpush

语法 : lpush key value [value …]

作用: 将一个或多个值 value 插入到列表 key 的表头(最左边) , 从左边开始加入值,从左到右的顺序依次插入到表头

返回值 : 数字,新列表的长度

  1. rpush

和 lpush 相对应

  1. lrange

语法 : lrange key start stop

作用 : 获取列表 key 中指定区间内的元素 , 0 表示列表的第一个元素, 以 1 表示列表的第二个元素; start, stop 是列表的下标值, 也可以负数的下标, -1表示列表的最后一个元素 ,-2 表示列表的倒数第二个元素, 以此类推 . start, stop 超出列表的范围不会出现错误.

返回值 : 指定区间的列表

  1. lindex

语法 : lindex key index

作用: 获取列表 key 中下标为指定 index 的元素 , 列表元素不删除, 只是查询, 0表示列表的第一个元素, -1表示列表的最后一个元素

返回值 : 指定下标的元素 ; index不在列表范围, 返回nil

  1. llen

语法 : llen key

作用 : 获取列表 key 的长度

返回值: 数值, 列表的长度; key 不存在返回 0

常用命令

  1. lrem

语法 : lrem key count value

作用 : 根据参数 count 的值 , 移除 列表中与参数 value相等的元素. count>0 , 从列表的左侧向右开始移除; count < 0 从列表的尾部开始移除; count=0 移除表中所有与value 相等的值

  1. lset

语法 : lset key index value

作用 : 将列表key 下标为 index 的元素的值设置为value

返回值 : 设置成功返回 ok , key不存在或者 index 超出范围返回错误信息

  1. linsert

语法 : linsert key Before|Alfter pivot value

作用 : 将值 value 插入到列表 key 当中位于 pivot之前或之后的位置 . key 不存在, pivot不存在列表中, 不执行任何操作

返回值 : 命令执行成功, 返回新列表的长度. 没有找到pivot 返回 -1 , key不存在返回 0

4) 集合类型 set

redis 的 set 的String类型的无序集合, 集合成员是唯一的, 即集合中不能出现重复的数据

基本命令

  1. sadd

语法 : sadd key member [member…]

作用 : 将一个或多个 member 元素加入到集合 key 当中 , 已经存在于集合的member元素将被忽略 ,不会再添加.

返回值 : 加入到集合的新元素的个数 . 不包括被忽略的元素.

  1. smembers

语法 : smembers key

作用 : 获取集合 key 中的所有成员元素, 不存在的 key 视为空集合

  1. sismember

语法 : sismember key member

作用 : 判断member 元素是否是集合 key 的成员

返回值 : member 是集合成员返回1, 其他返回 0

  1. scard

语法 : scard key

作用 : 获取集合里面的元素个数

返回值 : 数字, key 的元素个数 . 其他情况返回 0

  1. srem

语法 : srem key member [member…]

作用 : 删除集合 key 中的一个或多个 member 元素 , 不存在的元素被忽略 ;

返回值 : 数字,成功删除的元素个数 , 不包含忽略的元素

常用命令

  1. srandmember

语法 : srandmember key [count]

作用 : 只提供key , 随机返回集合中一个元素 , 元素不删除,依然再集合中 ; 提供了count 整正数, 返回包含count 个数元素的集合, 集合元素各不相同 ; count 是负数 ,返回一个count 绝对值的长度的集合 ,集合中元素可能会重复多次

返回值 : 一个元素 , 多个元素的集合

  1. spop

语法 : spop key [count]

作用 : 随机从集合中删除一个元素, count 是删除元素的个数.

返回值 : 被删除的元素, key 不存在或空集合返回nil

5) 有序集合 zset

redis 有序集合zset 和集合 set 一样也是String类型元素的集合, 且不允许重复的成员 . 不同的是 zset 的每个元素都会关联一个分数 ( 分数可以重复) , redis 通过分数来为集合中的成员进行从小到大的排序 .

基本命令

  1. zadd

语法 : zadd key score member [score member…]

作用 : 将一个或多个member元素及其score 值加入到有序集合key 中, 如果member 存在集合中, 则更新值; score 可以是整数或浮点数;

返回值 : 数字 ,新添加的元素个数

  1. zrange

语法 : zrange key start stop [Withscores]

作用 : 查询有序集合 , 指定区间内的元素. 集合成员按score值从小到大来排序. start 和stop都是从0 开始 . 0是第一个元素 , -1 是最后一个元素 , with scores选项让score 和value一同返回.

返回值 : 自定区间的成员集合

  1. zrevrange

语法 : zrevrange key start stop [Withscores]

作用 : 返回有序集 key 中, 指定区间内的成员. 其中 成员的位置按score 值递减(从大到小)来排序 . 其他同zrange命令.

返回值 : 指定区间的成员集合

  1. zrem

语法 : zrem key member [member …]

作用 : 删除有序集合 key 中的一个或多个成员 , 不存在的成员被忽略

返回值 : 被成功删除的成员数量 , 不包括被忽略的成员 .

  1. zcard

语法 : zcard key

作用 : 获取有序集 key 的元素成员的个数

返回值哦 : key 存在返回集合元素个数

常用命令

  1. zreangebyscore

语法 : zrangebyscore key min max [Withscores] [limit offset count]

作用 : 获取有序集 key 中 , 所有score值价于min 和max 之间 (包括min 和 max )的成员,有序成员是按递增(从小到大)排序. min , max 是包括在内 , 使用符号 ‘ ( ‘ 表示不包括 . min , max 可以使用 -inf , +inf 表示最大和最小 limit 用来限制返回结果的数量和区间 , Withscores用来显示 score和 value

返回值 : 指定区间的集合数据

2 . zrevreangebyscore

语法 : zrevrangebyscore key max min [Withscores] [limit offset count]

作用 : 和 zreangebyscore 一样 ,只是按大到小排序

  1. zcount

语法 : zcount key min max

作用 : 返回有序集 key 中, score 值再 min 和 max 之间(默认包括score 值等于min 或max的成员数量

Redis 事务

1) multi

语法 : multi

作用 : 标记一个事务的开始 . 事务内的多条命令会按照先后顺序被放进一个队列当中 .

返回值 : 总是返回 ok

2) exec

语法 : exec

作用 : 执行所有事务块内的命令

返回值 : 事务内的所有执行语句内容, 事务被打断(影响) 返回nil

3) discard

语法 : discard

作用 : 取消事务 , 放弃执行事务块内的所有命令

返回值 : 总是返回 ok

数据持久化

Redis 对数据持久化有两种方式

RDB 方式

什么是 RDB 方式

Redis Database (RDB) , 就是在指定的时间间隔内将内存中的数据集快照写入磁盘 , 数据恢复时将快照文件直接再读到内存,

RDB 保存了在某个时间点的数据集 (全部数据) 存储在一个二进制文件中 , 只有一个文件. 默认是 dump.rdb . RDB 技术非常适合做备份 , 可以保存最近一小时, 一天 , 一个月的全部数据. 保存数据是在单独的进程中写文件, 不影响redis 的正常使用 . RDB 恢复数据时比其他AOP速度快

如何实现 RDB

RDB方式的数据持久化 , 需要在redis.conf 文件中配置即可 ,默认配置是启用的 .

在配置文件redis.conf 中搜索 SNAPSHOTTING , 查找在注释开始和结束之间关于RDB的配置说明 . 配置 SNAPSHOTTING 地方有三处

  1. 配置执行RDB生成快照文件的时间策略 . 例如 : 让它在 ‘N’ 秒内数据集至少有 ‘M’ 个 key 改动时自动保存一次数据
save N M
  1. dbfilename: 设置RDB的文件名 ,默认文件名是 dump.rdb
  2. dir : 指定RDB 文件的存储位置 , 默认是 ./ 当前目录

AOF 方式

什么是AOF方式

Append-Only file (AOF), Redis每次收到一条改变数据的命令时 , 它将把该命令写到一个AOF文件中(只记录写操作, 读操作不记录) . 当Redis重启动时, 它通过执行AOF文件中所有的 命令来恢复数据

如何实现 AOF

AOF方式的数据持久化 , 仅需要在redis.conf文件中配置,

配置项 :

  1. appenddonly : 默认时no , 改成yes 开启了aof持久化
  2. appendfilename : 指定AOF 文件名 ,默认文件名为appendonly.aof
  3. dir : 指定RDB 和AOF文件的存放目录, 默认是 ./
  4. appendfsync : 配置向aof文件写命令数据的策略;
    1. no : 不主动进行同步操作 , 而是完全交由操作系统来做(每30一次) , 比较块但不是很安全 .
    2. always : 每执行写入都会执行同步 , 慢一些但比较安全.
    3. exerysec : 每秒执行一次同步操作, 比较平衡 , 介于速度和安全之间 , 这是默认项
  5. auto-aof-rewrite-min-size : 允许重重写的最小AOF文件大小, 默认64M , 当aof文件大于64M时, 开始整理aof文件, 去掉无用的操作命令,缩小aof文件.

悲观锁和乐观锁

在使用WATCH命令监控一个KEY后,当前队列中的命令会由于外部命令的执行而放弃,这是乐观锁的体现。

  • 悲观锁
    认为当前环境非常容易发生碰撞,所以执行操作前需要把数据锁定,操作完成后释放锁,其他操作才可以继续操作。
  • 乐观锁
    认为当前环境不容易发生碰撞,所以执行操作前不锁定数据,万一碰撞真的发生了,那么放弃自己的操作。

主从复制

Redis 主从复制 可将 主节点 数据同步给 从节点,从节点此时有两个作用:

  1. 一旦 主节点宕机从节点 作为 主节点备份 可以随时顶上来。
  2. 扩展 主节点读能力,分担主节点读压力。

主从复制 同时存在以下几个问题:

  1. 一旦 主节点宕机从节点 晋升成 主节点,同时需要修改 应用方主节点地址,还需要命令所有 从节点复制 新的主节点,整个过程需要 人工干预
  2. 主节点写能力 受到 单机的限制
  3. 主节点存储能力 受到 单机的限制
  4. 原生复制 的弊端在早期的版本中也会比较突出,比如:Redis 复制中断 后,从节点 会发起 psync。此时如果 同步不成功,则会进行 全量同步主库 执行 全量备份 的同时,可能会造成毫秒或秒级的 卡顿

主从复制的实现 :

方式 1 : 修改配置文件 , 启动时 , 服务器读取配置文件, 并自动成为指定服务器的从服务器 , 而构成主从复制的关系

实现步骤 : 模拟多个redis服务器, 在一台已安装redis的机器上 , 运行多个redis应用模拟多个redis服务器. 一个Master , 两个Slave.
  1. 复制三个redis.conf配置文件来启动三个redis服务,一个主服务器,两个从服务器
    主服务器的配置文件如下```shell

    先清空配置文件的内容

引入原配置文件

include /usr/local/redis/redis.conf

后台启动



daemonize yes<br />port 6380<br />pidfile /var/run/redis_6380.pid<br />logfile 6380.log<br />dbfilename dump6380.rdb

从服务器的配置文件redis6382.conf如下

#先清空配置文件的内容

#引入原配置文件
include /usr/local/redis/redis.conf
#后台启动
daemonize yes
port 6382
pidfile /var/run/redis_6382.pid
logfile 6380.log
dbfilename dump6382.rdb
#当前服务器是那台服务器的从服务器
slaveof 127.0.0.1 16380

从服务器的配置文件redis6384.confredis6382.conf就端口号不一样

  1. 使用三个配置文件启动三个redis服务,shell redis-server /redis/redis6380.conf redis-server /redis/redis6382.conf redis-server /redis/redis6383.conf

  2. 使用客户端连上服务器之后可查看redis的信息```shell

    连上服务器

    redis-server -p 6380

    查看信息

    info replication ```

注意: 主服务器可读可写,但主服务器主要是用来作写操作的, 从服务器用来作读操作 , 从服务器不可作写操作

方式 2 : ./redis-server --slaveof <master-IP><master-port>,在启动redis时指定当前服务成为某个主redis服务的从Slave

容灾处理

假如主服务器6380停掉了,就只剩下两个从服务器 , 那么就不能做写操作 , 我们可以将两个从服务器中的一个变为主服务器,然后再告诉另一个从服务器当前的主服务器是谁

  1. 将从服务器6382改为主服务器```shell

    连接6382

    slaveof no one ```

  2. 将从服务器6384的主服务器改为6382```shell

    连接6384,告诉主服务器的地址和端口

    slaveof 127.0.0.1 6382 ```

  3. 当原来的主服务器6380再次启动时,改为现在主服务器6382的从服务器shell slaveof 127.0.0.1 6382

Redis Sentinel(哨兵)

Redis Sentinel的主要功能

Sentinel 的主要功能包括 主节点存活检测主从运行情况检测自动故障转移failover)、主从切换RedisSentinel 最小配置是 一主一从

RedisSentinel 系统可以用来管理多个 Redis 服务器,该系统可以执行以下四个任务:

  • 监控

Sentinel 会不断的检查 主服务器从服务器 是否正常运行。

  • 通知

当被监控的某个 Redis 服务器出现问题,Sentinel 通过 API 脚本管理员 或者其他的 应用程序 发送通知。

  • 自动故障转移

主节点 不能正常工作时,Sentinel 会开始一次 自动的 故障转移操作,它会将与 失效主节点主从关系 的其中一个 从节点 升级为新的 主节点,并且将其他的 从节点 指向 新的主节点

  • 配置提供者

Redis Sentinel 模式下,客户端应用 在初始化时连接的是 Sentinel 节点集合,从中获取 主节点 的信息。

主观下线和客观下线

默认情况下,每个 Sentinel 节点会以 每秒一次 的频率对 Redis 节点和 其它Sentinel 节点发送 PING 命令,并通过节点的 回复 来判断节点是否在线。

  • 主观下线

主观下线 适用于所有 主节点从节点。如果在 down-after-milliseconds 毫秒内,Sentinel 没有收到 目标节点 的有效回复,则会判定 该节点主观下线

  • 客观下线

客观下线 只适用于 主节点。如果 主节点 出现故障,Sentinel 节点会通过 sentinel is-master-down-by-addr 命令,向其它 Sentinel 节点询问对该节点的 状态判断。如果超过 <quorum> 个数的节点判定 主节点 不可达,则该 Sentinel 节点会判断 主节点客观下线

Sentinel的通信命令

Sentinel 节点连接一个 Redis 实例的时候,会创建 cmdpub/sub 两个 连接Sentinel 通过 cmd 连接给 Redis 发送命令,通过 pub/sub 连接到 Redis 实例上的其他 Sentinel 实例。

SentinelRedis 主节点从节点 交互的命令,主要包括:

命令 作 用
PING SentinelRedis 节点发送 PING 命令,检查节点的状态
INFO SentinelRedis 节点发送 INFO 命令,获取它的 从节点信息
PUBLISH Sentinel 向其监控的 Redis 节点 __sentinel__:hello 这个 channel 发布 自己的信息主节点 相关的配置
SUBSCRIBE Sentinel 通过订阅 Redis 主节点从节点__sentinel__:hello 这个 channnel,获取正在监控相同服务的其他 Sentinel 节点

SentinelSentinel 交互的命令,主要包括:

命令 作 用
PING Sentinel 向其他 Sentinel 节点发送 PING 命令,检查节点的状态
SENTINEL:is-master-down-by-addr 和其他 Sentinel 协商 主节点 的状态,如果 主节点 处于 SDOWN 状态,则投票自动选出新的 主节点

Redis Sentinel的工作原理

每个 Sentinel 节点都需要 定期执行 以下任务:

  1. 每个 Sentinel每秒钟 一次的频率,向它所知的 主服务器从服务器 以及其他 Sentinel实例 发送一个 PING 命令。
  2. 如果一个 实例instance)距离 最后一次 有效回复 PING 命令的时间超过 down-after-milliseconds 所指定的值,那么这个实例会被 Sentinel 标记为 主观下线
  3. 如果一个 主服务器 被标记为 主观下线,那么正在 监视 这个 主服务器 的所有 Sentinel 节点,要以 每秒一次 的频率确认 主服务器 的确进入了 主观下线 状态。
  4. 如果一个 主服务器 被标记为 主观下线,并且有 足够数量Sentinel(至少要达到 配置文件 指定的数量)在指定的 时间范围 内同意这一判断,那么这个 主服务器 被标记为 客观下线
  5. 在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率,向它已知的所有 主服务器从服务器 发送 INFO 命令。当一个 主服务器Sentinel 标记为 客观下线 时,Sentinel下线主服务器 的所有 从服务器 发送 INFO 命令的频率,会从 10 秒一次改为 每秒一次
  6. Sentinel 和其他 Sentinel 协商 主节点 的状态,如果 主节点 处于 SDOWN 状态,则投票自动选出新的 主节点。将剩余的 从节点 指向 新的主节点 进行 数据复制
  7. 当没有足够数量的 Sentinel 同意 主服务器 下线时, 主服务器客观下线状态 就会被移除。当 主服务器 重新向 SentinelPING 命令返回 有效回复 时,主服务器主观下线状态 就会被移除。

注意:一个有效的 PING 回复可以是:+PONG-LOADING 或者 -MASTERDOWN。如果 服务器 返回除以上三种回复之外的其他回复,又或者在 指定时间 内没有回复 PING 命令, 那么 Sentinel 认为服务器返回的回复 无效non-valid)。

Redis Sentinel搭建

  1. 一个稳健的 Redis Sentinel 集群,应该使用至少 三个 Sentinel 实例,并且保证讲这些实例放到 不同的机器 上,甚至不同的 物理区域
  2. Sentinel 无法保证 强一致性
  3. 常见的 客户端应用库 都支持 Sentinel
  4. Sentinel 需要通过不断的 测试观察,才能保证高可用。

Redis Sentinel的配置文件

# 哨兵sentinel实例运行的端口,默认26379  
port 26379
# 哨兵sentinel的工作目录
dir ./

# 哨兵sentinel监控的redis主节点的 
## ip:主机ip地址
## port:哨兵端口号
## master-name:可以自己命名的主节点名字(只能由字母A-z、数字0-9 、这三个字符".-_"组成。)
## quorum:当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了  
# sentinel monitor <master-name> <ip> <redis-port> <quorum>  
sentinel monitor mymaster 127.0.0.1 6379 2

# 当在Redis实例中开启了requirepass <foobared>,所有连接Redis实例的客户端都要提供密码。
# sentinel auth-pass <master-name> <password>  
sentinel auth-pass mymaster 123456  

# 指定主节点应答哨兵sentinel的最大时间间隔,超过这个时间,哨兵主观上认为主节点下线,默认30秒  
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000  

# 指定了在发生failover主备切换时,最多可以有多少个slave同时对新的master进行同步。这个数字越小,完成failover所需的时间就越长;反之,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为1,来保证每次只有一个slave,处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1  

# 故障转移的超时时间failover-timeout,默认三分钟,可以用在以下这些方面:
## 1. 同一个sentinel对同一个master两次failover之间的间隔时间。  
## 2. 当一个slave从一个错误的master那里同步数据时开始,直到slave被纠正为从正确的master那里同步数据时结束。  
## 3. 当想要取消一个正在进行的failover时所需要的时间。
## 4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来同步数据了
# sentinel failover-timeout <master-name> <milliseconds>  
sentinel failover-timeout mymaster 180000

# 当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本。一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
# 对于脚本的运行结果有以下规则:  
## 1. 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10。
## 2. 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。  
## 3. 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
# sentinel notification-script <master-name> <script-path>  
sentinel notification-script mymaster /var/redis/notify.sh

# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

Redis Sentinel的节点规划

角色 IP地址 端口号
Redis Master 10.206.20.231 16379
Redis Slave1 10.206.20.231 26379
Redis Slave2 10.206.20.231 36379
Redis Sentinel1 10.206.20.231 16380
Redis Sentinel2 10.206.20.231 26380
Redis Sentinel3 10.206.20.231 36380

Redis Sentinel的配置搭建

Redis-Server的配置管理

分别拷贝三份 redis.conf 文件到 /usr/local/redis-sentinel 目录下面。三个配置文件分别对应 masterslave1slave2 三个 Redis 节点的 启动配置

$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-16379.conf
$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-26379.conf
$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-36379.conf

分别修改三份配置文件如下:

  • 主节点:redis-16379.conf
daemonize yes
pidfile /var/run/redis-16379.pid
logfile /var/log/redis/redis-16379.log
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-16379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
  • 从节点1:redis-26379.conf
daemonize yes
pidfile /var/run/redis-26379.pid
logfile /var/log/redis/redis-26379.log
port 26379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-26379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
slaveof 127.0.0.1 16379
  • 从节点2:redis-36379.conf
daemonize yes
pidfile /var/run/redis-36379.pid
logfile /var/log/redis/redis-36379.log
port 36379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-36379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
slaveof 127.0.0.1 16379

如果要做 自动故障转移,建议所有的 redis.conf 都设置 masterauth。因为 自动故障 只会重写 主从关系,即 slaveof,不会自动写入 masterauth。如果 Redis 原本没有设置密码,则可以忽略。

Redis-Server启动验证

按顺序分别启动 163792637936379 三个 Redis 节点,启动命令和启动日志如下:

Redis 的启动命令:

$ sudo redis-server /usr/local/redis-sentinel/redis-16379.conf
$ sudo redis-server /usr/local/redis-sentinel/redis-26379.conf
$ sudo redis-server /usr/local/redis-sentinel/redis-36379.conf

查看 Redis 的启动进程:

$ ps -ef | grep redis-server
    0  7127     1   0  2:16下午 ??         0:01.84 redis-server 0.0.0.0:16379 
    0  7133     1   0  2:16下午 ??         0:01.73 redis-server 0.0.0.0:26379 
    0  7137     1   0  2:16下午 ??         0:01.70 redis-server 0.0.0.0:36379

查看 Redis 的启动日志:

  • 节点 redis-16379
$ cat /var/log/redis/redis-16379.log 
7126:C 22 Aug 14:16:38.907 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7126:C 22 Aug 14:16:38.908 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7126, just started
7126:C 22 Aug 14:16:38.908 # Configuration loaded
7127:M 22 Aug 14:16:38.910 * Increased maximum number of open files to 10032 (it was originally set to 256).
7127:M 22 Aug 14:16:38.912 * Running mode=standalone, port=16379.
7127:M 22 Aug 14:16:38.913 # Server initialized
7127:M 22 Aug 14:16:38.913 * Ready to accept connections
7127:M 22 Aug 14:16:48.416 * Slave 127.0.0.1:26379 asks for synchronization
7127:M 22 Aug 14:16:48.416 * Full resync requested by slave 127.0.0.1:26379
7127:M 22 Aug 14:16:48.416 * Starting BGSAVE for SYNC with target: disk
7127:M 22 Aug 14:16:48.416 * Background saving started by pid 7134
7134:C 22 Aug 14:16:48.433 * DB saved on disk
7127:M 22 Aug 14:16:48.487 * Background saving terminated with success
7127:M 22 Aug 14:16:48.494 * Synchronization with slave 127.0.0.1:26379 succeeded
7127:M 22 Aug 14:16:51.848 * Slave 127.0.0.1:36379 asks for synchronization
7127:M 22 Aug 14:16:51.849 * Full resync requested by slave 127.0.0.1:36379
7127:M 22 Aug 14:16:51.849 * Starting BGSAVE for SYNC with target: disk
7127:M 22 Aug 14:16:51.850 * Background saving started by pid 7138
7138:C 22 Aug 14:16:51.862 * DB saved on disk
7127:M 22 Aug 14:16:51.919 * Background saving terminated with success
7127:M 22 Aug 14:16:51.923 * Synchronization with slave 127.0.0.1:36379 succeeded

以下两行日志日志表明,redis-16379 作为 Redis主节点redis-26379redis-36379 作为 从节点,从 主节点 同步数据。

7127:M 22 Aug 14:16:48.416 * Slave 127.0.0.1:26379 asks for synchronization
7127:M 22 Aug 14:16:51.848 * Slave 127.0.0.1:36379 asks for synchronization
复制代码
  • 节点 redis-26379
$ cat /var/log/redis/redis-26379.log 
7132:C 22 Aug 14:16:48.407 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7132:C 22 Aug 14:16:48.408 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7132, just started
7132:C 22 Aug 14:16:48.408 # Configuration loaded
7133:S 22 Aug 14:16:48.410 * Increased maximum number of open files to 10032 (it was originally set to 256).
7133:S 22 Aug 14:16:48.412 * Running mode=standalone, port=26379.
7133:S 22 Aug 14:16:48.413 # Server initialized
7133:S 22 Aug 14:16:48.413 * Ready to accept connections
7133:S 22 Aug 14:16:48.413 * Connecting to MASTER 127.0.0.1:16379
7133:S 22 Aug 14:16:48.413 * MASTER <-> SLAVE sync started
7133:S 22 Aug 14:16:48.414 * Non blocking connect for SYNC fired the event.
7133:S 22 Aug 14:16:48.414 * Master replied to PING, replication can continue...
7133:S 22 Aug 14:16:48.415 * Partial resynchronization not possible (no cached master)
7133:S 22 Aug 14:16:48.417 * Full resync from master: 211d3b4eceaa3af4fe5c77d22adf06e1218e0e7b:0
7133:S 22 Aug 14:16:48.494 * MASTER <-> SLAVE sync: receiving 176 bytes from master
7133:S 22 Aug 14:16:48.495 * MASTER <-> SLAVE sync: Flushing old data
7133:S 22 Aug 14:16:48.496 * MASTER <-> SLAVE sync: Loading DB in memory
7133:S 22 Aug 14:16:48.498 * MASTER <-> SLAVE sync: Finished with success
  • 节点 redis-36379
$ cat /var/log/redis/redis-36379.log 
7136:C 22 Aug 14:16:51.839 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7136:C 22 Aug 14:16:51.840 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7136, just started
7136:C 22 Aug 14:16:51.841 # Configuration loaded
7137:S 22 Aug 14:16:51.843 * Increased maximum number of open files to 10032 (it was originally set to 256).
7137:S 22 Aug 14:16:51.845 * Running mode=standalone, port=36379.
7137:S 22 Aug 14:16:51.845 # Server initialized
7137:S 22 Aug 14:16:51.846 * Ready to accept connections
7137:S 22 Aug 14:16:51.846 * Connecting to MASTER 127.0.0.1:16379
7137:S 22 Aug 14:16:51.847 * MASTER <-> SLAVE sync started
7137:S 22 Aug 14:16:51.847 * Non blocking connect for SYNC fired the event.
7137:S 22 Aug 14:16:51.847 * Master replied to PING, replication can continue...
7137:S 22 Aug 14:16:51.848 * Partial resynchronization not possible (no cached master)
7137:S 22 Aug 14:16:51.850 * Full resync from master: 211d3b4eceaa3af4fe5c77d22adf06e1218e0e7b:14
7137:S 22 Aug 14:16:51.923 * MASTER <-> SLAVE sync: receiving 176 bytes from master
7137:S 22 Aug 14:16:51.923 * MASTER <-> SLAVE sync: Flushing old data
7137:S 22 Aug 14:16:51.924 * MASTER <-> SLAVE sync: Loading DB in memory
7137:S 22 Aug 14:16:51.927 * MASTER <-> SLAVE sync: Finished with success

Sentinel的配置管理

分别拷贝三份 redis-sentinel.conf 文件到 /usr/local/redis-sentinel 目录下面。三个配置文件分别对应 masterslave1slave2 三个 Redis 节点的 哨兵配置

$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-16380.conf
$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-26380.conf
$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-36380.conf
  • 节点1:sentinel-16380.conf
protected-mode no
bind 0.0.0.0
port 16380
daemonize yes
sentinel monitor master 127.0.0.1 16379 2
sentinel down-after-milliseconds master 5000
sentinel failover-timeout master 180000
sentinel parallel-syncs master 1
sentinel auth-pass master 123456
logfile /var/log/redis/sentinel-16380.log
  • 节点2:sentinel-26380.conf
protected-mode no
bind 0.0.0.0
port 26380
daemonize yes
sentinel monitor master 127.0.0.1 16379 2
sentinel down-after-milliseconds master 5000
sentinel failover-timeout master 180000
sentinel parallel-syncs master 1
sentinel auth-pass master 123456
logfile /var/log/redis/sentinel-26380.log
  • 节点3:sentinel-36380.conf
protected-mode no
bind 0.0.0.0
port 36380
daemonize yes
sentinel monitor master 127.0.0.1 16379 2
sentinel down-after-milliseconds master 5000
sentinel failover-timeout master 180000
sentinel parallel-syncs master 1
sentinel auth-pass master 123456
logfile /var/log/redis/sentinel-36380.log

Sentinel启动验证

按顺序分别启动 163802638036380 三个 Sentinel 节点,启动命令和启动日志如下:

$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-16380.conf
$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-26380.conf
$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-36380.conf

查看 Sentinel 的启动进程:

$ ps -ef | grep redis-sentinel
    0  7954     1   0  3:30下午 ??         0:00.05 redis-sentinel 0.0.0.0:16380 [sentinel] 
    0  7957     1   0  3:30下午 ??         0:00.05 redis-sentinel 0.0.0.0:26380 [sentinel] 
    0  7960     1   0  3:30下午 ??         0:00.04 redis-sentinel 0.0.0.0:36380 [sentinel]

查看 Sentinel 的启动日志:

  • 节点 sentinel-16380
$ cat /var/log/redis/sentinel-16380.log 
7953:X 22 Aug 15:30:27.245 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7953:X 22 Aug 15:30:27.245 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7953, just started
7953:X 22 Aug 15:30:27.245 # Configuration loaded
7954:X 22 Aug 15:30:27.247 * Increased maximum number of open files to 10032 (it was originally set to 256).
7954:X 22 Aug 15:30:27.249 * Running mode=sentinel, port=16380.
7954:X 22 Aug 15:30:27.250 # Sentinel ID is 69d05b86a82102a8919231fd3c2d1f21ce86e000
7954:X 22 Aug 15:30:27.250 # +monitor master master 127.0.0.1 16379 quorum 2
7954:X 22 Aug 15:30:32.286 # +sdown sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
7954:X 22 Aug 15:30:34.588 # -sdown sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379

sentinel-16380 节点的 Sentinel ID69d05b86a82102a8919231fd3c2d1f21ce86e000,并通过 Sentinel ID 把自身加入 sentinel 集群中。

  • 节点 sentinel-26380
$ cat /var/log/redis/sentinel-26380.log 
7956:X 22 Aug 15:30:30.900 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7956:X 22 Aug 15:30:30.901 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7956, just started
7956:X 22 Aug 15:30:30.901 # Configuration loaded
7957:X 22 Aug 15:30:30.904 * Increased maximum number of open files to 10032 (it was originally set to 256).
7957:X 22 Aug 15:30:30.905 * Running mode=sentinel, port=26380.
7957:X 22 Aug 15:30:30.906 # Sentinel ID is 21e30244cda6a3d3f55200bcd904d0877574e506
7957:X 22 Aug 15:30:30.906 # +monitor master master 127.0.0.1 16379 quorum 2
7957:X 22 Aug 15:30:30.907 * +slave slave 127.0.0.1:26379 127.0.0.1 26379 @ master 127.0.0.1 16379
7957:X 22 Aug 15:30:30.911 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 16379
7957:X 22 Aug 15:30:36.311 * +sentinel sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379

sentinel-26380 节点的 Sentinel ID21e30244cda6a3d3f55200bcd904d0877574e506,并通过 Sentinel ID 把自身加入 sentinel 集群中。此时 sentinel 集群中已有 sentinel-16380sentinel-26380 两个节点。

  • 节点 sentinel-36380
$ cat /var/log/redis/sentinel-36380.log 
7959:X 22 Aug 15:30:34.273 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7959:X 22 Aug 15:30:34.274 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7959, just started
7959:X 22 Aug 15:30:34.274 # Configuration loaded
7960:X 22 Aug 15:30:34.276 * Increased maximum number of open files to 10032 (it was originally set to 256).
7960:X 22 Aug 15:30:34.277 * Running mode=sentinel, port=36380.
7960:X 22 Aug 15:30:34.278 # Sentinel ID is fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
7960:X 22 Aug 15:30:34.278 # +monitor master master 127.0.0.1 16379 quorum 2
7960:X 22 Aug 15:30:34.279 * +slave slave 127.0.0.1:26379 127.0.0.1 26379 @ master 127.0.0.1 16379
7960:X 22 Aug 15:30:34.283 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 16379
7960:X 22 Aug 15:30:34.993 * +sentinel sentinel 21e30244cda6a3d3f55200bcd904d0877574e506 127.0.0.1 26380 @ master 127.0.0.1 16379

sentinel-36380 节点的 Sentinel IDfd166dc66425dc1d9e2670e1f17cb94fe05f5fc7,并通过 Sentinel ID 把自身加入 sentinel 集群中。此时 sentinel 集群中已有 sentinel-16380sentinel-26380sentinel-36380 三个节点。

Sentinel配置刷新

  • 节点1:sentinel-16380.conf

sentinel-16380.conf 文件新生成如下的配置项:

# Generated by CONFIG REWRITE
dir "/usr/local/redis-sentinel"
sentinel config-epoch master 0
sentinel leader-epoch master 0
sentinel known-slave master 127.0.0.1 36379
sentinel known-slave master 127.0.0.1 26379
sentinel known-sentinel master 127.0.0.1 26380 21e30244cda6a3d3f55200bcd904d0877574e506
sentinel known-sentinel master 127.0.0.1 36380 fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
sentinel current-epoch 0

可以注意到,sentinel-16380.conf 刷新写入了 Redis 主节点关联的所有 从节点 redis-26379redis-36379,同时写入了其余两个 Sentinel 节点 sentinel-26380sentinel-36380IP 地址,端口号Sentinel ID

# Generated by CONFIG REWRITE
dir "/usr/local/redis-sentinel"
sentinel config-epoch master 0
sentinel leader-epoch master 0
sentinel known-slave master 127.0.0.1 26379
sentinel known-slave master 127.0.0.1 36379
sentinel known-sentinel master 127.0.0.1 36380 fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
sentinel known-sentinel master 127.0.0.1 16380 69d05b86a82102a8919231fd3c2d1f21ce86e000
sentinel current-epoch 0

可以注意到,sentinel-26380.conf 刷新写入了 Redis 主节点关联的所有 从节点 redis-26379redis-36379,同时写入了其余两个 Sentinel 节点 sentinel-36380sentinel-16380IP 地址,端口号Sentinel ID

# Generated by CONFIG REWRITE
dir "/usr/local/redis-sentinel"
sentinel config-epoch master 0
sentinel leader-epoch master 0
sentinel known-slave master 127.0.0.1 36379
sentinel known-slave master 127.0.0.1 26379
sentinel known-sentinel master 127.0.0.1 16380 69d05b86a82102a8919231fd3c2d1f21ce86e000
sentinel known-sentinel master 127.0.0.1 26380 21e30244cda6a3d3f55200bcd904d0877574e506
sentinel current-epoch 0

可以注意到,sentinel-36380.conf 刷新写入了 Redis 主节点关联的所有 从节点 redis-26379redis-36379,同时写入了其余两个 Sentinel 节点 sentinel-16380sentinel-26380IP 地址,端口号Sentinel ID

Sentinel时客户端命令

  • 检查其他 Sentinel 节点的状态,返回 PONG 为正常。
> PING sentinel
  • 显示被监控的所有 主节点 以及它们的状态。
> SENTINEL masters
  • 显示指定 主节点 的信息和状态。
> SENTINEL master <master_name>
  • 显示指定 主节点 的所有 从节点 以及它们的状态。
> SENTINEL slaves <master_name>

返回指定 主节点IP 地址和 端口。如果正在进行 failover 或者 failover 已经完成,将会显示被提升为 主节点从节点IP 地址和 端口

> SENTINEL get-master-addr-by-name <master_name>
  • 重置名字匹配该 正则表达式 的所有的 主节点 的状态信息,清除它之前的 状态信息,以及 从节点 的信息。
> SENTINEL reset <pattern>
  • 强制当前 Sentinel 节点执行 failover,并且不需要得到其他 Sentinel 节点的同意。但是 failover 后会将 最新的配置 发送给其他 Sentinel 节点。
SENTINEL failover <master_name>

Redis Sentinel故障切换与恢复

Redis CLI客户端跟踪

上面的日志显示,redis-16379 节点为 主节点,它的进程 ID7127。为了模拟 Redis 主节点故障,强制杀掉这个进程。

$ kill -9 7127

使用 redis-cli 客户端命令进入 sentinel-16380 节点,查看 Redis 节点 的状态信息。

$ redis-cli -p 16380
  • 查看 Redis 主从集群的 主节点 信息。可以发现 redis-26379 晋升为 新的主节点
127.0.0.1:16380> SENTINEL master master
 1) "name"
 2) "master"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "26379"
 7) "runid"
 8) "b8ca3b468a95d1be5efe1f50c50636cafe48c59f"
 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) "588"
19) "last-ping-reply"
20) "588"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "9913"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "663171"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "2"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "180000"
39) "parallel-syncs"
40) "1"

Redis Sentinel日志跟踪

查看任意 Sentinel 节点的日志如下:

7954:X 22 Aug 18:40:22.504 # +tilt #tilt mode entered
7954:X 22 Aug 18:40:32.197 # +tilt #tilt mode entered
7954:X 22 Aug 18:41:02.241 # -tilt #tilt mode exited
7954:X 22 Aug 18:48:24.550 # +sdown master master 127.0.0.1 16379
7954:X 22 Aug 18:48:24.647 # +new-epoch 1
7954:X 22 Aug 18:48:24.651 # +vote-for-leader fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 1
7954:X 22 Aug 18:48:25.678 # +odown master master 127.0.0.1 16379 #quorum 3/2
7954:X 22 Aug 18:48:25.678 # Next failover delay: I will not start a failover before Wed Aug 22 18:54:24 2018
7954:X 22 Aug 18:48:25.709 # +config-update-from sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
7954:X 22 Aug 18:48:25.710 # +switch-master master 127.0.0.1 16379 127.0.0.1 26379
7954:X 22 Aug 18:48:25.710 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 26379
7954:X 22 Aug 18:48:25.711 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
7954:X 22 Aug 18:48:30.738 # +sdown slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
7954:X 22 Aug 19:38:23.479 # -sdown slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
  • 分析日志,可以发现 redis-16329 节点先进入 sdown 主观下线 状态。
+sdown master master 127.0.0.1 16379
  • 哨兵检测到 redis-16329 出现故障,Sentinel 进入一个 新纪元,从 0 变为 1
+new-epoch 1
  • 三个 Sentinel 节点开始协商 主节点 的状态,判断其是否需要 客观下线
+vote-for-leader fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 1
  • 超过 quorum 个数的 Sentinel 节点认为 主节点 出现故障,redis-16329 节点进入 客观下线 状态。
+odown master master 127.0.0.1 16379 #quorum 3/2
  • Sentinal 进行 自动故障切换,协商选定 redis-26329 节点作为新的 主节点
+switch-master master 127.0.0.1 16379 127.0.0.1 26379
  • redis-36329 节点和已经 客观下线redis-16329 节点成为 redis-26479从节点
7954:X 22 Aug 18:48:25.710 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 26379
7954:X 22 Aug 18:48:25.711 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379

Redis的配置文件

分别查看三个 redis 节点的配置文件,发生 主从切换redis.conf 的配置会自动发生刷新。

  • 节点 redis-16379
daemonize yes
pidfile "/var/run/redis-16379.pid"
logfile "/var/log/redis/redis-16379.log"
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-16379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
  • 节点 redis-26379
daemonize yes
pidfile "/var/run/redis-26379.pid"
logfile "/var/log/redis/redis-26379.log"
port 26379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-26379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
  • 节点 redis-36379
daemonize yes
pidfile "/var/run/redis-36379.pid"
logfile "/var/log/redis/redis-36379.log"
port 36379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-36379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
slaveof 127.0.0.1 26379

分析redis-26379 节点 slaveof 配置被移除,晋升为 主节点redis-16379 节点处于 宕机状态redis-36379slaveof 配置更新为 127.0.0.1 redis-26379,成为 redis-26379从节点

重启节点 redis-16379。待正常启动后,再次查看它的 redis.conf 文件,配置如下:

daemonize yes
pidfile "/var/run/redis-16379.pid"
logfile "/var/log/redis/redis-16379.log"
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-16379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
# Generated by CONFIG REWRITE
slaveof 127.0.0.1 26379

节点 redis-16379 的配置文件新增一行 slaveof 配置属性,指向 redis-26379,即成为 新的主节点从节点

Jedis操作Redis

首先 , redis安装完之后 , 默认只能本机连接 , 如果要通过网络连接 要修改redis.conf配置文件 , 再关闭Linux的防火墙或让redis的端口号通过防火墙

  1. 修改redis.conf配置文件
    1. bind ip 注释此行
    2. protected-mode yes 保护模式改为 no
  2. 关闭防火墙

查看防火墙状态 systemctl status firewalld

[root@lemon ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: active (running) since 五 2019-06-14 11:20:13 CST; 20min ago
     Docs: man:firewalld(1)
 Main PID: 6233 (firewalld)
   CGroup: /system.slice/firewalld.service
           └─6233 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid

6月 14 11:20:11 lemon systemd[1]: Starting firewalld - dynamic firewall daemon...
6月 14 11:20:13 lemon systemd[1]: Started firewalld - dynamic firewall daemon.

关闭防火墙 systemctl stop firewalld.service

[root@lemon ~]# systemctl stop firewalld.service
[root@lemon ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since 五 2019-06-14 11:44:30 CST; 21s ago
     Docs: man:firewalld(1)
  Process: 6233 ExecStart=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS (code=exited, status=0/SUCCESS)
 Main PID: 6233 (code=exited, status=0/SUCCESS)

6月 14 11:20:11 lemon systemd[1]: Starting firewalld - dynamic firewall daemon...
6月 14 11:20:13 lemon systemd[1]: Started firewalld - dynamic firewall daemon.
6月 14 11:44:30 lemon systemd[1]: Stopping firewalld - dynamic firewall daemon...
6月 14 11:44:30 lemon systemd[1]: Stopped firewalld - dynamic firewall daemon.

示例 :

导入Jedis

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.0.1</version>
</dependency>
public class RedisString {

    public static void main(String[] args) {

        // 连接地址
        String host = "192.168.112.129";
        // 端口号
        int port = 6379;

        Jedis jedis = new Jedis(host, port);
        // 设置密码
        jedis.auth("123456");

        jedis.set("name", "谢金池");
        String name = jedis.get("name");

        System.out.println(name);
    }
}

jedis连接池 JedisPool

pom.xml

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.2</version>
</dependency>

关于Redis的文章

作者:零壹技术栈链接:https://juejin.im/post/5b76e732f265da4376203849来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Docker 搭建 Redis 集群

搭建一主两从环境,docker-compose.yml 配置如下:

version: '3.1'
services:
  master:
    image: redis
    container_name: redis-master
    ports:
      - 6379:6379

  slave1:
    image: redis
    container_name: redis-slave-1
    ports:
      - 6380:6379
    command: redis-server --slaveof redis-master 6379

  slave2:
    image: redis
    container_name: redis-slave-2
    ports:
      - 6381:6379
    command: redis-server --slaveof redis-master 6379

搭建 Sentinel 集群

我们至少需要创建三个 Sentinel 服务,docker-compose.yml 配置如下:

version: '3.1'
services:
  sentinel1:
    image: redis
    container_name: redis-sentinel-1
    ports:
      - 26379:26379
    command: redis-sentinel /usr/local/etc/redis/sentinel.conf
    volumes:
      - ./sentinel1.conf:/usr/local/etc/redis/sentinel.conf

  sentinel2:
    image: redis
    container_name: redis-sentinel-2
    ports:
      - 26380:26379
    command: redis-sentinel /usr/local/etc/redis/sentinel.conf
    volumes:
      - ./sentinel2.conf:/usr/local/etc/redis/sentinel.conf

  sentinel3:
    image: redis
    container_name: redis-sentinel-3
    ports:
      - 26381:26379
    command: redis-sentinel /usr/local/etc/redis/sentinel.conf
    volumes:
      - ./sentinel3.conf:/usr/local/etc/redis/sentinel.conf

修改 Sentinel 配置文件

需要三份 sentinel.conf 配置文件,分别为 sentinel1.confsentinel2.confsentinel3.conf,配置文件内容相同

port 26379
dir /tmp
# 自定义集群名,其中 127.0.0.1 为 redis-master 的 ip,6379 为 redis-master 的端口,2 为最小投票数(因为有 3 台 Sentinel 所以可以设置成 2)
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes

查看集群是否生效

进入 Sentinel 容器,使用 Sentinel API 查看监控情况:

docker exec -it redis-sentinel-1 /bin/bash
redis-cli -p 26379
sentinel master mymaster
sentinel slaves mymaster