Redis
导航:
一. Redis简介
1.1 主流应用架构

解读: 首先使用缓存可以快速查找所需要的内容,不必每次都请求数据库;1-2则表示直接查询缓存;如果缓存中没有的话,就从数据库中查询到内容,然后返回存入Redis中再返回给客户端,这样下次请求此数据的时候就可以直接从缓存中查询了–>3,4,5,6 ; 而熔断7,8则表示直接对外提供一个服务,当存储器挂掉了,那么也可以直接从缓存中取值,然后直接返回结果;
1.2 缓存中间件–Memcache和Redis的区别?
-
Memcache: 代码层次类似Hash
- 支持简单数据类型
- 不支持数据持久化存储
- 不支持主从
- 不支持分片
-
Redis:
- 数据类型丰富
- 支持数据磁盘持久化存储
- 支持主从
1.3 为什么Redis能这么快?
- 完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高;
- 数据结构简单,对数据操作也简单;
- 采用单线程,单线程也能处理高并发请求,想多核也可启动多实例
- 使用多路I/O复用模型,非阻塞IO
- Redis采用I/O多路复用函数: epoll/kqueue/evport/select?
- 因地制宜
- 优先选择时间复杂度为O(1)的I/O多路复用函数作为底层实现
- 以时间复杂度为O(n)的select 作为保底
- 基于react设计模式监听I/O事件-
- Redis采用I/O多路复用函数: epoll/kqueue/evport/select?
二. Redis常用的数据类型
2.1 常用数据类型
- String : 最基本的数据类型,二进制安全
- Hash: String元素组成的字典,适合用于存储对象
- List: 列表,按照String 元素插入顺序排序
- Set:String元素组成的无序集合,通过哈希表实现,不允许重复
- Sorted Set: 通过分数来为集合中的成员进行从小到大的排序
- 用于计数的HyperLogLog,用于支持存储地理位置信息的Geo等;
存入相同的key,但value不同的时候,会被重写,value值为最新输入的value值;
2.2 从海量Key里查询出某一固定前缀的Key
- 留意细节:
- 摸清数据规模,即问清楚边界
- KEYS pattern: 查找所有给定模式pattern的Key
- KEYS指令一次性返回所有匹配的key
- 键的数量过大会使服务卡顿
- SCAN cursor [MATCH pattern] [COUNT count]
- 基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程
- 以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历
- 不保证每次执行都返回某个给定数量的元素,支持模糊查询
- 一次返回的数量不可控,只能是大概率符合count参数
可能会获取到重复数据,需要每次添加新的游标
三. 如何通过Redis实现分布式锁
3.1 分布式锁需要解决的问题
- 互斥性
- 安全性
- 死锁
- 容错
3.2 分布式锁的尝试:

可以使用setnx来实现分布式锁;当获取到锁的时候返回1,没有获取到则返回0,通过get还能确定是否为这个锁;
- 如何解决SETNX长期有效的问题:
- EXPIRE key seconds
- 设置key的生存时间,当key过期时(生存时间为0),会被自动删除
- 缺点:
- 原子性得不到满足
- EXPIRE key seconds
RedisService redisService= SpringUtils.getBean(RedisService.class);
long status=redisService.setnx(key,"1");
if(status==1){
redisService.expire(key,expire);
//执行独占资源逻辑
doOcuppiedWork();
}
使用过期时间,可以当一个线程获取到了锁之后,给它设置一个过期时间,在这个时间内执行方法;过期后就相当于释放锁;
存在的风险: 在设置过期时间前被挂掉的话,这个线程可能就会一直占用此线程
3.3 分布式锁的优化:
-
SET key value [EX seconds] [PX milliseconds] [NX|XX]
- EX second: 设置键 的过期时间为second 秒
- PX millisecond: 设置键的过期时间为millisecond 毫秒
- NX: 只在键不存在时,才对键进行设置操作
- XX: 只在键已经存在时,才对键进行设置操作
- SET 操作成功完成时,返回OK,否则返回nil
-
操作代码如下:
RedisService redisService =SpringUtils.getBean(RedisService.class);
String result=redisService.set(lockKey,requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,expireTime);
if("OK",equals(result)){
//执行独占资源逻辑
doOcuppiedWork()
}
3.4 大量的Key同时过期的注意事项
- 集中过期,由于清除大量的Key很耗时,会出现短暂的卡顿现象
- 解决方案: 在设置key的过期时间的时候,给每个Key加上随机值;
3.5 如何使用Reids做异步队列
-
使用List作为队列,PRUSH生产消息,LPOP消费消息
- 缺点:没有等待队列里有值就直接消费
- 弥补:可以通过在应用层引入Sleep机制去调用LPOP重试
-
BLPOP key [key…] timeout: 阻塞直到队列有消息或者超时
- 缺点:只能供一个消费者消费
-
pub/sub: 主题订阅模式:
-
发送者[pub]发送消息,订阅者(sub)接收消息
-
订阅者可以订阅任意数量的频道
-
图示:

-
Redis Client1:
subscribe myTopic
Redis Client2:
subscribe myTopic
Redis Client3:
subscribe anotherTopic
Redis Client4:
publish myTopic "Hello"
//publish这个返回值为订阅者数量,在这里面clent4推送的消息只有1,2能收到,3因为订阅的内容不同,所以无法收到
消息的发布是无状态的,无法保证可达
四. Redis的持久化方式–RDB
4.1 RDB持久化
-
RDB(快照)持久化: 保存某个时间点的全量数据快照
- SAVE:阻塞Redis的服务器进程,直到RDB文件被创建完毕
- BGSAVE: Fork出一个子进程来创建RDB文件,不阻塞服务器进程
-
手动触发操作:
ls dump.rbd # 显示dump.rdb
rm -f dump.rdb # 删除所有dump.rdb
./redis-cli #连接Redis客户端
save #保存RDB持久化 会卡顿一段时间
exit #退出
ls dump.rbd ...
rm-f dump.rdb ...
ls dump.rbd # 已经被删除了,会提示:No such file or directory
./redis-cli ...
lastsave # 快照最后一次保存的时间
bgsave # 保存RDB持久化[单独开一个线程] 提示: Background saving started 不会卡顿
lastsave # 最后一次的执行时间,这里会被更新;因为使用了bgsave
exit
- 自动化触发RDB持久化的方式
- 根据redis.conf 配置里的SAVE m n 定时触发(用的是BGSAVE)
- 主从复制时,主节点自动触发
- 执行Debug Reload
- 执行Shutdown 且没有开启AOF持久化
4.2 BGSAVE原理
- 示意图:

系统调用fork(): 创建进程,实现了Copy-on-Write
4.3 Copy-on-Write
- 概述:
- 如果有多个调用者同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正赋值一份专用副本给该调用者,而其他调用者所见到的最初的资源仍然保持不变;
4.4 RDB 持久化的缺点:
- 内存数据的全量同步,数据量大会由于I/O而严重影响性能
- 可能会因为Redis挂掉而丢失从当前至最近一次快照期间的数据
五. 持久化方式-- AOF及混合模式
5.1 概述:
- AOF(Append-Only-File)持久化: 保存写状态
- 记录下除了查询以外的所有变更数据库状态的指令
- 以append的形式追加保存到AOF文件中(增量)
appendonly.aof[默认名字] 它是默认关闭的
vim redis.conf -> 将appendonly no 改为appendonly yes
ls appendonly.aof # 判断是否已经存在,如果不存在则继续,假设不存在
config set appendonly yes
set aofTest "hehe"
exit
ls appendonly.aof # 这时已经能正常显示了;
5.2 日志重写解决AOF文件大小不断增大的问题,原理如下;
- 调用fork(),创建一个子进程
- 子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件
- 主进程持续将新的变动同时写到内存和原来的AOF里
- 主进程获取子进程重写AOF的完成信号,往新AOF同步增量变动
- 使用新的AOF文件替换掉旧的AOF文件
5.3 RDB和AOF文件共存情况下的恢复流程:

5.4 RDB和AOF的优缺点
-
RDB
- 优点:全量数据快照,文件小,恢复快
- 缺点: 无法保存最近一次快照之后的数据
-
AOF:
- 优点:可读性高,适合保存增量数据,数据不易丢失
- 缺点: 文件体积大,恢复时间长
5.5 RDB-AOF混合持久化方式:
- 概述:
- 细细想来aofrewrite时也是先写一份全量数据到新AOF文件中再追加增量只不过全量数据是以redis命令的格式写入。那么是否可以先以RDB格式写入全量数据再追加增量日志呢这样既可以提高aofrewrite和恢复速度也可以减少文件大小还可以保证数据的完毕性整合RDB和AOF的优点那么现在4.0实现了这一特性——RDB-AOF混合持久化。
- 方式:BGSAVE做镜像全量持久化,AOF做增量持久化
- 操作: 通过aof-use-preamble配置项可以打开混合开关
六. Pipeline及主从同步
6.1 使用Pipeline的好处:
- Pipeline和Linux的管道类似
- Redis基于请求/响应模型,单个请求处理需要一一应答
- Pipeline批量执行指令,节省多次IO往返的时间
- 有顺序依赖的指令建议分批发送
6.2 Redis全同步过程:
- Salve发送sync命令到Master
- Master启动一个后台进程,将Redis中的数据快照保存到文件中
- Master 将保存数据快照期间接收到的写命令缓存起来
- Master完成写文件操作后,将该文件发送给Salve
- 使用新的AOF文件替换掉旧的AOF文件
- Master将这期间收集的增量写命令发送给Salve端;
6.3 增量同步过程
- Master 接收到用户的操作指令,判断是否需要传播到Slave
- 将操作记录追加到AOF文件
- 将操作传播到其他Slave: 1. 对其主从库 2. 往响应缓存写入指令 3. 将缓存中的数据发送给Slave
6.4 解决主从同步Master宕机后的主从切换问题:
- 监控: 检查主从服务器是否运行正常
- 提醒: 通过API向管理员或者其他应用程序发送故障通知
- 自动故障迁移: 主从切换
6.5流言协议:Gossip
- 在杂乱无章中寻求一致:
- 每个节点都随机地与对方通信,最终所有节点的状态达成一致
- 种子节点定期随机向其他节点发送节点列表以及需要传播的消息
- 不保证信息一定会传递给所有节点,但是最终会趋于一致
七. Redis的集群原理
7.1 如何从海量数据里快速找到所需?
- 分片:按照某种规则去划分数据,分散存储在多个节点上
- 常规的按照哈希划分无法实现节点的动态增减
7.2 一致性哈希算法:
- 概述: 对2^32取模,将哈希值空间组织成虚拟的圆环
- 方式: 将数据key使用相同的函数Hash计算出哈希值:

好处: 一个节点宕机后,其他的Node节点不会受到影响
7.3 Hash环的数据倾斜问题
- 概述: 当节点数比较少的时候,比如只有2个的时候,当数据计算出来都存入了节点A,只有较小部分的数据存入了节点B,则会造成数据倾斜,数据没有平均的分配到各节点上,若A节点宕机则也会造成较大的影响;
- 解决: 引入虚拟节点解决数据倾斜的问题:

引入了虚拟节点,将本来只有A、B两个节点变成了A#1,A#2,A#3,B#1,B#2,B#3等六个节点,解决了节点少而造成数据分布不均匀的问题;

14万+

被折叠的 条评论
为什么被折叠?



