redis知识框架总结
2023-05-29 00:50:59 0 举报
AI智能生成
redis知识框架总结
作者其他创作
大纲/内容
redis核心数据结构
五种数据结构
字符串string
基本使用
注意:存储对象主要是通过key值的设计,对象名:id:字段名
应用场景
计数器
Web集群的session共享
分布式系统全局序列号(redis批量生成序列号提升性能)
哈希hash
基本使用
注意:插入的时候,除了key,value,中间多了域field(字段)
应用场景
存储多个属性描述的对象:电商购物车
与string对比
区别
hash的结构是,key下面,还有一层key-value键值对,即域;hashkey对应的value存的是真的一个对象结构,而String,分开存取的对象的key-value结构,只不过通过对key的命名的处理,来集中成一个对象。
优点
同类数据归类整合储存,方便数据管理
相比string操作消耗内存与cpu更小
相比string储存更节省空间
缺点
过期功能不能使用在field上,只能用在key上
Redis集群架构 下不适合大规模使:表名做key,可能会导致整个user表都存放在一个redis服务器上
列表list
基本使用
左右两边压入和弹出元素
应用场景
按照时间顺序,发放消息推送:微博消息和公众号消息
注意和消息队列的区别
redis模式。push模式,消息队列,pull模式
大V如何操作
对于粉丝较多的情况,使用消息队列,使用pull模式,然后每个用户上线之后,自己根据topic去拉取对应的消息,然后再放到自己的redis的list结构的key中去,但是注意,这个,拉到本地,还要根据时间进行一个排序处理,而redis不需要排序
集合set
基本使用
这里重点关注set随机选元素操作和运算操作:交集、并集、差集
应用场景
微信小程序
微信微博点赞、收藏、标签(只有好友才显示)
微信微博关注模型:共同关注,可能认识的人
实现电商商品的筛选,一些符合特有配置的手机
有序集合zset
基本使用
对key-value数据增加了数值字段,可以用于计数和排序筛选
应用场景
微博热度排行
源码底层结构
redis的底层结构
RedisDB
dict
dictht(数组)
dictEntry(链表结点)
*key(存储的key值):sds
len
free
buf[]
*val(存储的value):redisObject
type(value的数据类型)
String
list
hash
set
zset
encoding(实际编码类型)
raw
embstr
quicklist
hashtable
ziplist
inset
skiplist
*ptr(具体的value值,如:sds)
next(下一个dictEntry)
dictht
图展示:
五种数据结构底层编码结构
数据结构和编码结构的对应关系:
int:存储 8 个字节的长整型
embstr, 代表 embstr 格式的 SDS,存储小于 44 个字节的字符串
raw,存储大于 44 个字节的字符串(3.2 版本之前是 39 字节)
ziplist,压缩链表,省去头尾节点
quicklist,快速链表,头尾结点+压缩链表
intset,整数集合:一个有序的,存储整型数据的结构
hashTable:就是hasht结构
skiplist:跳表
redis的持久化
RDB快照
Redis 将内存数据库快照保存在名字为 dump.rdb 的二进制文件中
方式:同步:save;异步:bgsave
AOF方式
将修改的每一条指令记录进文件appendonly.aof中(先写入os cache,每隔一段时间fsync到磁盘)
比如执行命令“set zhuge 666”,aof文件里会记录如下数据
比如执行命令“set zhuge 666”,aof文件里会记录如下数据
策略:
always:每次
everysec:每秒 fsync 一次
everysec:每秒 fsync 一次
混合持久化
对比
RDB速度快安全性低,AOF速度慢安全性高
AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换
策略:
redis集群
主从架构
搭建:自身配置
主从复制
工作原理:master的bgSave生成rdb文件,生成缓存发给slave
流程图:
数据部分复制(重连之后上传)
工作原理:slave记录了offset并发送给master,master根据offset返回该标记位后面的数据,如果没有了,就进行全量数据复制
流程图:
高可用架构
Redis哨兵高可用架构
搭建:搭建一个哨兵集群,来监控redis集群,如果master挂掉,就主动更换从节点
架构:
哨兵leader选举流程
Redis自身的高可用集群
搭建:大家多个主从集群,然后每一个主节点分担16384个槽位
新增结点:
新增主从结点
分片工作
分多少槽位给指定redis实例
从哪些结点选出槽位给redis(一般为all)
删除结点
删除从节点
分片工作
移动多少槽位
移动目标redis结点
移动初始redis结点——主结点
删除主结点
架构:
原理
槽位定位算法:key值经过哈希计算,在对16384进行取模,计算槽位
Redis的通信机制
集中式
gossip:meet,ping,pong,fail
Redis选举原理
选举过程:从salve发现master挂掉之后,就会向外界广播,其他主结点只能回应一个从结点的响应,这样最先获取到半数以上响应的从结点就会当选新的主结点
解决集群脑裂:
设置写数据最少同步成功slave数据量
Redis集群为什么至少需要三个master节点,并且推荐节点数为奇数?
子主题
redis实战问题解决
大厂生产级Redis高并发分布式锁实战
问题:在高并发秒杀场景下,可能出现货物超卖的情况
解决:
1、单机上一个synchronized锁
在高并发下的集群服务中,synchronized就不能发挥正常锁排队功能
2、使用redis分布式锁
业务逻辑中抛异常,没并发删除锁,就会出现死锁
3、使用redis分布式锁+过期时间+clientId上锁
下面删除锁,不是原子性的,有可能在在刚执行玩if判断之后,锁10s自动过期,就会出现删除其他锁的问题
4、使用redisson分布式锁结构
redisson,一个架构,底层使用锁续命,只要主线程还在执行业务逻辑,就会每隔一段时间,进行锁续命,完美解决上诉问题
Redisson分布式锁原理
原理:主线程设置了30秒(看门狗,默认30s)的一个分布式锁,然后,每隔10秒会为自己进行一次续命逻辑,其他线程,会间歇性的尝试加锁。
流程图:
核心源码:采用方法嵌套调用的方法去实现锁续命,scheduleExpirationRenewal就是一个定时任务,去所为锁进行续命,且是到1/3过期时间,就续命一次
Redisson底层的redis业务逻辑都是通过lua脚本执行的,原理就是redis是单线程操作的,所以就是可以采用lua脚本保证原子性,所以可以使用lua脚本实现事务功能
分布式锁的主从失效问题
上面基本上解决了高分发实战下面的分布式锁的问题,但是假如面对主从结构中,主结点挂掉的话,假如此时key还没有缓存到从结点,这个时候,就又会超卖,发生并发问题。
解决
zookeeper
采取RedLock(红锁)解决,但是实际上也解决不了
原理:一般实现奇数结点(半数机制),然后客户端至少收到半数以上的结点设置成功,才算key设置成功
问题:但是不推荐使用,因为也没有百分之百解决,因为线上为了保持高可用,每一个redis后面也要拖一个从结点,这样,假如一个结点挂了一个之后,还是有该问题,除非你不使用从节点,或者搞多几个结点,会更加可靠,但是结点过多之后,性能就会下降。
还有一个问题,1s中我们会使用持久话,那假如我们没有从结点,到那时挂掉时,丢了1秒数据,假如刚好丢失锁key,也会出现加锁的问题,发生超卖。
还有一个问题,1s中我们会使用持久话,那假如我们没有从结点,到那时挂掉时,丢了1秒数据,假如刚好丢失锁key,也会出现加锁的问题,发生超卖。
缓存击穿(数据库还没有穿)
问题:因为失效机制,譬如我们批量上架商品,会批量的取消,这样就会出现大量的商品直接穿透到数据库,会出现服务卡顿,抖动
解决:我们在维护一个过期时间时候,就可以在一个基准的时间上,加一个随机时间
缓存穿透(数据库查询也没有数据,穿透了数据库)
问题
不小心删除商品按钮,后端的数据和缓存都删掉,那每次商品访问,都会访问到redis和数据库,但是因为没有该数据,就会一直访问数据库,压力就会比较大。
还有一种情况,黑客攻击,根据规则,一直访问服务,因为数据库没有该商品,没有缓存,就一直访问该商品的数据库。
解决
我们可以对黑客的请求,放一个空缓存(不建议为null,返回一个空字符串)并设置一个短期(几分钟)的过期时间,这样就不会穿透到数据库因为为null,我们会默认为没有建立缓存,会继续访问数据库,所以,我们要缓存一个空对象即可,然后和前端约定,空对象没有意义
布隆过滤器
某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。
缓存雪崩
问题
缓存雪崩指的是缓存层支撑不住或宕掉后, 流量会像奔逃的野牛一样, 打向后端存储层。
例子:新浪微博,一个热点事件(一个大V发微博),突然超高并发
解决
保证缓存层服务高可用性,比如使用Redis Sentinel或Redis Cluster。
依赖隔离组件为后端限流熔断并降级。比如使用Sentinel或Hystrix限流降级组件。
防止大量缓存在同一时间点失效
进行缓存预热
多级缓存
原理:JVM缓存如guava,可以实现百万级别并发
代码示例:
解决:面对web的集群,我们的内存缓存不是集群怎么办
可以使用MQ,大家都监控在一个channel,然后缓存更新之后,都在自己onMessage方法中,获取更新的缓存(这里不必要绝对一致了,不然架构太复杂了,容忍短时间不一致问题)
大厂解决:热点监控系统
会有一个单独的热点更新系统,去维护热点中的热点事件,只有在读的时候,获取map信息,但是维护map,在一个系统中会单独做,比如可以通过AOP对redis服务做一个拦截,然后每次拦截之后就会向热点更新系统中做一个请求,然后对这些请求进行分布式的大数据实时计算,这样就可以解耦,让业务代码更加的简单明了
针对冷门商品突然并发被访问:双重检测锁(DCL)
大v带货,导致没有缓存的冷门商品突然并发被访问,打挂数据库
问题:突发热点缓存导致后台压力特别大的问题:
解决:
双重检测锁(DCL)可以解决该问题,只有一个访问,能够重建缓存,然后其他请求卡在锁上面,然后拿到锁之后,就会直接拿到缓存
代码示例:
优化
分析:不能使用读写锁,也不适合分段锁(已经是按照商品上锁了),我们可以加一个过期失效时间,超过1s直接失效
代码示例:这里有问题,假如发声百分之1的可能,锁的缓存,没有在1s内执行完,就会执行过去,发生并发bug
缓存与数据库双写不一致问题
问题
查库和更新缓存之间,被其他线程更改数据库,就会导致数据库和缓存内容不一致
解决
本质上,就是查询数据库和更新缓存不是原子性的,很难避免,在高并发下
所以,就要把查询数据库和更新缓存保证原子性,可以加一把分布式锁
所以,就要把查询数据库和更新缓存保证原子性,可以加一把分布式锁
代码示例:
优化
原理:读多写少,可以使用读写锁优化,注意,这里的读写,主要针对数据库的读写!!!而不是写缓存!!!
过程展示:
代码优化:上分布式读写锁
Redis的开发规范和性能优化
键值设计
key名设计
可读性和可管理性
简洁性
不要包含特殊字符
value设计
【强制】:拒绝bigkey(防止网卡流量、慢查询)
定义:一般来说,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。,否则就是bigKey
危害
导致redis阻塞
网络拥塞
过期删除
产生
社交类:粉丝列表,如果某些明星或者大v不精心设计下,必是bigkey
统计类:例如按天存储某项功能或者网站的用户集合,除非没几个人用,否则必是bigkey。
缓存类:将数据从数据库load出来序列化放到Redis里,这个方式非常常用,但有两个地方需要注意,第一,是不是有必要把所有字段都缓存;第二,有没有相关关联的数据,有的同学为了图方便把相关数据都存一个key下,产生bigkey
优化
拆
选择适合的数据类型。
控制key的生命周期,设置key的过期时间
命令的使用
【推荐】 O(N)命令关注N的数量
例如hgetall、lrange、smembers、zrange、sinter等并非不能使用,但是需要明确N的值。有遍历的需求可以使用hscan、sscan、zscan代替。
【推荐】:禁用命令
禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。
【推荐】合理使用select
redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。
【推荐】使用批量操作提高效率
原生命令:例如mget、mset。
非原生命令:可以使用pipeline提高效率。
非原生命令:可以使用pipeline提高效率。
【建议】Redis事务功能较弱,不建议过多使用,可以用lua替代
客户端使用
连接池配置
maxTotal:最大连接数
根据需求合理设置
maxIdle实际上才是业务需要的最大连接数
不要过小,建议连接池的最佳性能是maxTotal = maxIdle
minIdle(最小空闲连接数)
Redis对于过期键有三种清除策略
被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key
主动删除:由于惰性删除策略无法保证冷数据被及时删掉,所以Redis会定期(默认每100ms)主动淘汰一批已过期的key,这里的一批只是部分过期key,所以可能会出现部分key已经过期但还没有被清理掉的情况,导致内存并没有被释放
当前已用内存超过maxmemory限定时,触发主动清理策略
清除算法
LRU 算法(Least Recently Used,最近最少使用)
淘汰很久没被访问过的数据,以最近一次访问时间作为参考。
LFU 算法(Least Frequently Used,最不经常使用)
淘汰最近一段时间被访问次数最少的数据,以次数作为参考。
选择
当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。这时使用LFU可能更好点。
redis6.0新特性
多线程处理网络IO,但是还是单线程处理业务逻辑
收藏
0 条评论
下一页