Linux分布式缓存系统——Redis持久化+Sentinel哨兵模式+Redis集群

Redis概念

Redis介绍

Redis是一个开源的使用C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis与其他key-value缓存产品有以下特点:

  1. 支持数据持久化,可以将内存放入数据保存在磁盘中,启动的时候可以再次加载进行使用;
  2. 支持多种数据结构的存储,通常被称为数据结构服务器,值(value) 可以是字符串(String)、哈希(Hash)、列表(List)、 集合(Sets) 和有序集合(Sorted Sets)等五种基本数据类型(还支持扩展数据类型);
  3. 支持数据备份,即master-slave模式的数据备份;
  4. 所有的操作都是原子性的,同时Redis还支持几个操作合并后的原子执行;
  5. 高性能,Redis读的速度是110000次/s,写的速度是81000次/s。

Redis和Memcached的区别

对比项 Redis Memcached
数据存储介质 缓存的数据存放在内存和硬盘中,能够达到持久化存储 缓存的数据存放在内存中,内存失效则数据丢失,无法恢复
数据支持类型 支持复杂的数据结构:String,List,Hash,Set,Zset 仅支持key-value结构,但可以缓存图片、视频等非结构化数据
架构模式 支持Master-Slave(主从)模式的,应用在单核上,在存储小数据时比memcached性能更高 支持分布式,应用在多核上
存储数据大小 单个Value存储的数据最大为1G 存储的最大为1MB,当存储的Value数据值大于100K时,memcached性能高于Redis

Redis应用场景

  1. 网站缓存:在Web服务器和数据库之间充当缓存层,加快Web服务器的响应速度。
  2. 会话保持:使用Redis存储会话数据,可以在无状态服务器之间共享用户状态。
  3. 消息队列:利用Redis的发布/订阅功能,可以实现消息队列,适用于异步任务处理。
  4. 分布式锁:在分布式系统中,Redis提供分布式锁功能,确保同一时间只有一个客户端可以访问共享资源。
  5. 计数器:例如转发数、评论数,有了原子递增(Atomic Increment),可以加上计数,用GETSET重置,或者是让其过期。
  6. 排行榜:通过有序集合的支持,Redis可用于实现实时排行榜。

Redis原生支持集群模式
在Redis3.x版本中,便能支持cluster模式,而memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据

https://blog.csdn.net/qq_35764295/article/details/127068164【Redis系列】Redis发布版本历史及特性
https://www.modb.pro/db/515749 Redis系列之——前世今生

Redis体系结构

Redis结构

互联网数据目前基本使用关系数据库或者key value存储,但前者存在冗余数据,后者修改和删除比较麻烦。
Redis在内存中设计了各种数据类型,让业务能够高速原子的访问这些数据结构,并且不需要关心持久存储的问题,从架构上解决了前面两种存储需要走弯路的问题。

Redis事务

Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
Redis事务具有ACID中的一致性(Consistency)隔离性(Isolation),不支持其他特性。AOF持久化模式且appendfsync为always时,事务也具有耐久性。

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

数据备份

备份模式:在Redis中有Replication,由于Redis的高性能,复制基本没有延迟,达到了防止单点故障实现高可用
容灾模式:sentinel系统集群监控redis主从系统集群。

Redis数据类型

String (字符串)

Redis最基本的数据类型,一个key对应一个value,一个键最大能存储512MB
set key value赋值,用get key获得key的值

Hash(哈希)

是一个键值对集合,是一个string类型的field和value的映射表,适合用于存储对象
hset/hget 一次插入一个键值对
hmset/hmget 一次插入多个键值对
hgetall 用于获取hash对象

127.0.0.1:6379> hset set1 name zhangsan
(integer) 1
127.0.0.1:6379> hset set1 score 100
(integer) 1
127.0.0.1:6379> hset set1 age 20
(integer) 1

127.0.0.1:6379> hmget set1 name score age
1) "zhangsan"
2) "100"
3) "20"

127.0.0.1:6379> hgetall set1
1) "name"
2) "zhangsan"
3) "score"
4) "100"
5) "age"
6) "20"

LIST(列表)

Ipush 往列表的前边插入,Irange 闭区间读取列表

127.0.0.1:6379> lpush lvar 1
(integer) 1
127.0.0.1:6379> lpush lvar a
(integer) 2
127.0.0.1:6379> lpush lvar ab
(integer) 3
127.0.0.1:6379> lrange lvar 0 2
1) "ab"
2) "a"
3) "1"

Set(集合)

Redis的Set是string类型的无序集合,不能重复,通过哈希表实现,可以添加,删除
sadd 往集合中插入元素
smembers 列举出集合中的元素

127.0.0.1:6379> sadd setvar redis
(integer) 1
127.0.0.1:6379> sadd setvar mongodb
(integer) 1
127.0.0.1:6379> sadd setvar rabbitmq
(integer) 1
127.0.0.1:6379> SMEMBERS setvar
1) "mongodb"
2) "rabbitmq"
3) "redis"

Zset(Sorted Sets:有序集合)

