Redis的单机模式、主从模式、哨兵模式

一、单机模式

1. 单机模式的安装方式

如果是CentOS,那么安装就很简单了,可以通过命令yum install redis -y进行安装,如下图所示:

image.png

然后可以通过whereis redis-server查看redis安装在哪里;安装完在/etc目录下有redis.conf文件,这是一个生成的配置文件。
image.png

接下来通过一个简单的配置之后就启动Redis。vim /etc/redis.conf

# 绑定的网卡IP,这个默认只能通过127.0.0.1访问,如果要公网可以访问,则需要绑定公网的IP,或者bind 0.0.0.0;但不推荐将redis绑定公网IP,也不推荐设置root用户启动;一般绑定为程序可以访问的内网IP就可以了
bind 127.0.0.1
# 端口号,如果公网可以访问呢,不建议设置为默认的6379
port 6379
# 日志文件
logfile /data/redis/logs/redis-6379.log
# 数据文件存放的文件夹
dir /data/redis/data
# 数据文件名
dbfilename dump.rdb
# 是否为守护进行启动
daemonize yes

简单的配置完这些,就可以启动redis了。因为是yum安装的,可以直接通过下面的命令进行启动:

redis-server /etc/redis.conf

因为是守护进程的启动,所以不会有redis的那个图标什么的。如果没有使用守护进程,需要后台启动,可以在启动命令后面加上&来启动。启动完了可以通过ps -ef|grep redis来查看进程是否存在。

那可能会有不是CentOS的系统,或者不使用yum安装的,那可以通过下载包来进行安装。
可以到https://redis.io/download官网上,下载下来上传到主机上(有的主机外网不能访问),或者直接在主机上下载。在官网上有对应的教程。

2. 单机模式在SpringBoot中的使用

单机模式在SpringBoot中的使用就非常简单了,只需要在配置文件里配置上redis的地址、端口,有密码就配置上密码就可以进行连接了。连接之后可以通过StringRedisTemplate或者RedisTemplate进行访问。注意如果用的是Linux主机,本地测试的时候需要注意bind的IP地址,以及防火墙是不是放开了对应的端口(云主机需要去查看配置安全策略)。

spring.redis.host=48.106.128.154
spring.redis.port=6379
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Test
    public void test00() {
        stringRedisTemplate.opsForValue().set("username", "hugh");
    }

二、主从复制模式

1. 主从模式配置

单节点可能会遇到节点挂掉的情况,那么就没法用了,尤其是redis里面有数据的情况,要找回数据需要通过redis的持久化来找回数据(持久化再写一个文来说明持久化机制)。所以可以通过主从配置来实现一个备份(当然这有缺点,待会说)。
主从配置其实很简单,就是启动两个redis,配置个主从,可以是一主一从,也可以是一主多从的结构模型。因为线上环境都是有密码的,所以模拟一个有密码的主从模式。

配置文件和上面的单机模式的配置几乎是一样的,只是要改端口、数据文件几个内容,简要配置如下:

主配置:redis-6380.conf
bind 0.0.0.0
port 6380
logfile /data/redis/logs/redis-6380.log
dir /data/redis/data/6380
dbfilename dump.rdb
daemonize yes
# 线程
pidfile /var/run/redis_6380.pid
# 密码
requirepass 123456

从配置1: redis-6381.conf
bind 0.0.0.0
port 6381
logfile /data/redis/logs/redis-6381.log
dir /data/redis/data/6381
dbfilename dump.rdb
daemonize yes
pidfile /var/run/redis_6381.pid
requirepass 123456
# 设置主节点和主节点密码
slaveof 127.0.0.1 6380
masterauth 123456
# 从节点只读
slave-read-only yes

从配置2: redis-6382.conf
bind 0.0.0.0
port 6382
logfile /data/redis/logs/redis-6382.log
dir /data/redis/data/6382
dbfilename dump.rdb
daemonize yes
pidfile /var/run/redis_6382.pid
requirepass 123456
# 设置主节点和主节点密码
slaveof 127.0.0.1 6380
masterauth 123456
# 从节点只读
slave-read-only yes

