Redis基础三(redis的高级配置)

Redis进阶配置

一、Redis持久化操作

​ 持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。(Redis 数据都放在内存中。如果机器挂掉,内存的数据就不存在。所以需要做持久化,将内存中的数据保存在磁盘,下一次启动的时候就可以恢复数据到内存中。)

Redis 提供了两种持久化方式: RDB(默认,rbd 就是之前说的快照Redis DataBase) 和 AOF(Append Of File)。

1.1 RDB持久化(快照)

​ Redis 可以通过创建快照来 获得存储在内存里面的数据在某个时间点上的副本。Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本,还可以将快照留在 本地以便重启服务器的时候使用。

1.1.1 RDB持久化流程

​ Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

1.1.2 RDB 持久化触发策略

​ RDB持久化提供了两种触发策略:一种是手动触发,另一种是自动触发。

  • 手动触发策略:

    ​ 手动触发是通过SAVE命令或者BGSAVE命令将内存数据保存到磁盘文件中。如下所示:

    save:会阻塞当前Redis服务器,直到持久化完成,线上应该禁止使用。

    bgsave:该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。

shell 复制代码
127.0.0.1:6379> SAVE
OK
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379>  LASTSAVE
(integer) 1611298430
  • 自动触发策略:

    ​ 自动触发策略,是指 Redis 在指定的时间内,数据发生了多少次变化时,会自动执行BGSAVE命令。自动触发的条件包含在了 Redis 的配置文件中( redis.conf 配置文件),如下所示:

    上图所示, save m n 的含义是在时间 m 秒内,如果 Redis 数据至少发生了 n 次变化,那么就自动执行BGSAVE命令。配置策略说明如下:

    1. save 900 1 表示在 900 秒内,至少更新了 1 条数据,Redis 自动触发 BGSAVE 命令,将数据保存到硬盘。
    2. save 300 10 表示在 300 秒内,至少更新了 10 条数据,Redis 自动触 BGSAVE 命令,将数据保存到硬盘。
    3. save 60 10000 表示 60 秒内,至少更新了 10000 条数据,Redis 自动触发 BGSAVE 命令,将数据保存到硬盘。

    ​ 只要上述三个条件任意满足一个,服务器就会自动执行BGSAVE命令。当然您可以根据实际情况自己调整触发策略。

1.1.3 dump.rdb 文件
  • 在 **redis.conf ** 中配置文件名称,默认为 dump.rdb.

  • RDB 文件的保存路径,也可以修改。 默认为Redis启动时命令行所在的目录下,我们可以自定义目录位置。

  • stop-writes-on-bgsave-error

    当 Redis 无法写入磁盘的话,直接关掉Redis的写操作。推荐Yes

1.2 AOF 持久化(只追加文件)

​ 以日志 的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录 ), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

注意:AOF默认不开启(可以通过 appendonly 参数开启:appendonly yes)。可以在redis.conf中配置文件名称,默认为 appendonly.aof。AOF文件的保存路径,同RDB的路径一致。

​ AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)。

​ 开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入硬盘中的 AOF

文件。AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是

appendonly.aof。

1.2.1 AOF 持久化策略
  • appendfsync always

    ​ 始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好。这样会严重降低 Redis的速度。

  • appendfsync everysec

    每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。

  • appendfsync no

    redis不主动进行同步,把同步时机交给操作系统。

    ​ 为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF

    文件,Redis 性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数

    据。

1.2.2 ReWrite重写机制

​ AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制, 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof

  • 如何重写?

    ​ AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),redis4.0版本后的重写,是指上就是把rdb 的快照,以二级制的形式附在新的aof头部,作为已有的历史数据,替换掉原来的流水账操作。

  • no-appendfsync-on-rewrite:

    ​ 如果 no-appendfsync-on-rewrite=yes ,不写入aof文件只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能)

    ​ 如果 no-appendfsync-on-rewrite=no, 还是会把数据往磁盘里刷,但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低)

  • 触发机制,何时重写?

    Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发

    重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写。

    auto-aof-rewrite-percentage:设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)

    auto-aof-rewrite-min-size:设置重写的基准值,最小文件64MB。达到这个值开始重写。

例如:文件达到70MB开始重写,降到50MB,下次什么时候开始重写?100MB

系统载入时或者上次重写完毕时,Redis会记录此时AOF大小,设为base_size,

如果Redis的AOF当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写。