zset和set一样也是String类型的集合, 且不允许元素重复
zset和set不同的地方在于zset关联一个double类型的分数,redis通过分数对集合中的元素排序
zset的元素是唯一的,但是分数是可以重复的
分数可正可负可为0

127.0.0.1:6379> zadd zvar 1 redis
(integer) 1
127.0.0.1:6379> zadd zvar 1 mongodb
(integer) 1
127.0.0.1:6379> zadd zvar 2 mongodb		#已存在
(integer) 0
127.0.0.1:6379> zadd zvar 0 rabbitmq
(integer) 1
127.0.0.1:6379> zrangebyscore zvar 0 2
1) "rabbitmq"
2) "redis"
3) "mongodb"

Redis单线程模型

    Redis内部使用单线程的文件事件处理器(file event handler)。
    Redis采用IO多路复用机制同时监听多个socket,将产生事件的socket压入内存队列中,事件分派器根据socket上的事件类型来选择对应的事件处理器进行处理。

redis单线程模型效率高的原因:

  • 纯内存操作
  • 核心是基于非阻塞的 IO 多路复用机制
  • C 语言实现,一般来说,C 语言实现的程序“距离”操作系统更近,执行速度相对会更快
  • 单线程,避免多线程的频繁上下文切换的问题,预防多线程可能产生的竞争问题

Redis数据持久化

RDB(Redis DataBase)

RDB,默认的持久化方式,将内存中数据以快照的方式写入到二进制文件dump.rdb中,通过配置文件中的save参数来定义快照的周期,例如配置redis在n秒内如果超过m个key被修改就自动做快照。

#默认的快照保存配置
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存

AOF(Append-only-file)

AOF持久化方式,Redis会将每一个收到的写命令通过write函数追加文本文件appendonly.aof中。当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。

由于os会在内核中缓存write做的修改,所以可能不是立即写到磁盘上。这样AOF方式的持久化也还是有可能会丢失部分修改。可以通过配置文件设置Redis通过fsync函数强制os写入到磁盘的时机。

appendonly yes
#启用aof持久化方式
# appendfsync always
#每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec
#每秒钟强制写入磁盘一次,默认配置,在性能和持久化方面做了很好的折中,推荐
# appendfsync no
#完全依赖os,性能最好,持久化没保证

Redis提供了bgrewriteaof命令,收到此命令redis将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件。这样做可以压缩aof的持久化文件。

https://www.cnblogs.com/zh-dream/p/12275061.html SAVE、BGSAVE和BGREWRITEAOF执行区别

混合持久化

启用混合持久化

混合持久化是Redis 4.0之后引入的新持久化方式,在AOF持久化的基础上,定期进行RDB持久化,以保证数据的快速恢复,又可以保证数据的完整性一致性
持久化过程为在AOF重写时,将RDB文件以二进制压缩格式写入到AOF文件的开头,之后的数据再以AOF格式追加到文件的末尾。
缺点:如果RDB部分或AOF部分损坏或丢失,整个AOF文件都无法恢复数据或只能恢复部分数据。
定期备份AOF文件,以防止数据丢失。

#开启AOF持久化
appendonly yes
appendfilename "appendonly.aof"
dir "/path/to/your/redis/dir"
 
# 开启混合持久化
aof-use-rdb-preamble yes

通过以下命令触发AOF重写:

  1. 在后台执行 AOF 重写:bgrewriteaof
  2. 设置 AOF 文件增长百分比阈值,当达到该阈值时自动执行 AOF 重写:config set auto-aof-rewrite-percentage <percentage>
  3. 设置 AOF 文件增长最小字节数阈值,当达到该阈值时自动执行 AOF 重写:config set auto-aof-rewrite-min-size <size>

恢复混合持久化数据

将appendonly.aof文件放到Redis的根目录,Redis以AOF持久化方式启动时,会自动加载并恢复数据,首先读取AOF文件中的 RDB 部分,将其中的键值对加载到内存中,然后读取AOF文件中的AOF部分,按顺序执行其中的命令,更新内存中的数据。

https://www.cnblogs.com/shoshana-kong/p/17519577.html Redis 混合持久化

数据持久化对比

RDB方式 AOF方式
描述 默认方式,实现方式是定时将内存的快照(snapshot) 持久化到硬盘。 AOF即append only file,在写入内存数据的同时将操作命令保存到日志文件中。
优点 使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证redis的高性能。 保证了数据的可靠性及安全性,保证更高的数据完整性。
缺点 RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。 在并发更改上万的系统中,命令日志是一个非常庞大的数据,管理维护成本非常高,恢复创建时间会非常长;AOF文件比RDB文件大,且恢复速度慢。
应用 更适合数据要求不严谨的时候。 适合数据完整性要求较高的场景。

Redis安装配置及运用

Redis应用编译安装

