Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 客户端可以订阅任意数量的频道。
频道:channel1
订阅者:client1、client2、client5
当有新消息通过
publish
命令发送给频道channel1
时,这个消息就会被发送给订阅的三个客户端,只要订阅了就都会收到消息。
订阅频道:
subscribe channel [channel....]:订阅给定的一个或多个频道的信息
psubscribe pattern [pattern...]:订阅一个或多个符合给定模式的频道
发布频道:
publish channel message:将信息发送到指定的频道
退订频道:
unsubscribe channel:退订给定的频道
Redis 事务可以一次执行多个命令(允许在一次单独的步骤中执行一组命令),两个重要的保证:
# 标记一个事务块的开始
multi
# 执行所有事务块内的命令
exec
# 取消事务,放弃执行事务块内的所有命令
discard
# 取消 watch 命令对所有 key 的监视
unwatch
# 监视一个(或多个)key,如果在事务执行之前这个(或这些)key 被其他命令所改动,那么事务将被打断
watch key1 key...
- 开始事务
- 命令入队
- 执行事务
例如转账操作:A向B转账
- 输入 multi 命令开始,输入的命令都会依次进入命令队列中,但是不会执行。
- 直到输入 exec 命令后,Redis 会将之前的命令队列中的命令依次执行。
- 命令队列的过程中可以通过discard来放弃队列运行。
如果执行的某个命令出现了错误,则只有报错的命令不会被执行,其他的命令不受影响,都会执行,不会像 MySql一样进行事务的回滚。
队列中的某个命令出现了报告错误,执行时整个的所有队列都会被取消。
监视一个或者多个 key,如果在事务执行之前这些 key 被改变,那么事务将被打断。
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存中数据丢失。
Redis 提供了两种持久化方式
- RDB(默认方式):一定时间取存储文件。
- AOF:默认每秒去存储历史命令。
内存:
高效、断电(关机)数据会丢失。
硬盘:
读写速度慢于内存,断电数据不会丢失。
RDB 是 Redis DataBase 的缩写,功能核心函数 rdbSave(生成 RDB 文件)和 rdbLoad(从文件加载内存)两个函数
RDB:
是 Redis 的默认持久化机制。快照是默认的持久化方式。这种方式就是讲内存中数据以快照的方式写入到二进制文件中,默认的文件名为 dump.rdb
,恢复数据将文件移动到 redis 安装目录并启动服务即可。
优缺点:
- 快照保存数据极快、还原数据极快,适用于灾难备份。
- 小内存机器不适合使用,RDB 机制符合要求就会照快照。
快照条件:
服务器正常关闭时 ./bin/redis-cli shutdown
key 满足一定条件,会进行快照
vim redis.conf搜索save
:/save
save 900 1 #每900秒至少1个key发生变化,就进行快照
save 300 10 #每300秒至少10个key发生变化,就进行快照
save 60 10000 #每60秒至少10000个key发生变化,就进行快照
由于快照的方式是在一定间隔时间做一次的,如果 redis 意外宕机的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用 AOF 持久化方式。
Append-only file:
AOF 比快照方式有更好的持久化性,是由于在使用 AOF 持久化方式时,Redis会将每一个收到的写命令都通过 write 函数追加到文件中(默认是appendonly.aof)。当 Redis 重启时会通过重新执行文件中保存的写命令在内存中重建整个数据库的内容。
每当执行服务器(定时)任务或者函数时 flushAppendOnlyFile 函数都会被调用,这个函数执行以下两个工作AOF保存:
write:
根据条件,将 aof_buf 中的缓存写入到 AOF 文件。
save:
根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
有三种方式(默认是:每秒 fsync 一次):
# 启用 aof 持久化方式
appendonly yes
# 收到写命令就立即写入磁盘,最慢,但是保证完全的持久化
appendfsync always
# 每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中
appendfsync everysec
# 完全依赖os,写入aof文件,不等待磁盘同步,性能最好,但持久化没有哦保证
appendfsync no
产生的问题:
持久化的文件会越来越大,例如调用 incr tset 命令 100 次,文件中必须保存全部的 100 条命令,其实有 99 条都是多余的。
对强一致性要求比较高的,应采用实时同步方案,即查询缓存查询不到再从数据库查询,保存数据库信息到缓存;更新缓存时,先更新数据库,再将缓存的设置过期(建议不要去更新缓存内容,直接设置缓存过期)。
@Cacheable:
查询时使用,注意 long 类型需要转换为 String 类型,否则会抛出异常。
@CachePut:
更新时使用,使用此注解,一定会从数据库上查询数据。
@CacheEvict:
删除时使用
@Caching:
组合用法具体用法请查看此篇文章:SpringBoot与缓存
非实时同步:
实时:一方修改,另一方同步修改
非实时:一方修改,另一方不需要修改
1个文章 1分钟内被点击10万次(点击数10万)这时候就需要定时任务(比如凌晨触发):将redis中的值查询出来更新到mysql中
对于并发程度较高的,可以采用异步队列的方式同步,可以采用kafka等消息中间件处理消息生产和消费。
双11时 5000万人--> 支付成功后--> 生成订单--> 一小部分人先发货--> 4000万人[中间件异步队列] 生成物流 -->物流发货
消息中间件有:activemq rabbitmq zeromq rockermq kafka
缓存穿透是指查询一个一定不存在的数据,由于缓存数据不存在,需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决办法:
- 对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该 key 对应的数据 insert 了之后清理缓存。
- 对一定不存在的key进行过滤。
当缓存服务器重启或者大量缓存集中在某一时间段失效,发生大量的缓存穿透,所有的查询都集中在数据库上,造成缓存雪崩。
解决办法:
1、加锁排队
在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2、数据预热
可以通过缓存 reload 机制,预先去更新缓存,在即将发生大并发访问前手动触发加载缓存不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
某个 key 访问非常频繁,当 key 失效的时候有大量线程来构建缓存,导致负载增加,系统崩溃。
解决办法:
1、使用锁,单机使用 synchronized,lock 等,分布式用分布式锁。
2、缓存过期时间不设置,而是设置在 key 对应的 value 里。如果检测到存在的时间超过过期时间则异步更新缓存。
3、在 value 设置一个比过期时间 t0 小的过期时间值 t1,当 t1 过期的时候,延长 t1 并做更新缓存操作。
4、设置标签缓存,标签缓存设置过期时间,标签缓存过期后,需异步地更新实际缓存。
一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的,原因如下:
- 结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;(容错性)
- 容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256G,也不能将所有内容用作Redis储存内存,一般来说,单台Redis最大使用内存不应该超过20G
1、高可用
通常用来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性。(一直能使用)。
2、高并发
是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。高并发相关常用的一些指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等。
响应时间:
系统对请求作出响应的时间。例如系统处理一个Http请求需要200ms,这个200ms就是系统的响应时间。吞吐量:
单位时间内处理的请求数量。QPS:
每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。并发用户数:
同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。
提升系统的并发能力
提高系统并发能力的方式,方法论上主要有两种:垂直扩展(Scale Up)与水平扩展(Scale Out)。
垂直扩展
垂直扩展:提升单机处理能力,垂直扩展的方式又分为两种:
(1)增强单机硬件性能,例如:增加CPU核数如32核,升级更好的网卡如万兆,升级更好的硬盘如SSD,扩充硬盘容量如2T,扩充内存如128G;
(2)提升单机架构性能,例如:使用Cache来减少IO次数,使用异步来增加单服务吞吐量,使用无锁数据结构来减少响应时间;
总结:
这两种方式的不足之处,单机的性能总是有极限的,所以互联网分布式架构设计高并发终极解决方案还是水平扩展。
水平拓展
水平扩展:只要增加服务器数量,就能线性扩充系统性能。水平扩展对系统架构设计是有要求的,难点在于如何在架构各层进行可水平扩展的设计。
3、高性能
高性能(High Performance)就是指程序处理速度快,所占内存少、CPU低。
1、概述
一个 Redis 服务可以有多个该服务的复制品,这个 Redis 服务称为 Master,其他复制称为 Slaves。
如图所示,将一台Redis服务器作主库(Master),其他三个作为从库(Slave),主库只负责写数据,每次有数据更新都将更新的数据同步到它所有的从库,而从库只负责读数据。
2、优点
- 读写分离,不仅可以提高服务器的负载能力,并且可以根据读请求的规模自由增加或者减少从库的数量
- 数据被复制成看好几份,就算有一台机器出现故障,也可以使用其他机器的数据快速恢复
3、注意
在Redis主从模式中,一台主库可以拥有多个从库,但是一个从库只能隶属一个主库。
4、主从复制配置
拷贝多个redis.conf文件
主配置文件:
# 端口
port 6379
# 设置守护进程启动redis服务
daemonize no 修改为 daemonize yes
# (注释掉)允许本机外的机器访问Redis服务
# bind 127.0.0.1
# 设置密码 设定数据库密码(保证服务安全/有些情况下不设定密码是无法进行远程连接访问的)
requirepass 123456
# 保存并退出
:wq
# 服务端启动
./bin/redis-server ./master/redis.conf(配置文件路径)
# 查看是否启动
ps -ef|grep redis
# 客户端启动(如果需要在远程Redis上执行命令,执行下面命令;如果本地客户端启动 -h 127.0.0.1 或者不写)
./bin/redis-cli -h 127.0.0.1 -p 6379 -a 123456
从配置文件:
# 修改端口
port 6380
# 设置守护进程启动redis服务
daemonize no 修改为 daemonize yes
# (注释掉)允许本机外的机器访问Redis服务
# bind 127.0.0.1
# 指定主服务器IP地址和端口号
replicaof IP地址 端口号
# 主服务器有密码,则需要配置是,无密码不需要配置
masterauth 123456
# 设置密码 设定数据库密码(保证服务安全/有些情况下不设定密码是无法进行远程连接访问的)
requirepass 123456
# 保存并退出
:wq
# 启动从数据库
./bin/redis-server ./slave/redis.conf
# 客户端启动
./bin/redis-cli -h 127.0.0.1 -p 6380 -a 123456
# 查看docker中master的ip地址
docker inspect --format='{{.NetworkSettings.IPAddress}}' myredis
测试:
主数据库可以读和写,从数据库只能读数据
1、概述
Redis-Sentinel (哨兵模式)是高可用解决方案,当 redis 在做 master-slave 的高可用方案时,假如 master 宕机了,redis 本身(以及其很多客户端)都没有实现自动进行主备切换,而 redis-sentinel 本身也是独立运行的进程,可以部署在其他与 redis 集群可通讯的机器中监控 redis 集群。哨兵就是为监控 Redis 系统的运行状况而存在的。
2、哨兵特点
- 不时地监控 redis 是否按照预期良好地运行。
- 如果发现某个 redis 节点运行出现状况,能够通知另外一个进程(例如它的客户端)
能够进行自动切换,当一个 master 节点不可用时,能够选举出 master 的多个 slave 中的一个来作为新的 master,其它的 slave 节点会将它所追随的 master 的地址改为被提升为 master 的 slave 的新地址。
- 哨兵为客户端提供服务发现,客户端连接哨兵,哨兵提供当前 master 的地址然后提供服务,如果出现切换,也就是master 挂了,哨兵会提供客户端一个新地址
1、概述
Redis Cluster 是社区版推出的 Redis 分布式集群解决方案,主要解决 Redis 分布式方面的需求,比如,当遇到单机内存,并发和流量等瓶颈的时候,Redis Cluster 能起到很好的负载均衡的目的。
2、集群
Redis 集群搭建的方式有很多种,但从 redis3.0 之后版本支持 redis-cluster 集群,至少需要3(Master)+ 3(Slave)才能建立集群。 Redis-Cluster 采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
说明:
Redis Cluster集群节点最小配置6个节点以上(3主3从),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用
3、集群特点
4、Redis Cluster 容错
容错性:
指软件检测应用程序所运行的软件或硬件中发生的错误并从错误中恢复的能力,通常可以从系统的可靠性、可用性、可测性等几个方面来衡量。
1、什么时候判断 master 不可用?
投票机制,投票过程是集群中所有 master 参与,如果半数以上 master 节点与 master 节点通信超时(cluster-node-timeout),认为当前 master 节点挂掉。
2、什么时候整个集群不可用(cluster_state:fail)?
如果集群任意 master 挂掉,且当前 master 没有 slave,集群进入 fail 状态,也可以理解成集群的 slot 映射 【0-16383】不完整时进入 fail 状态,如果集群超过半数以上 master 挂掉,无论是否有 slave,集群进入 fail 状态。
5、Redis Cluster节点分配
Redis Cluster采用虚拟槽分区,所有的键根据哈希函数映射到 0-16383 个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。
三个主节点分别是:A,B,C三个节点,它们可以是一台机器上的三个端口,也可以是三台不同的服务器。那么,采用哈希槽(hash slot)的方式来分配 16384 个 slot 的话,它们三个节点分别承担的 slot 区间是
节点A覆盖:0-5460
节点B覆盖:5461-10922
节点C覆盖:10923-16383
6、Redis Cluster集群搭建
Redis集群需要至少三个 master 节点,并且给每个 master 再搭建一个 slave 节点,总共 6 个 Redis 实例
搭建步骤:
# 1、创建Redis节点安装目录
mkdir /usr/local/redis_cluster
# 2、创建 6 个文件夹
mkdir 7000 7001 7002 7003 7004 7005
# 3、将 redis-conf 分别拷贝到这 6 个文件夹下
cp redis.conf /usr/local/redis_cluster/7000
修改配置文件
# 关闭保护模式,用于公网访问
protected-mode no
# 修改端口
port 7000
# 开启集群模式
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
# 后台启动
daemonize yes
pidfile /var/run/redis_7000.pid
logfile "7000.log"
# dir /redis/data
# 此处绑定ip,可以是阿里内网IP和本地IP也可以直接注释掉该项
# bind 127.0.0.1
# 设置连接主节点密码
masterauth 123456
# 设置redis密码,各个节点请密码保持一致
requirepass 123456
依次创建文件夹并复制配置文件
# 将配置文件中的7000全部替换成新的对应的端口号
:%s/old/new
依次启动6个节点
# 将安装redis的目录下的bin复制到cluster下,方便启动服务端
cd /usr/local/redis
# 复制
cp -r /usr/local/redis/bin /usr/local/redis_cluster
# 依次启动各个节点
./bin/redis-server ./7000/redis.conf
./bin/redis-server ./7001/redis.conf
./bin/redis-server ./7002/redis.conf
./bin/redis-server ./7003/redis.conf
./bin/redis-server ./7004/redis.conf
./bin/redis-server ./7005/redis.conf
创建集群
Redis 5版本后,通过Redis-cli客户端命令来创建集群
./bin/redis-cli --cluster create -a 123456 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1
输入yes继续构建,下图表示构建成功
1、任意连接一个集群的节点:
./bin/redis-cli -h -c -p 7000 -a 123456
# -c:表示连接集群内部,不是单个节点
redis cluster在设计的时候,就考虑了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
2、命令查看集群效果
info replication
cluster nodes
每个 Redis 的节点都有一个ID值,此ID值将此特定 Redis 实例永久使用,以便实例在集群上下文中具有唯一的名称。每个节点都会记住使用此ID的其他每个节点,而不是通过IP或端口。IP地址和端口可能会发生变化,但唯一的节点标识符在节点的整个生命周期内都不会改变。
3、测试数据
发生存数据和读数据会从不同的节点存储和读取
Redis Cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会从这些从节点选取一个来充当主节点,从而保证集群不会挂掉。
集群有ABC三个主节点,如果这三个节点都没有加入从节点,如果B节点挂掉,我们就无法访问整个集群了。A和C的slot(槽)也无法访问了。所以在建立集群的时候,一定要为主节点都添加从节点,那么即使B挂掉系统也可以继续正确工作。B1节点就会替代B节点成为新的主节点,集群将会继续正确地提供服务。当B重新开启后,它就会变成B1的从节点。如果B和B1同时挂了,Redis集群就无法继续正确的提供服务了。
1、在集群目录/usr/local/redis_cluster下新建关闭集群脚本 shutdown.sh
/usr/local/redis_cluster/bin/redis-cli -c -h 127.0.0.1 -p 7000 -a 123456 shutdown
/usr/local/redis_cluster/bin/redis-cli -c -h 127.0.0.1 -p 7001 -a 123456 shutdown
/usr/local/redis_cluster/bin/redis-cli -c -h 127.0.0.1 -p 7002 -a 123456 shutdown
/usr/local/redis_cluster/bin/redis-cli -c -h 127.0.0.1 -p 7003 -a 123456 shutdown
/usr/local/redis_cluster/bin/redis-cli -c -h 127.0.0.1 -p 7004 -a 123456 shutdown
/usr/local/redis_cluster/bin/redis-cli -c -h 127.0.0.1 -p 7005 -a 123456 shutdown
2、在集群目录/usr/local/redis_cluster下新建启动集群脚本 start.sh
/usr/local/redis_cluster/bin/redis-server ./7000/redis.conf
/usr/local/redis_cluster/bin/redis-server ./7001/redis.conf
/usr/local/redis_cluster/bin/redis-server ./7002/redis.conf
/usr/local/redis_cluster/bin/redis-server ./7003/redis.conf
/usr/local/redis_cluster/bin/redis-server ./7004/redis.conf
/usr/local/redis_cluster/bin/redis-server ./7005/redis.conf
3、将文件变成可执行文件
chmod u+x start.sh
chmod u+x shutdown.sh
4、运行可执行文件
# 启动集群
./start.sh
# 关闭集群
./shutdown.sh
# 查看redis进程
ps -ef|grep redis
1、搭建步骤
# 1、创建Redis节点安装目录
mkdir /usr/local/docker/docker_cluster
# 2、创建 6 个文件夹
mkdir 6381 6382 6383 6384 6385 6386
# 3、将 redis-conf 分别拷贝到这 6 个文件夹下
cp redis.conf /usr/local/docker/docker_cluster/6381
2、修改配置文件
# 关闭保护模式,用于公网访问
protected-mode no
# 修改端口
port 6381
# 开启集群模式
cluster-enabled yes
cluster-config-file nodes-6381.conf
cluster-node-timeout 5000
# 日志文件
pidfile /var/run/redis_6381.pid
logfile "6381.log"
# dir /redis/data
# 此处绑定ip,可以是阿里内网IP和本地IP也可以直接注释掉该项
# bind 127.0.0.1
# 设置连接主节点密码
masterauth 123456
# 设置redis密码,各个节点请密码保持一致
requirepass 123456
3、依次复制和修改每个配置文件
# 1、批量替换
:%s/old/new
4、编写docker-compose.yml文件
# 版本号任意都行
version: '3'
services:
'6381':
container_name: '6381'
image: redis
command: redis-server /etc/usr/local/redis.conf
network_mode: "host"
volumes:
- /usr/local/docker/docker_cluster/6381/redis.conf:/etc/usr/local/redis.conf
- /usr/local/docker/docker_cluster/6381/data:/data
'6382':
container_name: '6382'
image: redis
command: redis-server /etc/usr/local/redis.conf
network_mode: "host"
volumes:
- /usr/local/docker/docker_cluster/6382/redis.conf:/etc/usr/local/redis.conf
- /usr/local/docker/docker_cluster/6382/data:/data
'6383':
container_name: '6383'
image: redis
command: redis-server /etc/usr/local/redis.conf
network_mode: "host"
volumes:
- /usr/local/docker/docker_cluster/6383/redis.conf:/etc/usr/local/redis.conf
- /usr/local/docker/docker_cluster/6383/data:/data
'6384':
container_name: '6384'
image: redis
command: redis-server /etc/usr/local/redis.conf
network_mode: "host"
volumes:
- /usr/local/docker/docker_cluster/6384/redis.conf:/etc/usr/local/redis.conf
- /usr/local/docker/docker_cluster/6384/data:/data
'6385':
container_name: '6385'
image: redis
command: redis-server /etc/usr/local/redis.conf
network_mode: "host"
volumes:
- /usr/local/docker/docker_cluster/6385/redis.conf:/etc/usr/local/redis.conf
- /usr/local/docker/docker_cluster/6385/data:/data
'6386':
container_name: '6386'
image: redis
command: redis-server /etc/usr/local/redis.conf
network_mode: "host"
volumes:
- /usr/local/docker/docker_cluster/6386/redis.conf:/etc/usr/local/redis.conf
- /usr/local/docker/docker_cluster/6386/data:/data
5、安装docker-compose
# 1、执行安装
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# 2、修改目录权限
chmod +x /usr/local/bin/docker-compose
# 3、启动
docker-compose up -d
# 4、查看容器是否启动
docker ps
6、创建集群
docker exec -it 6381 redis-cli --cluster create -a 123456 101.200.46.192:6381 101.200.46.192:6382 101.200.46.192:6383 101.200.46.192:6384 101.200.46.192:6385 101.200.46.192:6386 --cluster-replicas 1
7、集群验证
docker exec -it 6381 redis-cli -h 127.0.0.1 -c -p 6381 -a 123456
9、SpringBoot连接集群
SpringBoot2.x中使用Lettuce操作Redis参考:Redis基础篇
1、修改application.yml配置文件
server:
port: 8081
spring:
redis:
password: 123456
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000
shutdown-timeout: 100
cluster:
nodes:
- 101.200.46.192:6381
- 101.200.46.192:6382
- 101.200.46.192:6383
- 101.200.46.192:6384
- 101.200.46.192:6385
- 101.200.46.192:6386
2、测试
@SpringBootTest
class BootLettuceApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void t2(){
redisTemplate.opsForValue().set("redis-cluster","RedisCluster集群");
Object o = redisTemplate.opsForValue().get("redis-cluster");
System.out.println("RedisCluster集群验证:" + o);
}
}