1.2.3 使用AOF持久化流程

  1. bgrewriteaof触发重写,判断是否当前有bgsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行。
  2. 主进程fork出子进程执行重写操作,保证主进程不会阻塞。
  3. 子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失。
    • 子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。
    • 主进程把aof_rewrite_buf中的数据写入到新的AOF文件。
  4. 使用新的AOF文件覆盖旧的AOF文件,完成AOF重写。

  • 优势

    • 备份机制更稳健,丢失数据概率更低。
    • 可读的日志文本,通过操作AOF稳健,可以处理误操作。
  • 劣势

    • 比起RDB占用更多的磁盘空间。

    • 恢复备份速度要慢。

    • 每次读写都同步的话,有一定的性能压力。

      复制代码
      同时开启两种持久化方式:在这种情况下,当 redis 重启的时候会优先载入 AOF 文件来恢复原始的数据, 因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整. RDB 的数据不实时,同时使用两者时服务器重启也只会找 AOF 文件。那要不要只使用 AOF 呢? 不推荐,因为 RDB 更适合用于备份数据库(AOF 在不断变化不好备份), 快速重启,而且不会有 AOF 可能潜在的 bug,留着作为一个万一的手段。

二、redis主从读写分离架构设计

​ 之前我们仅仅是在一台 Redis 服务中操作数据, 但是如果是高并发访问的情况下,一台 redis 服务可能扛不住,为了解决这样的问题可以配置多台 Redis 实现数据的同步,这些 Redis服务之间是主(master)从(slave)关系。

​ 主机数据更新后根据配置和策略, 自动同步到备机的机制。主机就是master,备机就是slave。主从复制的机制能干嘛:

  • 读写分离,性能扩展。从而提高了访问的性能。
  • 容灾快速恢复,可以很好的实现数据的备份。

2.1 搭建一主多从

​ 现在需要准备三台 redis 服务,所以需要三个 redis 配置文件,同时要使用到三个端口,分别是 6379,6380,6381。

​ 另一种方案是准备三台虚拟机 (暂不使用该方案)。

详细操作:创建堕胎机器:

  1. 拷贝多个 redis.conf 文件
  2. 开启 daemonize yes
  3. pid 文件名称 (pidfile)
  4. 指定端口 port
  5. Log 文件名称 (可以不配置)
  6. dump.rdb 名称 (dbfilename)
  7. appendonly 关掉

搭建步骤:

  • 新建一个目录,存放redis的配置文件:
shell 复制代码
#新建目录
mkdir myredis 
cd myredis/
# 拷贝原有Redis的配置文件 redis.conf 到新创建的文件夹中(注意自己的原始 redis.conf 存放位置)
cp /usr/local/bin/redis.conf redis.conf
  • 在自定义创建的文件下 在创建三个配置文件

​ 新建redis6379.conf、redis6380、redis6381 三个配置文件,并在配置文件中定义以下内容

shell 复制代码
#redis6379.conf 配置文件内容
port 6379
bind 0.0.0.0
daemonize yes
save 900 1
dbfilename dump6379.rdb
dir /usr/local/redis/data
pidfile /usr/local/redis/run/redis-6379.pid
logfile /usr/local/redis/logs/redis-6379.log

#=====================================
#redis6380.conf 配置文件内容
port 6380
bind 0.0.0.0
daemonize yes
save 900 1
dbfilename dump6380.rdb
dir /usr/local/redis/data
pidfile /usr/local/redis/run/redis-6380.pid
logfile /usr/local/redis/logs/redis-6380.log
slaveof 127.0.0.1 6379
masterauth hellosun