[root@redis ~]# yum install gcc gcc-c++ make -y
[root@redis ~]# wget http://download.redis.io/releases/redis-6.2.7.tar.gz
[root@redis ~]# tar xf redis-6.2.7.tar.gz -C /opt
[root@redis ~]# cd /opt/redis-6.2.7/
[root@redis redis-6.2.7]# make
Hint: It's a good idea to run 'make test' ;)
[root@redis redis-6.2.7]# echo $?
0
[root@redis redis-6.2.7]# mkdir -p /data/redis/
[root@redis redis-6.2.7]# make PREFIX=/data/redis install
Hint: It's a good idea to run 'make test' ;)
[root@redis redis-6.2.7]# mkdir /data/redis/conf
[root@redis redis-6.2.7]# cp -p redis.conf /data/redis/conf/
#添加系统路径
[root@redis ~]# echo "export PATH=$PATH:/data/redis/bin" >>/etc/bashrc
[root@redis ~]# source /etc/bashrc

Redis常用命令

[root@redis redis-5.0.4]# cd /data/redis/bin/
[root@redis bin]# ll
total 32700
-rwxr-xr-x 1 root root 4366520 Mar  8 10:03 redis-benchmark
-rwxr-xr-x 1 root root 8101224 Mar  8 10:03 redis-check-aof
-rwxr-xr-x 1 root root 8101224 Mar  8 10:03 redis-check-rdb
-rwxr-xr-x 1 root root 4806736 Mar  8 10:03 redis-cli
lrwxrwxrwx 1 root root      12 Mar  8 10:03 redis-sentinel -> redis-server
-rwxr-xr-x 1 root root 8101224 Mar  8 10:03 redis-server

查看redis版本:redis-server -v
image.png
设置密码:vim /etc/redis/redis.conf
requirepass passwd
重启服务:service redisd restart
image.png
密码登录:redis-cli -a password
image.png查看redis-server统计信息:10.24.24.7:7779> INFO replication
登录sentinel:/usr/local/redis/bin/redis-cli -h 10.24.24.32 -p 7800 -a passwd
查看sentinel信息:10.24.24.32:7800> info Sentinel
sentinel获取主节点信息:10.24.24.32:7800> sentinel get-master-addr-by-name master 会返回主节点ip和端口
查看集群状态:192.168.213.129:6379> cluster info

Redis应用启停

Redis应用启动

[root@redis bin]# ./redis-server -h
Usage: ./redis-server [/path/to/redis.conf] [options]
       ./redis-server - (read config from stdin)
       ./redis-server -v or --version
       ./redis-server -h or --help
       ./redis-server --test-memory <megabytes>

Examples:
       ./redis-server (run the server with default conf)
       ./redis-server /etc/redis/6379.conf
       ./redis-server --port 7777
       ./redis-server --port 7777 --replicaof 127.0.0.1 8888
       ./redis-server /etc/myredis.conf --loglevel verbose

Sentinel mode:
       ./redis-server /etc/sentinel.conf --sentinel
[root@redis bin]# /data/redis/bin/redis-server /data/redis/conf/redis.conf
(服务已启动,但进程未退出,重新打开一个终端测试)
[root@redis ~]# tty
/dev/pts/1
[root@redis ~]# ps -ef|grep redis
root       5578   1260  0 10:09 pts/0    00:00:00 /data/redis/bin/redis-server 127.0.0.1:6379
root       5583   1293  0 10:10 pts/1    00:00:00 grep --color=auto redis

需要退出,Ctrl+C即可杀死进程,退出服务
若需要进程一直处于运行状态, 则可将进程放在后台使用

[root@redis ~]# /data/redis/bin/redis-server /data/redis/conf/redis.conf &
[root@redis ~]# /data/redis/bin/redis-cli shutdown

Redis应用退出

redis-cli shutdown
如果设置上密码后,单纯的redis-cli是关不掉的,必须加上ip、port、passwd
/data/redis/bin/redis-cli -h 192.168.213.129 -p 6380 -a redis shutdown

Redis启动脚本

[root@redis ~]# cat /data/redis/bin/redis
#!/bin/bash
stop() {
/data/redis/bin/redis-cli -a redis shutdown
}
start(){
/data/redis/bin/redis-server /data/redis/conf/redis.conf &
}
case $1 in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
     *)
        echo "Usage:$0 (start|stop|restart)"
esac
[root@redis ~]# chmod +x /data/redis/bin/redis
[root@redis ~]# ln -s /data/redis/bin/redis /usr/local/bin/redis

Redis客户端连接

本地连接

[root@redis ~]# /data/redis/bin/redis-cli
127.0.0.1:6379> set name anliu
OK
127.0.0.1:6379> get name
"anliu"
127.0.0.1:6379> set IP 192.168.213.129
OK
127.0.0.1:6379> get IP
"192.168.213.129"

启动客户端、验证

127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set var "hello world"
OK
127.0.0.1:6379> get var
"hello world"

远程连接

需要已经安装redis,可以使用redis-cli命令
redis-cli -h host -p port -a password

Redis配置调优

内核参数 解释
net.core.somaxconn net.core.somaxconn默认值128,代表socket监听队列backlog上限,当一个请求(request) 尚未被处理或建立时,他会进入backlog。而socket server可以一次性处理backlog中的所有请求, 处理后的请求不再位于监听队列中。监听队列被填满后,新来的请求会被拒绝。
overcommit_memory 内核对内存分配的一种策略,。_overcommit_memory=0,_内核检查如果有足够的可用内存供应用进程使用,内存申请允许,否则内存申请失败,并把错误返回给应用进程;overcommit_memory=1,内核不管当前的内存状态,允许分配所有的物理内存;overcommit_memory=2,内核允许分配超过所有物理内存和交换空间总和的内存。
hugepage hugepage动态分配