然后分别通过命令

redis-server /etc/redis/redis-6380.conf 
redis-server /etc/redis/redis-6381.conf 
redis-server /etc/redis/redis-6382.conf

分别启动redis。因为我是在同一台机子上做的,如果在多台机子上做,需要修改设置主节点的IP,这里要注意安全策略的配置,可能端口连不上什么的。如果是不同的机子,那么多台redis的端口设置成一样也是可以的。
启动之后用redis-cli连接一台,去查看状态

# 连接
redis-cli -h 127.0.0.1 -p 6030
# 密码验证
auth 123456
# 查看状态
info replication

# 显示的内容
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=996,lag=0
slave1:ip=127.0.0.1,port=6382,state=online,offset=996,lag=0
master_repl_offset:996
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:995

可以看到当前是角色是master,还有两个slave。这时候在master节点存储数据后,会同步到slave节点,但是不会从slave节点同步到master节点。

2. 主从模式切换

主从模式有哪些好处或者说使用场景呢?

  1. 我们可以实现读写分写,写数据都是往主节点去写入,读数据在从节点读取。但是这必须要接受网络延迟或者一些其他原因可能导致的主从之间数据的延迟;
  2. 读写都是主节点,从节点用于主节点发生故障的时候的备用节点。那么这时候问题就来了,主从模式需要我们自己去切换主从节点的角色,还要手动的修改配置文件。
    这合理吗?这肯定不合理。有这个时间修改,可能重启redis,持久化文件恢复都做好了(当然有一种情况是主机直接坏掉了,或者这台主机的网络直接不通了,这时候切到从节点还是有用的)
    那就简单说一下怎么切换节点吧。
    现在我们假设主节点的机器直接坏掉了,也就是我们直接kill掉主节点的线程, 这时候到从节点上info replication查看状态,发现会显示主节点已经down了:
    image.png

    切换主节点步骤:
#1. 断开复制
slaveof no one
#2. 从节点晋升为主节点(断开复制后就自己切换为master了)
#3. 切换其他从节点的主节点
redis-cli -h 127.0.0.1 -p 6382
auth 123456
slaveof 127.0.0.1 6381

说了上面的主从复制,其实问题很多,比如手动的切换啊,还要去改程序啊等等,所以就有了哨兵模式,哨兵模式能实现帮我们自己察觉哪个节点挂了,并切换主节点。

三、哨兵模式(Redis Sentinel)

当主节点出现故障时,Redis Sentinel能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。
Redis Sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis 数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当 它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还 会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可 达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会 将这个变化实时通知给Redis应用方。

Redis Sentinel与主从复制模式只是多了若干Sentinel节点,并没有对Redis节点做特殊处理

1. 几个名词

主节点(master): 一个redis服务,独立进程
从节点(slave):redis服务,独立进程
Redis数据节点: 主节点和从节点
Sentinel节点:监控Redis的数据节点
Sentinel节点集合: 若干个Sentinel节点的抽象组合

2. 哨兵模式的部署方式
2.1 部署Redis节点

Redis节点的部署和主从模式是一样的,可以参照上面的主从复制部署,同样是部署一个主节点和两个从节点。

2.2 部署Sentinel节点

Sentinel节点的配置文件都类似的,基本就是端口不同,如下所示:

# /etc/redis/sentinel-27001.conf
# 哨兵的端口
port 27001
# 绑定的IP,和redis的一样
bind 0.0.0.0
# 放置的文件夹
dir "/data/redis/sentinel/27001"
# 守护进程启动
daemonize yes
# 哨兵的密码,可以和redis的配置不一样,但是,只有redis5以后的版本才支持
# requirepass 123123
# 日志文件
logfile "/data/redis/sentinel/sentinel-27001.log"
# 意思是sentinel监控一个别名是mymaster的主节点,IP是46.115.158.124端口是6380,2则表示判断主节点不可达需要的票数,一般是sentinel/2+1
# 这行可以配置多条,用来表示监听多个redis主从服务
# 注意这里配置的IP地址是要程序能访问的,要是配置个127.0.0.1是可以启动,但是程序会访问不了,那也是扯淡
sentinel monitor mymaster 46.115.158.124 6380 2
# redis主节点的密码
sentinel auth-pass mymaster 123456
# 每个Sentinel节点都要通过定期发送ping命令来判断Redis数据节点和其 余Sentinel节点是否可达,如果超过了down-after-milliseconds配置的时间且没 有有效的回复,则判定节点不可达,单位是毫秒
sentinel down-after-milliseconds mymaster 30000
# parallel-syncs是用来限制在一次故障转移之后,每次向新的主节 点发起复制操作的从节点个数
sentinel parallel-syncs mymaster 1
# failover-timeout通常被解释成故障转移超时时间
sentinel failover-timeout mymaster 180000

# /etc/redis/sentinel-27002.conf
port 27002
bind 0.0.0.0
# 放置的文件夹
dir "/data/redis/sentinel/27002"
daemonize yes
# requirepass 123123
logfile "/data/redis/sentinel/sentinel-27002.log"
sentinel monitor mymaster 46.115.158.124 6380 2
sentinel auth-pass mymaster 123456
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

# /etc/redis/sentinel-27003.conf
port 27003
bind 0.0.0.0
# 放置的文件夹
dir "/data/redis/sentinel/27003"
daemonize yes
# requirepass 123123
logfile "/data/redis/sentinel/sentinel-27003.log"
sentinel monitor mymaster 46.115.158.124 6380 2
sentinel auth-pass mymaster 123456
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

然后通过以下命令来启动:

redis-sentinel /etc/redis/sentinel-27001.conf
redis-sentinel /etc/redis/sentinel-27002.conf
redis-sentinel /etc/redis/sentinel-27003.conf

这时候,整个哨兵模式就启动了。启动后配置文件会多一些内容,这个不用管也不用删除。我们可以通过命令行来进行查看哨兵模式的状态:

redis-cli -h 127.0.0.1 -p 27001 info Sentinel
# 显示信息
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=46.115.158.124:6380,slaves=2,sentinels=3
3. 在SpringBoot使用哨兵模式

SpringBoot1.x默认使用的是Jedis作为Redis的客户端,而在2.x已经是使用lettuce作为客户端来连接和操作Redis。
在这里需要说明的是需要将springboot-starter-data-redis升级到2.2版本及以上,lettuce-core 升级到5.2.0及以上,这边修饰了Sentinel密码的一些大的问题。
SpringBoot中配置很简单,首先将之前配置的关于单机的redis的配置全部删掉,然后在配置文件中添加:

# sentinel的节点信息,注意端口一定要打开
spring.redis.sentinel.nodes=46.115.158.124:27001,46.115.158.124:27002,46.115.158.124:27003
# 配置的sentinel设置的别名
spring.redis.sentinel.master=mymaster
# sentinel的密码,在上面的sentinel里面注释的那部分;注意,redis5.x之后才支持Sentinel配置密码,yum默认安装的是3.x,是不支持密码的,你配置了这个会一直报错
# spring.redis.sentinel.password=123123
# redis 数据库的密码
spring.redis.password=123456

Java代码同样是使用RedisTemplate或者StringRedisTemplate就可以操作了:

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Test
    public void test00() {
        stringRedisTemplate.opsForValue().set("username", "hugh");
    }

这时候我们直接将6380所在的那个redis服务杀了之后,过一会(不会立即,因为有ping和判断的时间),再去看sentinel服务,会发现主节点已经切换了:

# redis-cli -h 127.0.0.1 -p 27001 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=47.105.178.104:6382,slaves=2,sentinels=3