#=====================================
#redis6381.conf 配置文件内容
port 6381
bind 0.0.0.0
daemonize yes
save 900 1
dbfilename dump6381.rdb
dir /usr/local/redis/data
pidfile /usr/local/redis/run/redis-6381.pid
logfile /usr/local/redis/logs/redis-6381.log
slaveof 127.0.0.1 6379
masterauth hellosu
  • **bind 0.0.0.0 :**在服务器的环境中,指的就是当前服务器上所有的 ip 地址,如果机器上有 2 个 ip 192.168.30.10 和 10.0.2.15,redis 在配置中,如果配置监听在 0.0.0.0 这个地址上,那么,通过这 2 个 ip 地址都是能够到达这个 redis 服务的。同时呢,访问本地的 127.0.0.1 也是能够访问到 redis 服务的。
  • save 900 1: 表示 15 分钟持久化一次数据(将内存中的数据同步保存到磁盘中)
  • bfilename dump6381.rdb:持久化的文件
  • **slaveof 127.0.0.1 6379:**表示当前 redis 服务的主主机(master)
  • **masterauth hellosun:**表示 master 机的密码
  • 启动三台 redis-server

    注意:这里需要调用 redis-server命令。需要配置好该命令,如果找不着该命令,就去对应的bin目录下执行redis-server /www/server/redisMyssss/redis3679.conf 命令。

    shell 复制代码
    [root@java2201 redisMyss]# redis-server redis6379.conf 
    [root@java2201 redisMyss]# redis-server redis6380.conf 
    [root@java2201 redisMyss]# redis-server redis6381.conf  
    [root@java2201 redisMyss]# ps -ef | grep redis
    root       3012      1  0 14:45 ?        00:00:00 redis-server 127.0.0.1:6379
    root       3018      1  0 14:46 ?        00:00:00 redis-server 127.0.0.1:6380
    root       3024      1  0 14:46 ?        00:00:00 redis-server 127.0.0.1:6381
    root       3030   2541  0 14:46 pts/0    00:00:00 grep --color=auto redis
  • 配置从机(上面配置文件中已经设置)

    在6380 、6381 两个redis服务上配置从机角色。6379 是主机,不需要做任何配置。

    首先我们关掉redis三台服务器:

    shell 复制代码
    [root@java2201 redisMyss]# redis-cli -p 6379 shutdown
    [root@java2201 redisMyss]# redis-cli -p 6380 shutdown
    [root@java2201 redisMyss]# redis-cli -p 6381 shutdown

    在 redis6380.conf 、redis6381.conf 配置文件中配置从机,在配置文件中添加: slaveof 127.0.0.1 6379

  • 重启三台redis服务器,查询相关的服务器运行信息。

    首先使用 redis-cli -p 命令使用客户端连接服务器( 注意:如果设置密码的从机,需要登录密码 ),然后使用命令info replication 查看redis服务器运行情况。

    shell 复制代码
    #===========查看 master(主机 redis6379) 的运行情况=========
    [root@java2201 redisMyss]# redis-cli -p 6379
    127.0.0.1:6379> info replication
    # Replication
    role:master  # 主机
    connected_slaves:2  # 从机的数量
    slave0:ip=127.0.0.1,port=6380,state=online,offset=42,lag=0  # 从机的具体状态
    slave1:ip=127.0.0.1,port=6381,state=online,offset=28,lag=1  # 从机的具体状态
    master_failover_state:no-failover
    master_replid:df8e8d5239e9e60712e03aef99e38dc2954ef428
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:42
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:1
    repl_backlog_histlen:42
    
    ===========查看 slave(从机 redis6380、redis6381) 的运行情况=========
    [root@java2201 redisMyss]# redis-cli -p 6380
    127.0.0.1:6380> info replication
    Replication
    role:slave  # 角色 从机
    master_host:127.0.0.1
    master_port:6379
    master_link_status:up
    master_last_io_seconds_ago:8
    master_sync_in_progress:0
    slave_repl_offset:56
    slave_priority:100
    slave_read_only:1
    connected_slaves:0
    master_failover_state:no-failover
    master_replid:df8e8d5239e9e60712e03aef99e38dc2954ef428
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:56
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:1
    repl_backlog_histlen:56
  • 测试reids主从复制是否成功

在主机上添加数据,在从机上查看:

shell 复制代码
#======在redis6379机器上设置数据======
127.0.0.1:6379> set username eric
OK

#======在从机redis6380 redis6381上查看数据======
127.0.0.1:6380> get username
"eric"
127.0.0.1:6381> get username
"eric"

在从机上写数据,看是否可以写成功。

shell 复制代码
127.0.0.1:6380> set k1 v1
(error) READONLY You can't write against a read only replica.  
# 不能写数据,因为从机只能读数据

127.0.0.1:6381> set k2 v2
(error) READONLY You can't write against a read only replica.
# 不能写数据,因为从机只能读数据

2.2 主从复制原理

​ 首先测试一个几个问题会发生的情况:

2.2.1 从机挂掉后的情况
  1. 这里先停掉redis6381这台机器。
  2. 然后在主机6379上新增一个数据( 此时redis6381从机已经关闭 )
  3. 登录到6380这台从机上去获取新增的数据,查看效果
  4. 重启redis6381 这台从机,查询挂掉期间主机上新增的数据,看是否能够获取到
shell 复制代码
#===== 1.停掉redis6381 ======
127.0.0.1:6381> shutdown
not connected> exit

[root@java2201 redisMyss]# ps -ef | grep redis
root       3956      1  0 15:49 ?        00:00:00 redis-server 127.0.0.1:6379
root       3962      1  0 15:49 ?        00:00:00 redis-server 127.0.0.1:6380
root       3975   3807  0 15:49 pts/0    00:00:00 redis-cli -p 6379
root       3987   3864  0 15:49 pts/1    00:00:00 redis-cli -p 6380
root       4002   3905  0 15:50 pts/2    00:00:00 grep --color=auto redis

#===== 2.主机上新增数据 ======
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> keys *
1) "username"
2) "k1"

#===== 3.登录到从机6380 查看情况 ======
127.0.0.1:6380> keys *
1) "k1"
2) "username"