Redis启动后,有这样3个警告

5578:M 08 Mar 2020 10:09:02.235 # WARNING: The TCP backlog setting of 511 canno                                                t be enforced because /proc/sys/net/core/somaxconn is set to the lower value of                                                 128.
5578:M 08 Mar 2020 10:09:02.235 # Server initialized
5578:M 08 Mar 2020 10:09:02.235 # WARNING overcommit_memory is set to 0! Backgr                                                ound save may fail under low memory condition. To fix this issue add 'vm.overco                                                mmit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl                                                 vm.overcommit_memory=1' for this to take effect.
5578:M 08 Mar 2020 10:09:02.235 # WARNING you have Transparent Huge Pages (THP)                                                 support enabled in your kernel. This will create latency and memory usage issu                                                es with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/t                                                ransparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order                                                 to retain the setting after a reboot. Redis must be restarted after THP is dis                                                abled.

需要我们调整对应的内核参数

[root@redis ~]# echo 1024 > /proc/sys/net/core/somaxconn
[root@redis ~]# echo 1 > /proc/sys/vm/overcommit_memory
#overcommit_memory 1容许过量使用内存 《= 物理内存+ swap 8G+2G ==10G
#12G内存 1inux内存	oom内存泄漏 oom_kill
[root@redis ~]# echo never > /sys/kernel/mm/transparent_hugepage/enabled

Redis安全性设置

设置密码

[root@redis ~]# vim /data/redis/conf/redis.conf
requirepass redis
[root@redis ~]# redis restart
[root@redis ~]# /data/redis/bin/redis-cli
127.0.0.1:6379> auth redis
OK

设置命令别名

可以通过把一个命令重命名为空串或随机字符串来彻底kill掉这个命令

[root@redis ~]# vim /data/redis/conf/redis.conf
#rename-command CONFIG ""
#rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52

#测试
192.168.213.129:63789> config get max--entries*
config get max--entries* (error) ERR unknown command 'config'

设置启动日志

[root@redis ~]# vim /data/redis/conf/redis.conf
logfile "/data/redis/log/logs"

Redis配置文件详解

#yes作为守护进程后台运行
daemonize yes
#当redis作为守护进程运行的时候,它会把pid默认写到/var/run/redis.pid 文件里面,你也可以指定写入的位置
pidfile /var/run/redis_6379.pid

发布订阅

创建订阅频道redisChat

192.168.213.129:6379> subscribe redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1

客户端订阅

192.168.213.129:6379> psubscribe redisChat
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "redisChat"
3) (integer) 1

发布消息

192.168.213.129:6379> publish redisChat "hello world"
(integer) 2

客户端会收到消息

1) "pmessage"
2) "redisChat"
3) "redisChat"
4) "hello world"

keepalived实现redis主从高可用

方案说明  

两台机器(称为A和B),以统一的VIP对外提供服务 
1.正常情况下,A和B都启动,B会把A的数据同步过来(B is slave of A) 
2.当A挂了后,VIP漂移到B;B的keepalived 通知redis 执行:slaveof no one,由B提供服务 
3.当A起来后,VIP不切换,仍在B上面;而A的keepalived 通知redis 执行slaveof B,开始把B的数据同步过来(A is slave of B) 
4.依此循环 

也就是说: 
当两台机器都正常时,一个为master,一个为slave; 
当master挂掉时,slave升级为master; 
当原master再次起来时,它不会抢占,而是作为slave;依此循环 

https://blog.51cto.com/changeflyhigh/1953688 keepalived实现redis主从高可用

Redis架构对比

模式 架构图 优点 缺点 应用场景 说明
主从复制 image.png 1、实现数据冗余,提高数据可靠性。2、读写分离,提高系统性能。3、配置简单,易于实现。(SLAVEOF MASTER-IP MASTER-PORT) 1、主节点故障时,需要手动切换到从节点,故障恢复时间较长。2、主节点承担所有写操作,可能成为性能瓶颈。3.无法实现数据分片,受单节点内存限制。 1、数据备份和容灾恢复 2、读写分离 3、在线升级和扩展 主节点处理写操作,并实时同步到从节点,主从节点都可以读取数据。
哨兵模式 image.png 1、自动故障转移 2、数据冗余 3、读写分离 1、配置和管理相对复杂。2、依然无法实现数据分片,受单节点内存限制。 1、高可用性要求较高的场景。2、数据备份和容灾恢复 在主从复制基础上加入哨兵节点,实现了自动故障转移。哨兵节点监控主节点和从节点的运行状态,当主节点发生故障时,哨兵节点会自动在从节点中选举出一个新的主节点,并通知其他从节点和客户端(客户端遍历sentinel节点集合,获取可用的sentinel节点),实现故障转移。
Cluster模式 image.png 1、数据分片 2、负载均衡 3、自动故障转移 1、配置和管理较复杂 2、复杂操作受到限制(如在一个事务中操作多个key) 1、大规模数据存储:通过数据分片,突破单节点内存限制。 2、高性能要求场景:通过负载均衡,提高系统性能。3、高可用性要求场景 通过数据分片和分布式存储实现了负载均衡和高可用性,Redis Cluster将数据分为16384个槽位,每个节点负责管理一部分槽位,Cluster根据键的哈希值将请求路由到相应的节点。