这时候再跑测试用例,还是可以通过的。
这时候我们再把6380的节点起起来,查看状态,会发现他已经加入了,而且是作为从节点的:

127.0.0.1:6380> info replication
# Replication
role:slave
master_host:47.105.178.104
master_port:6382
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1602589760
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

四、CentOs安装redis5.x或者6.x

刚才说到了redis5.x才支持Sentinel配置自己的密码,而我们的在使用的时候,其实还是应该有sentinel的密码的,所以需要配置redis的服务密码后才能使用,但是直接能连上sentinel终归是有点不安全的,所以还是建议升级到redis5.x版本,配置上sentinel的密码。但是yum安装默认是3.x的,所以找了一下5.x和6.x的安装方式,从网上找来的,并且已经实践过5.x可用。
步骤如下:

# 安装redis4/5版本通过IUS存储库(仅支持redhat/centos)
# 1. 安装 epel repo
yum install -y epel-release bash-completion
# 2. 安装 IUS repo
yum install -y https://repo.ius.io/ius-release-el7.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
# 3.  安装 redis5
yum install -y redis5
# 安装redis6/最新版本通过 remi 存储库
# 1. 安装 remix repo
wget http://rpms.remirepo.net/enterprise/remi-release-7.rpm
rpm -ivh remi-release-7.rpm
# 2. 安装 redis6
yum --enablerepo=remi install redis

五、SpringBoot配置Sentinel错误汇总

  1. Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException
Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to 127.0.0.1:6380
org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to 127.0.0.1:6380
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1534)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1442)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getNativeConnection(LettuceConnectionFactory.java:1228)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getConnection(LettuceConnectionFactory.java:1211)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getSharedConnection(LettuceConnectionFactory.java:975)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getConnection(LettuceConnectionFactory.java:360)

请检查sentinel中是不是使用的127.0.0.1或者防火墙端口没打开,sentinel和redis的端口都需要打开,并且IP地址程序和方法

  1. Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required.
Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required.
org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required.
    at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:54)
    at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:52)
    at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
    at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
    at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
    at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:273)
    at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.convertLettuceAccessException(LettuceStringCommands.java:799)
    at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.set(LettuceStringCommands.java:148)

请检查是不是没有配置redis服务的密码:spring.redis.password=123456,或者密码是否正确

  1. Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Cannot connect to a Redis Sentinel:
Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Cannot connect to a Redis Sentinel: [RedisURI [host='46.115.158.124', port=27001], RedisURI [host='46.115.158.124', port=27002], RedisURI [host='46.115.158.124', port=27003]]
org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Cannot connect to a Redis Sentinel: [RedisURI [host='47.105.178.104', port=27001], RedisURI [host='47.105.178.104', port=27002], RedisURI [host='47.105.178.104', port=27003]]
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1534)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1442)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getNativeConnection(LettuceConnectionFactory.java:1228)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getConnection(LettuceConnectionFactory.java:1211)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getSharedConnection(LettuceConnectionFactory.java:975)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getConnection(LettuceConnectionFactory.java:360)
    at org.springframew

检查是不是配置了spring.redis.sentinel.password=123123但是redis的版本是5.x一下,不支持密码配置。

总结:
连接只需要配置spring.redis.sentinel.nodesspring.redis.sentinel.master
如果redis服务配置了密码,则需要配置spring.redis.password
如果sentinel要配置密码,则redis需要是5.x或以上版本,通过spring.redis.sentinel.password=123123配置

六、其他

Sentinel其实是用来容灾或者处理故障的,数据存储还是单台机子然后进行主从复制。如果数据量更大,那么就需要接入集群来进行处理了。
之后还要在写一个专门介绍集群的内容。
在例子中的SpringBoot是要Redis都是默认的配置,如果要定制化,则需要去阅读源码的配置类,然后进行一些配置,比如CatchManager等。

有什么问题,希望提出与交流

推荐阅读更多精彩内容