#===== 4.重启之前关闭的从机redis6381 ======
[root@java2201 myredis]# redis-server redis6381.conf 
[root@java2201 myredis]# redis-cli -p 6381
127.0.0.1:6381> info replication
# Replication
role:slave  # 重启之后 角色没有发生变化,依然是从机
master_host:127.0.0.1
master_port:6379

#===== 4.重启后的从机6381中,查询主机增加的数据 ======
127.0.0.1:6381> keys *
1) "k1"   # 依然能够同步到主机上最新的数据
2) "username"
2.2.2 主机挂掉后的情况

这里将主机进行挂掉操作,然后在从机中查看从机的状态是否发生变化。

shell 复制代码
#===== 1.将主机redis6379 关闭 ======
127.0.0.1:6379> shutdown
not connected> exit
[root@java2201 myredis]# ps -ef | grep redis
root       3962      1  0 15:49 ?        00:00:00 redis-server 127.0.0.1:6380
root       3987   3864  0 15:49 pts/1    00:00:00 redis-cli -p 6380
root       4022      1  0 15:52 ?        00:00:00 redis-server 127.0.0.1:6381
root       4036   3905  0 15:53 pts/2    00:00:00 redis-cli -p 6381
root       4054   3807  0 15:55 pts/0    00:00:00 grep --color=auto redis


#===== 2.查看两个从机的状态变化 ======
127.0.0.1:6380> info replication  # 从机6380机器上的状态
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down

127.0.0.1:6381> info replication # 从机6381机器上的状态
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down

可以看出,不管主机的状态是什么样,从机的角色永远不会发生变化。

2.2.3 主从复制的原理
  • Slave启动成功连接到master后会发送一个sync命令。
  • Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步。
  • 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
  • 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步。
  • 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行。

2.3 其它配置方式

2.3.1 薪火相传

上一个Slave可以是下一个slave的Master,Slave同样可以接收其他 slaves的连接和同步请求,那么该slave作为了链条中下一个的master, 可以有效减轻master的写压力,去中心化降低风险。

风险是一旦某个slave宕机,后面的slave都没法备份。

现在我们实现薪火相传的效果:

搭建redis6382,注意,redis6382是redis6381的从机。

shell 复制代码
[root@java2201 myredis]# vim redis6382.conf

#====== 配置文件内容 ======
include /myredis/redis.conf
pidfile /var/run/redis_6382.pid
port 6382
dbfilename dump6382.rdb
slaveof 127.0.0.1 6381  # 不是6379


#启动 redis6382 客户端,在查询他的状态
[root@java2201 myredis]# redis-cli -p 6382
127.0.0.1:6382> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6381  # 我们发现6381是6382的主机

在查询 redis6381的状态:

我们发现redis6380既是从机还是6382的主机。

注意:6381还是只能提供读的服务,不能写数据,它的作用是分担了6379机器数据同步的压力。

2.3.2 反客为主

在默认情况下,如果主机挂了,从机的角色不会发生变化,永远都是从机的角色。如果我们想主机挂了之后,从机的角色发生转换,转换成主机,这就叫反客为主。

  • 关闭主机redis6379

    shell 复制代码
    127.0.0.1:6379> shutdown
    not connected> exit
  • 把从机redis6380设置成主机

    shell 复制代码
    127.0.0.1:6380> slaveof no one
    OK
    127.0.0.1:6380> info replication
    # Replication
    role:master
    127.0.0.1:6380> set k2 v2  # 可以存数据了
    OK
  • 在从机上取数据

    shell 复制代码
    127.0.0.1:6381> slaveof 127.0.0.1 6380  # 重新指定其主机是6380
    OK
    127.0.0.1:6381> keys *
    1) "k1"
    2) "username"
    3) "k2"
    127.0.0.1:6381> get k2
    "v2"

2.4 哨兵模式

反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从节点切换成主节点。

​ 在之前配置的 Redis 主从服务中,可以实现数据的备份,也实现了读写分离(master 负责写入和更新数据,slave 负责读取数据),但是还是存在一定的问题,一旦 master 死掉(比如网络出问题)之后就无法写入数据了,所以其实上之前的架构设计还很脆弱,容灾能力不强。

​ 如果 master 宕机了,整个系统无法写入数据,很可能让系统瘫痪给企业带来损失(最直观的是影响了口碑等影响)。

​ 最好的做法是当 master 死掉之后会在 slave 中通过一定算法(不用你实现)选举出一台新的 master。要实现这个操作需要配置哨兵对整个集群环境进行监控,一旦有两个或者两个以上的哨兵(数量是可以配置)认为当前的master 出问题了,最终就从 slave 中选举出一台新的 master,从而解决问题。