https://zhuanlan.zhihu.com/p/624144774?utm_id=0 详解Redis三大集群模式,轻松实现高可用

主从复制架构-从库只能读,不能写

方式1:启动时命令行指定master

(1)redis-server --slaveof,配置当前服务成为某个Redis服务的slave
实例1:目前有两个redis实例,一个基于当前主机的6379端口,一个基于当前主机的6380端口,将6379端口机器设置成6380的slave

[root@redis ~]# /data/redis/bin/redis-server /data/redis/conf/6380.conf
[root@redis ~]# ps -ef|grep redis
root      13189      1  0 15:23 ?        00:00:00 ./redis-server *:6380
root      13220      1  0 15:24 ?        00:00:00 /data/redis/bin/redis-server *:6379
root      13226   1260  0 15:24 pts/0    00:00:00 grep --color=auto redis
#加上--slaveof参数可启动一个从节点
[root@redis ~]# /data/redis/bin/redis-server --port 6379 --slaveof 192.168.213.129 6380
14713:S 08 Mar 2020 15:53:19.078 * MASTER <-> REPLICA sync: Finished with success

测试

127.0.0.1:6380> set a linux
OK
127.0.0.1:6379> get a
"linux"
127.0.0.1:6379> set b apache
(error) READONLY You can't write against a read only replica.

在主库上创建数据正常,在从库上创建数据将报错,get的时候,能够获取到主库上创建的值,因此,从库只能读,不能写

方式2:运行中指定master

(2)slaveof host port命令,将当前服务器状态从master修改为别的服务的slave

redis > SLAVEOF 192.168.213.122 6379	#将服务器转换为slave
redis > SLAVE OF NO ONE	#将服务器重新恢复到Master,不会丢弃已经同步的数据

实例2:已有一台master实例:127.0.0.1: 8000处于正常工作状态,接受读写请求,由于单台机器的压力过大,再启动一个slave实例:127.0.0.1: 8001来分担master的读压力

127.0.0.1:8001> SLAVEOF 127.0.0.1 8000

方式3:配置文件中指定master

启动时,服务器读取配置文件,并自动成为指定服务器的从服务器
实例3: Redis主从结构支持一主多从(所有从节点的配置都一样,只需要额外修改从节点中redis的配置文件中的slaveof属性即可)

#replicaof <masterip> <masterport>
replicaof 127.0.0.1 6379

Sentinel哨兵模式

Sentinel(哨兵)模式主要用于监控Redis集群中的Master状态,是Redis的高可用性解决方案,被集成在Redis 2.4之后的版本中,实现了自动故障转移和实时监控。
Sentinel作为一个独立的进程运行,并通过发送命令来监控多个Redis实例。当主服务器(Master)进入下线状态时,Sentinel可以自动将下线主服务器属下的某个从服务器(Slave)升级为新的主服务器,替代已下线的主服务器继续处理请求。

redis一主一从+三哨兵

image.png

https://blog.51cto.com/changeflyhigh/1953688 部署redis主从高可用集群

Sentinel配置实例

哨兵模式架构

27b968f47020d1d8dce21d107c98268.png
监控Monitoring:

  1. Sentinel会不断检查Master和Slaves是否正常
  2. 每一个Sentinel可以监控任意多个Master和该Master下的Slaves

主机规划

Redis服务的默认端口号是6379,哨兵模式下,哨兵使用的默认端口是26379。

主机名 IP 服务端口 哨兵端口
redis1 10.24.24.32 7701 7800
redis2 10.24.24.33 7701 7801
redis3 10.24.24.34 7701 7802

创建配置文件

redis.conf文件

#bind 0.0.0.0
protected-mode no
port 7701
tcp-backlog 511
timeout 300
tcp-keepalive 300
daemonize yes
supervised no
pidfile "/var/run/redis_7701.pid"
loglevel notice
logfile "/usr/local/redis/log/redis-7701.log"
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename "dump-7701.rdb"
dir "/usr/local/redis/log"

replica-serve-stale-data yes
masterauth "xwvIO@R7vlwD"

replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
requirepass "xwvIO@R7vlwD"
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4kb
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes

# Generated by CONFIG REWRITE
maxclients 4064

user default on sanitize-payload #1f54e616942427dfc6d90039bc6503a818682ac393cf2a58ee4ed6ae13db8abd ~* &* +@all

replicaof 10.24.24.33 7701

sentinel.conf文件

sentinel.conf是动态变化的,至少包含一个监控配置选项,用于指定被监控Master的相关信息
Sentinel会根据Master配置,自动发现Master的Slaves

选项 含义
name redis主服务名称,可以自行命名,但是在一个sentine1网络中,一个redis主服务只能有一个名称
ip redis主服务的IP地址
port redis主服务的端口号
quorum 表示要将这个主服务器判断为失效并下线至少需要2个Sentinel同意
protected-mode no	#关闭保护模式(默认yes,若想从远程连接redis集群,需要将redis-node和sentinel的protected-mode都修改为no,并增加密码验证或是IP限制等保护机制)

port 7800	#sentinel默认端口为26379
daemonize yes
sentinel deny-scripts-reconfig yes
#Sentinel monitor <name> <ip> <port> <quorum>
#quorum是sentinel通过投票后认为mater宕机的数量,此处为至少1个
sentinel monitor master 10.24.24.33 7701 2
logfile "/usr/local/redis/log/sentinel-7800.log"
sentinel down-after-milliseconds master 10000
sentinel failover-timeout master 60000
sentinel auth-pass master xwvIO@R7vlwD
dir "/usr/local/redis/log"

# Generated by CONFIG REWRITE
maxclients 4064
pidfile "/var/run/redis.pid"
user default on nopass sanitize-payload ~* &* +@all
[root@redis conf]# mkdir sentinel
[root@redis conf]# vim sentinel/sentinel1.conf
port 26379
sentinel monitor s1 127.0.0.1 6379 2
#protected-mode no	远程连接需要,同时要设置保护密码
[root@redis conf]# vim sentinel/sentinel2.conf
port 26380
sentinel monitor s1 127.0.0.1 6379 2
#protected-mode no

[root@redis ~]# nohup /data/redis/bin/redis-sentinel /data/redis/conf/sentinel/sentinel1.conf >> /data/redis/log/sentinel1.log 2>&1 &
[1] 19581
[root@redis ~]# nohup /data/redis/bin/redis-sentinel /data/redis/conf/sentinel/sentinel2.conf >> /data/redis/log/sentinel2.log 2>&1 &
[2] 19593
[root@redis ~]# netstat -tunpl|grep redis
tcp        0      0 0.0.0.0:26379           0.0.0.0:*               LISTEN      19581/redis-sentine
tcp        0      0 0.0.0.0:6379            0.0.0.0:*               LISTEN      18692/redis-server
tcp        0      0 0.0.0.0:26380           0.0.0.0:*               LISTEN      19593/redis-sentine
tcp        0      0 0.0.0.0:6380            0.0.0.0:*               LISTEN      18720/redis-server
tcp        0      0 0.0.0.0:6381            0.0.0.0:*               LISTEN      18728/redis-server
tcp6       0      0 :::26379                :::*                    LISTEN      19581/redis-sentine
tcp6       0      0 :::26380                :::*                    LISTEN      19593/redis-sentine

后端应用连接Redis服务配置

后端应用程序从sentinel连接Redis服务

#修改redis-sentinel.properties
redis.masterName=master
redis.sentinels=10.24.24.32:7800,10.24.24.33:7801,10.24.24.34:7802
redis.timeout=10000
redis.password=passwd

Redis Sentinel故障转移

image.png

https://blog.csdn.net/chen772209/article/details/106890849/ Redis(八)--Redis哨兵模式
https://blog.csdn.net/m0_58892222/article/details/130083534 Redis学习笔记——哨兵(sentinel)

高可用验证

#模拟master破坏
[root@redis ~]# kill 18692
#查看日志,此时6381实例已被选举为master, 6380实例连接到了6381实例上
[root@redis ~]# cat /data/redis/log/6380.log
18720:S 08 Mar 2020 17:51:01.760 * REPLICAOF 192.168.213.129:6381 enabled (user request from 'id=13 addr=127.0.0.1:38526 fd=14 name=sentinel-4cd7af2d-cmd age=1041 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=302 qbuf-free=32466 obl=36 oll=0 omem=0 events=r cmd=exec')
18720:S 08 Mar 2020 17:51:01.761 # CONFIG REWRITE executed with success.
18720:S 08 Mar 2020 17:51:02.053 * Connecting to MASTER 192.168.213.129:6381
#再次启动6379实例
[root@redis conf]# /data/redis/bin/redis-server 6380.conf
#由6379实例的日志可知,其数据从6381上同步
[root@redis ~]# cat /data/redis/log/6379.log
Connecting to MASTER 127.0.0.1:6381

登录redis:/usr/local/redis/bin/redis-cli -h 10.24.24.32 -p 7701 -a passwd
查看当前节点redis信息:10.24.24.32:7701> INFO replication
登录sentinel:/usr/local/redis/bin/redis-cli -h 10.24.24.32 -p 7800 -a passwd
查看当前节点sentinel信息:10.24.24.32:7800> info Sentinel
获取主节点信息:10.24.24.32:7800> sentinel get-master-addr-by-name master 会返回主节点ip和端口

Redis Cluster集群

集群模式介绍

    Redis3.0版本以上,提供集群功能
Redis Cluster集群是一个提供在多个Redis节点间共享数据的程序集,通过分区share来提供一定程度的可用性,在实际环境中当某一个节点宕机或者不可达的请况下继续处理命令