​ 关于哨兵机制的配置我们不需要额外下载任何安装包之类的东西,在我们安装 redis 的时候就已经自带了,所以我们只需要拷贝相关的启动文件和配置文件进行配置即可。

2.4.1 搭建哨兵模式步骤
  1. 如果要进行哨兵进程的启动,那么一定需要有一个哨兵程序启动文件 redis-sentinel (在reids的安装目录中),该文件自动会保存在编译后的 Redis 目录中,将该启动文件拷贝到 redis 的bin 目录中。

    cp /usr/local/src/redis/src/redis-sentinel /usr/local/redis/bin/

  2. sentinel.conf 拷贝到 我们

    将官方自带的哨兵配置文件 sentinel.conf (此文件在 redis 的源码目录下,即解压路径下)复制到存放主从机配置文件中(根据自己的存放位置进行拷贝)

    cp /usr/local/src/redis/sentinel.conf /usr/local/redis/conf/

  3. 哨兵机制中各配置项的含义

    其中mymaster为监控对象起的服务器名称

    NO 配置项 作用模式
    1 protected-mode no(在 redis.conf 的配置文件中) 关闭 redis 的保护模式,如果不关闭 redis 的保护模式则哨兵无法实现主从切换,无法自动修改配置文件选举出新的 maste
    2 port 26379 配置哨兵服务的端口
    3 sentinel monitor mymaster 127.0.0.16379 2 设置哨兵监控的名称,192.168.139.134 63792 :表 示 master 的 ip 地址,端口号, 2 指明当有两个 sentinel 认为 master 失效时,master 才算真正失效
    4 sentinel auth-pass mymaster hellosu 配置 master 的密码(如没设置密码,可以省略)
    5 sentinel down-after-milliseconds mymaster 5000 master 宕机之后指定时间才会被 sentinel 主观地认为是不可用的,单位是毫秒,默认为 30 秒。
    6 sentinel failover-timeout mymaster 18000 配置选举新的 master 超时时间(切换时间,单位是毫秒),如果超过了该时间认为切换失败(整个服务就完了)
    7 pidfile/usr/data/redis/run/redis-sentinel.pid 哨兵进程文件的保存目录,与 Redis 进程目录相同
    8 #logfile "/usr/data/redis/logs/sentinel-26379.log" 设置哨兵日志文件所在的路径,如果此时希望信息前台显示输出,则暂时不进行此内容的设置
    9 dir /usr/local/redis/data 【但是你要在系统中创建该目录】 指定一些临时数据的保存目录
    10 daemonize no 是否为后台运行,如果设置为 yes 表示后台运行(实际开发中要设置为 yes)
  4. 将如下的配置复制到三台 slave 机中的 sentinel.conf 文件

    conf 复制代码
    port 26379
    daemonize no
    pidfile /usr/data/redis/run/redis-sentinel.pid
    # logfile "/usr/data/redis/logs/sentinel-26379.log"
    dir /usr/local/redis/data
    sentinel monitor mymaster 192.168.139.134 6379 2
    sentinel auth-pass mymaster hellosun
    sentinel down-after-milliseconds mymaster 5000
    sentinel failover-timeout mymaster 18000
2.4.2 启动哨兵
shell 复制代码
[root@java2201 conf]# redis-sentinel sentinel.conf

我们可以看到哨兵监控的具体信息:

  • 将主机redis6379停掉:

    shell 复制代码
    [root@java2201 myredis]# redis-cli -p 6379 shutdown

    我们再看sentinel控制台的输出(此时需要等待一定的时间),我们可以看到切换主机的一些日志信息:

  • 查看redis6381的服务器运行信息

    shell 复制代码
    [root@java2201 ~]# redis-cli -p 6381
    127.0.0.1:6381> info replication
    # Replication
    role:master  # 变成了主机
    connected_slaves:1
    slave0:ip=127.0.0.1,port=6380,state=online,offset=36593,lag=0
    master_failover_state:no-failover
    master_replid:b6ff983efa23cafad22380a6d9d270eadcb69e99
    master_replid2:8b338cbee4b296b50621911fd89d26d68c110b14
    master_repl_offset:36593
    second_repl_offset:14976
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:1
    repl_backlog_histlen:36593
  • 接下来我们重新启动redis6379服务器

    shell 复制代码
    [root@java2201 myredis]# redis-cli -p 6379 shutdown
    [root@java2201 myredis]# redis-server redis6379.conf 
    [root@java2201 myredis]# redis-cli -p 6379
    127.0.0.1:6379> info replication
    # Replication
    role:slave  # 6379变成了从服务器了
    master_host:127.0.0.1
    master_port:6381
2.4.3 哨兵的选举策略

通过上面的操作,我们指定当从节点挂掉之后,哨兵会选出新的主节点,他们选举策略如下:

(1) 选择优先级靠前的。