优势:自动分割数据到不同节点上,集群部分节点失败或者不可达的情况下能够继续处理命令。

主机名 IP 数据存储-hash槽 数据库角色 端口
redis1 10.x.x.1 0-5460 master1 6379
slave3 6380
redis2 10.x.x.2 5461-10922 master2 6379
slave1 6380
redis3 10.x.x.3 10923-16383 master3 6379
slave2 6380

redis1主机宕机,对该故障节点进行主从切换,redis3上的slave1提升为主master1-新,继续提供读写服务,redis1主机恢复后,master1成为slave1-新。
71d39b3fc2ceb441f01d3bf32dbf37d.png

https://blog.csdn.net/royal1235/article/details/127955405 Redis学习(二)之 Redis Cluster集群
https://blog.csdn.net/chushoufengli/article/details/110879106 Redis Cluster 限制及解决方案

Redis集群的数据分片

Redis使用hash槽,每个key通过CRC16校验后对16384模运算来决定放置在哪个槽,集群的每一个节点负责一部分hash槽,如当前集群有三个节点,数据hash运算除以16384取余决定数据存放的槽。
Redis集群并不能保证数据的强一致性,集群使用异步复制写操作过程,这意味着在实际中集群在特定条件下操作可能丢失一些数据。

节点A:包含0-5460号hash槽
节点B:包含5461-10922号hash槽
节点C:包含10923-16383号hash槽

>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
#将副本192.168.213.129:6381添加到192.168.213.122:7001
#即7001是6381的主
Adding replica 192.168.213.129:6381 to 192.168.213.122:7001
Adding replica 192.168.213.122:7003 to 192.168.213.129:6379
Adding replica 192.168.213.129:6380 to 192.168.213.122:7002
M: 4838bf825e942e4a1a79b61081ce749c8932dcb1 192.168.213.122:7001
   slots:[0-5460] (5461 slots) master
M: 5ade0db448b0a37c3d7330f11f2831bf83e54c00 192.168.213.122:7002
   slots:[10923-16383] (5461 slots) master
S: d088be29c75658b2aa4ed239eab5e7f13dbfc447 192.168.213.122:7003
   replicates 5875462598cefde5b92f3292a81aa0828656c566
M: 5875462598cefde5b92f3292a81aa0828656c566 192.168.213.129:6379
   slots:[5461-10922] (5462 slots) master
S: 5731e62cf465d5fe4cd47802566ce9d80dc308ef 192.168.213.129:6380
   replicates 5ade0db448b0a37c3d7330f11f2831bf83e54c00
S: 3fc2693b0e5b27776a9730e42e1e69f9b5eba9a3 192.168.213.129:6381
   replicates 4838bf825e942e4a1a79b61081ce749c8932dcb1

集群搭建过程

主机规划与系统配置

192.168.213.129 6379 6380 6381
192.168.25.122 7003 7004 7005
#为了方便使用创建集群命令,将其复制到/usr/local/bin
[root@redis src]# cp -p /opt/redis-5.0.4/src/redis-trib.rb /usr/local/bin
#添加系统路径
[root@redis src]# export PATH=$PATH:/data/redis/bin

同步时间
关闭防火墙
卸载mariadb-libs,postfix

修改配置文件

搭建并使用Redis集群,需要启动集群模式的实例

bind 192.168.213.129
port 6380
daemonize yes
1ogfile "/data/redis/log/6380.log"
pidfile /var/run/redis_6380.pid
cluster-enabled yes #启动集群模式
cluster-config-file nodes-6380.conf #指定集群文件(自动生成)
cluster-node-timeout 5000 #集群超时时间(毫秒)
appendonly yes

启动实例

[root@redis conf]# redis-server redis.conf
[root@redis conf]# redis-server 6380.conf
[root@redis conf]# redis-server 6381.conf

[root@redis1 conf]# redis-server redis.conf
[root@redis1 conf]# redis-server 7002.conf
[root@redis1 conf]# redis-server 7003.conf

创建集群

[root@redis1 ~]# redis-cli --cluster create 192.168.213.122:7001 192.168.213.122:7002 192.168.213.122:7003 192.168.213.129:6379 192.168.213.129:6380 192.168.213.129:6381 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.213.129:6381 to 192.168.213.122:7001
Adding replica 192.168.213.122:7003 to 192.168.213.129:6379
Adding replica 192.168.213.129:6380 to 192.168.213.122:7002
M: 4838bf825e942e4a1a79b61081ce749c8932dcb1 192.168.213.122:7001
   slots:[0-5460] (5461 slots) master
M: 5ade0db448b0a37c3d7330f11f2831bf83e54c00 192.168.213.122:7002
   slots:[10923-16383] (5461 slots) master
S: d088be29c75658b2aa4ed239eab5e7f13dbfc447 192.168.213.122:7003
   replicates 5875462598cefde5b92f3292a81aa0828656c566
M: 5875462598cefde5b92f3292a81aa0828656c566 192.168.213.129:6379
   slots:[5461-10922] (5462 slots) master