优先级在redis.conf中默认:replica-priority 100,值越小优先级越高

(2) 选择偏移量最大的。

偏移量是指获得原主机数据最全的。

(3) 选择runid最小的服务。

每个redis实例启动后都会随机生成一个40位的runid。

2.5 Spring整合哨兵机制

​ 实现了哨兵机制的集群架构配置,此时如果原先的 master 服务宕机了,要求也不影响程序的访问,程序也能找到新的 master 机进行数据操作,要实现这个需求需要在项目中进行一些整合配置,我们就以 SpringDataRedis

作为案例进行整合配置。

三、redis集群

​ 如果redis容量不够了,数据写不进去了,redis如何扩容?

​ 如果redis的并发操作大,redis如何分摊并发压力?

​ 另外,主从模式,薪火相传模式,主机宕机,导致ip地址发生变化,应用程序中配置需要修改对应的主机地址、端口等信息。之前通过代理主机 来解决,但是redis3.0中提供了解决方案。就是无中心化集群配置。

3.1 搭建redis集群

​ redis集群有什么特点:Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。

​ Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

接下来我们开始redis集群的具体搭建:

  • 创建新的目录

创建目录redis-cluster,将之前搭建主从复制的redis.conf拷贝到当前目录。

注意:redis.conf配置文件一定要设置成远程登录,否则搭建集群不成功。

shell 复制代码
[root@java2201 /]# mkdir redis-cluster
[root@java2201 /]# cd redis-cluster
[root@java2201 redis-cluster]# cp /myredis/redis.conf redis.conf
  • 我们搭建3主3从,所以我们创建6个redis实例。

6个redis实例的端口号分别是6379,6380,6381,6389,6390,6391。

创建redis配置文件redis6379.conf,配置文件里面定义如下:

shell 复制代码
include /redis-cluster/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename dump6379.rdb
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000

注意:

cluster-enabled yes 打开集群模式

cluster-config-file nodes-6379.conf 设定节点配置文件名

cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换。

  • 复制其他节点的配置文件
shell 复制代码
[root@java2201 redis-cluster]# cp redis6379.conf redis6380.conf
[root@java2201 redis-cluster]# cp redis6379.conf redis6381.conf
[root@java2201 redis-cluster]# cp redis6379.conf redis6389.conf
[root@java2201 redis-cluster]# cp redis6379.conf redis6390.conf
[root@java2201 redis-cluster]# cp redis6379.conf redis6391.conf
[root@java2201 redis-cluster]# ll
总用量 24
-rw-r--r--. 1 root root 177 10月  6 19:57 redis6379.conf
-rw-r--r--. 1 root root 177 10月  6 20:01 redis6380.conf
-rw-r--r--. 1 root root 177 10月  6 20:01 redis6381.conf
-rw-r--r--. 1 root root 177 10月  6 20:01 redis6389.conf
-rw-r--r--. 1 root root 177 10月  6 20:01 redis6390.conf
-rw-r--r--. 1 root root 177 10月  6 20:01 redis6391.conf
  • 将配置文件里面的内容进行修改

可以使用查找并替换的命令来实现:

shell 复制代码
:%s/6379/6380
  • 启动6个redis服务
shell 复制代码
[root@java2201 redis-cluster]# redis-server redis6379.conf 
[root@java2201 redis-cluster]# redis-server redis6380.conf 
[root@java2201 redis-cluster]# redis-server redis6381.conf 
[root@java2201 redis-cluster]# redis-server redis6389.conf 
[root@java2201 redis-cluster]# redis-server redis6390.conf 
[root@java2201 redis-cluster]# redis-server redis6391.conf 

查看redis服务启动状态:

shell 复制代码
[root@java2201 redis-cluster]# ps -ef | grep redis
root       8673      1  0 22:56 ?        00:00:00 redis-server 127.0.0.1:6379 [cluster]
root       8679      1  0 22:56 ?        00:00:00 redis-server 127.0.0.1:6380 [cluster]
root       8685      1  0 22:56 ?        00:00:00 redis-server 127.0.0.1:6381 [cluster]
root       8691      1  0 22:56 ?        00:00:00 redis-server 127.0.0.1:6389 [cluster]
root       8697      1  0 22:56 ?        00:00:00 redis-server 127.0.0.1:6390 [cluster]
root       8703      1  0 22:56 ?        00:00:00 redis-server 127.0.0.1:6391 [cluster]
root       8483   6637  0 22:45 pts/0    00:00:00 grep --color=auto redis

注意:如果搭建成功,当前目录下面必须存在以nodes-开头的文件

  • 将6个节点合并成一个集群

合并集群之前,请确保所有redis实例启动后,nodes-xxxx.conf文件都生成正常。

先进入到最先安装redis的src目录:

shell 复制代码
[root@java2201 /]# cd  /opt/redis-6.2.1/src
[root@java2201 src]# ls
shell 复制代码
[root@java2201 src]# redis-cli --cluster create --cluster-replicas 1 192.168.10.130:6379 192.168.10.130:6380 192.168.10.130:6381 192.168.10.130:6389 192.168.10.130:6390 192.168.10.130:6391

注意:此处不要用127.0.0.1, 请用真实IP地址。

--replicas 1 采用最简单的方式配置集群,一台主机,一台从机,正好三组。

执行命令,之后,效果如下:

我们输入yes,表示接受以上配置:

如果出现以上信息说明集群搭建成功。

  • 连接集群

使用连接集群的命令: redis-cli -c -p 6379

-c:指的使用集群的方式连接redis。

-p:指定参数,这里任意参数都可以。

shell 复制代码
[root@java2201 src]# redis-cli -c -p 6379
[root@java2201 src]# cluster nodes # 查看集群的具体情况

3.2 redis集群细节

一个集群至少要有三个主节点。

选项 --cluster-replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。

分配原则尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在一个IP地址上。

一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个。

集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。

集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:

节点 A 负责处理 0 号至 5460 号插槽。

节点 B 负责处理 5461 号至 10922 号插槽。

节点 C 负责处理 10923 号至 16383 号插槽。

  • 在集群模式写写入数据

接下来我们在集群中插入数据:

shell 复制代码
127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 192.168.10.140:6381
OK
192.168.10.140:6381> set k2 v2
-> Redirected to slot [449] located at 192.168.10.140:6379
OK
192.168.10.140:6379>

注意:不在一个slot下的键值,是不能使用mget,mset等多键操作。

shell 复制代码
192.168.10.140:6379> mset username eric age 20
(error) CROSSSLOT Keys in request don't hash to the same slot   # 此时报错

我们可以通过{}来定义组的概念,从而使key中{}内相同内容的键值对放到一个slot中去。

shell 复制代码
192.168.10.140:6379> mset username{group1} eric age{group1} 20
-> Redirected to slot [7859] located at 192.168.10.140:6380
OK
192.168.10.140:6380>
  • 查询集群中的值
shell 复制代码
192.168.10.140:6380> cluster keyslot k2  # 计算key的插槽值
(integer) 449
192.168.10.140:6380> get k1
-> Redirected to slot [12706] located at 192.168.10.140:6381
"v1"
192.168.10.140:6381> get k2
-> Redirected to slot [449] located at 192.168.10.140:6379
"v2"
192.168.10.140:6381> get username{group1}
-> Redirected to slot [7859] located at 192.168.10.140:6380
"eric"
192.168.10.140:6380> get age{group1}
"20"
  • redis集群的故障恢复

在redis集群中,如果某一个主节点挂掉(下线),会出现什么样的后果:

shell 复制代码
192.168.10.140:6379> shutdown  # 将6379服务停掉
not connected> exit

现在我们重新启动6379这台机器,我们重新打开一个终端来启动:

shell 复制代码
[root@java2201 src]# cd /redis-cluster/
[root@java2201 redis-cluster]# redis-server redis6379.conf 

如果所有某一段插槽的主从节点都宕掉,redis服务是否还能继续?

如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为yes ,那么 ,整个集群都挂掉

如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为no ,那么,该插槽数据全都不能使用,也无法存储。

redis.conf中的参数 cluster-require-full-coverage

四、redis其它

4.1 Redis 的使用场景

  • 数据缓存(提高访问性能)

    比如热点新闻就可以存到 redis 中。

    如果数据在短时间之内不会发生变化,而且它们还要被频繁访问,为了提高用户的请求速度和降低网站的负载,降低数据库的读写次数,就把这些数据放到缓存中。

  • 会话缓存(共享 session)

    (session cache,保存 web 会话信息,替代了 ip_hash),可以将用户的信息保存到 redis 中(nginx 代理多个web服务器)。

  • 排行榜/计数器

做排行榜用 zset,里面有分值,就可以实现排行榜。(NGINX+redis 计数器进行 IP 自动封禁,防止恶意攻击)

  • 消息队列(ActiveMQ/RabbitMQ/Kafka/RocketMQ)

    生产者/消费者模型,发布/订阅模式(构建实时消息系统,聊天,群聊)重点做缓存(数据,用户信息)

4.2 LIMITS 限制 (淘汰策略)

redis基础一文章中有说明过!

​ 保存到 redis 中的数据会被淘汰,什么情况会被淘汰?比如超时时间到了,但是如果没有设置超时时间的数据如何淘汰?

  • maxmemory

    设置 redis 可以使用的内存量。一旦到达内存使用上限,redis 将会试图移除内部数据,移除规则可以过 maxmemory-policy 来指定。如果 redis 无法根据移除规则来移除内存中的数据,或者设置了"不允许移除",那么 redis 则会针对那些需要申请内存的指令返回错误信息,比如 SET、LPUSH 等。

  • maxmemory-policy

    • noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键(不移除任何数据)
    • allkeys-lru:加入键的时候,如果过限,首先通过 LRU 算法驱逐最近最久没有使用的键 【在指定的时间内某些 key 没有被使用就移出】
    • volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键(范围是设置了超时时间的 key 中查找)
    • allkeys-random:加入键的时候如果过限,从所有 key 随机删除
    • volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
    • volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
    • volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
    • allkeys-lfu:从所有键中驱逐使用频率最少的键

LRU(Least recently used,最近最少使用)

LRU : 最近最久没有使用的算法(对最近的表现做总结,考虑不完善,但是很容易实现)

如果一个数据最近一段时间都没有被访问到,那么认为这个数据在将来访问的可能性也比较小,因此,当空间满时,最久没有访问的数据最先被淘汰

LFU : 使用频率最少的算法(对历史数据做总结,考虑更全面一些,但是会耗费总结历史的时间)

LFU(least frequently used (LFU) page-replacement algorithm)

如果一个数据很少被访问到,那么认为这个数据在将来访问的可能性也比较小,因此,当空间满时,最小频率访问的数据最先被淘汰

4.3 Redis应用问题

4.3.1 缓存穿透
  • 问题描述:

    复制代码
      key对应的数据在数据库并不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据库。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

  • 解决方案:

    复制代码
      一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

(1) **对空值缓存:**如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟

(2) 设置可访问的名单(白名单):

​ 使用bitmap类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。

(3) 采用布隆过滤器**:(布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。

布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。)

将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。

(4) **进行实时监控:**当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务

4.3.2 缓存击穿

​ 缓存穿透是恶意的攻击行为。而缓存穿击是系统正常使用过程中出现了的某一个热点数据过期,导致大量的请求去查询关系型数据库。

  • 问题描述:

    复制代码
      key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
  • 解决方案:

    复制代码
      key可能会在某些时间点被超高并发地访问,是一种非常"热点"的数据。这个时候,需要考虑一个问题:缓存被"击穿"的问题。

**(1)预先设置热门数据:**在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长

**(2)实时调整:**现场监控哪些数据热门,实时调整key的过期时长[热点 key 永不过期(过期时间相对较长),避免出现淘汰策略]

(3)使用锁:

​ (3.1) 就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db。

​ (3.2) 先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX)去set一个mutex key

​ (3.3) 当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key;

​ (3.4) 当操作返回失败,证明有线程在load db,当前线程睡眠一段时间再重试整个get缓存的方法。

4.3.3 缓存雪崩
  • 问题描述:

    复制代码
      key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
    
      缓存雪崩与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key。
  • 解决方案:

    • **构建多级缓存架构:**nginx缓存 + redis缓存 +其他缓存(ehcache等)
    • 用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况
    • 比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
    • 记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。
    • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
    • 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中(比如说有多台 redis 服务,可以将热点数据分部在不同 redis 服务中)。
    • 设置热点数据永远不过期

缓存穿透--->人为恶意对某一个 key 进行查询导致的关系型数据库出现压力

缓存穿击--->正常使用中,某个热点数据过期导致的 关系型数据库出现的压力

缓存雪崩--->大批量的热点数据同时过期给关系型数据库带来的查询压力

相关推荐
{{uname}}14 分钟前
利用WebSocket实现实时通知
网络·spring boot·websocket·网络协议
熊大如如37 分钟前
Java 反射
java·开发语言
巨龙之路1 小时前
什么是时序数据库?
数据库·时序数据库
蔡蓝1 小时前
binlog日志以及MySQL的数据同步
数据库·mysql
猿来入此小猿1 小时前
基于SSM实现的健身房系统功能实现十六
java·毕业设计·ssm·毕业源码·免费学习·猿来入此·健身平台
goTsHgo2 小时前
Spring Boot 自动装配原理详解
java·spring boot
卑微的Coder2 小时前
JMeter同步定时器 模拟多用户并发访问场景
java·jmeter·压力测试
是店小二呀2 小时前
【金仓数据库征文】金融行业中的国产化数据库替代应用实践
数据库·金融·数据库平替用金仓·金仓数据库2025征文
pjx9872 小时前
微服务的“导航系统”:使用Spring Cloud Eureka实现服务注册与发现
java·spring cloud·微服务·eureka
炒空心菜菜2 小时前
SparkSQL 连接 MySQL 并添加新数据:实战指南
大数据·开发语言·数据库·后端·mysql·spark