S: 5731e62cf465d5fe4cd47802566ce9d80dc308ef 192.168.213.129:6380
   replicates 5ade0db448b0a37c3d7330f11f2831bf83e54c00
S: 3fc2693b0e5b27776a9730e42e1e69f9b5eba9a3 192.168.213.129:6381
   replicates 4838bf825e942e4a1a79b61081ce749c8932dcb1
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 192.168.213.122:7001)
M: 4838bf825e942e4a1a79b61081ce749c8932dcb1 192.168.213.122:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 5ade0db448b0a37c3d7330f11f2831bf83e54c00 192.168.213.122:7002
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 5731e62cf465d5fe4cd47802566ce9d80dc308ef 192.168.213.129:6380
   slots: (0 slots) slave
   replicates 5ade0db448b0a37c3d7330f11f2831bf83e54c00
S: d088be29c75658b2aa4ed239eab5e7f13dbfc447 192.168.213.122:7003
   slots: (0 slots) slave
   replicates 5875462598cefde5b92f3292a81aa0828656c566
M: 5875462598cefde5b92f3292a81aa0828656c566 192.168.213.129:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 3fc2693b0e5b27776a9730e42e1e69f9b5eba9a3 192.168.213.129:6381
   slots: (0 slots) slave
   replicates 4838bf825e942e4a1a79b61081ce749c8932dcb1
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

向集群写入存储数据

[root@redis1 ~]# redis-cli -h 192.168.213.122 -p 7001 -c
192.168.213.122:7001> set name 123
-> Redirected to slot [5798] located at 192.168.213.129:6379
OK
192.168.213.129:6379> set name 456
OK
192.168.213.129:6379> quit
[root@redis1 ~]# redis-cli -h 192.168.213.129 -p 6379 -c
192.168.213.129:6379> get name
"456"
192.168.213.129:6379> quit
[root@redis1 ~]# redis-cli -h 192.168.213.129 -p 6380 -c
192.168.213.129:6380> get name
-> Redirected to slot [5798] located at 192.168.213.129:6379
"456"

主挂掉,对应的从会升级为主;一个服务器挂掉,另一个服务器会全部升级为主,客户端不会挂掉

Redis七大经典问题

问题 描述 解决思路
缓存雪崩 缓存同时大量失效,数据库短时间内承受大量请求 Redis 高可用,主从+哨兵,Redis cluster;缓存过期时间设置随机,防止同一时间大量数据过期;逻辑上永不过期,仅缓存标记失效更新数据;多级缓存,应用--->一级缓存--->二级缓存。
缓存穿透 缓存和数据库中都未命中,数据库短时间内承受大量请求 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;缓存和数据库中都未命中,可将key-value对写为key-null,设置短缓存有效时间,防止同key暴力攻击;采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,避免对底层存储系统的查询压力。
缓存击穿 并发高,同时缓存未命中,又同时去数据库去取数据,数据库压力瞬间增大(并发查同一条数据) 设置热点数据永远不过期,异步线程处理;加写回操作加互斥锁,查询失败默认值快速返回;缓存预热。
数据不一致 各种原因导致缓存更新失败,与DB 数据不一致,一些缓存节点产生脏数据 缓存更新失败的key写入mq,这些key在再次被查询时,重新从DB加载,并更新缓存;缓存时间适当调短,让缓存数据及早过期;不采用rehash漂移策略,而采用缓存分层策略,尽量避免脏数据产生。
数据并发竞争 缓存信息过期,但仍然有大量用户在查询(火车票,微博转发) 写回操作加互斥锁,查询失败默认值快速返回;对缓存数据保持多个备份,减少并发竞争的概率。
热点key问题 特殊突发事件(比如奥运、春节这些重大活动或节日,热搜) 提前评估出可能的热key,通过 Spark,对应流任务进行实时分析,及时发现新发布的热点 key,n 个 key 分散存在多个缓存节点,把热 key 的请求打散,避免一个缓存节点过载。
BigKey问题 用户个人信息缓存,长微博 Redis底层数据结构根据Value进行数据结构的重新选择;扩展新的数据结构,进行序列化构建,通过restore一次性写入;将大key分拆为多个key,设置较长的过期时间。

问题

redis Connection refused 远程连接错误

安装Redis时,其配置文件默认绑定本地ip,bind 127.0.0.1,把它注释掉,再重启redis服务器,就可以进行远程连接了

[root@redis ~]# vim /data/redis/conf/redis.conf
#bind 127.0.0.1
[root@redis ~]# /data/redis/bin/redis-server /data/redis/conf/redis.conf

在群集模式下不允许复制指令

将配置文件中的 replicaof 127.0.0.1 6379 注释掉即可

[root@redis1 conf]# redis-server 7001.conf
*** FATAL CONFIG FILE ERROR ***
Reading the configuration file, at line 1381
>>> 'replicaof 127.0.0.1 6379'
replicaof directive not allowed in cluster mode

https://mp.weixin.qq.com/s/8-Lf5KiyclW77uQwrYDTCw 这些年背过的面试题——Redis篇

posted @ 2020-06-08 18:01  遇见你我看到光  阅读(468)  评论(0编辑  收藏  举报