Redis知识点
2021-08-17 22:07:51 0 举报
AI智能生成
redis面试知识点
作者其他创作
大纲/内容
这种方式需要redis不断的扫描所有的设置了过期时间的key,非常消耗性能,一般不会使用
立即过期
不主动删除已经过期的key,而是在使用的时候去检查是否过期,如果过期则删除
惰性过期
对于已经的过期key,又一直没有被使用,这个时候就定期去扫描expire列表,一部分删除
定期过期
过期策略
经过两种过期策略,都还是会有些key不会被删掉,时候就需要使用到内存淘汰机制
可以通过合理的配置内存淘汰机制,将一些没有使用又占用内存的key淘汰掉,让新的key可以进入redis
为什么会有内存淘汰机制
redisconf中,对maxmemory进行设置,32位系统的缺省值为3G,64位系统不限制
如何设置redis占用内存的大小
volatile - 配置了过期时间的key
allkeys - 字面意思,所有的key
前缀
最后一次访问时间与当前系统时间差值越大,越优先淘汰
LRU
一段时间内,访问频率越低,越优先淘汰
LFU
当前系统时间离配置的过期时间越近,越优先淘汰
TTL
随机淘汰
RANDOM
后缀
前缀后缀组合起来就是完整的淘汰策略,如volatile - lru
如果不配置淘汰策略,内存满了就不会有新的key进入redis
淘汰策略
内存淘汰
传统的LRU通过双向链表实现,访问时将节点放入队列头,优先淘汰队列尾的数据
传统的方式会占用更多内存,redis使用的是另外一种LRU,即在每个redisObject中维护一个访问时间字段,淘汰时扫描redisObject,淘汰那些据当前系统时间差值较大的数据
redis的LFU在LRU的基础上实现,加上了一个访问频次的字段,内存淘汰时会参考这个访问频次
访问频次会随时间衰减
算法实现
内存回收
自动触发:每次到备份时间就会fork一个子线程生成dump.rdb文件
save
bgsave - 后台生成
flushall
shutdown
手动触发
生成方式
生成快照不影响主流程
文件紧凑,节省空间
恢复速度快
可以定时备份rdb文件,作为冷备
优点
不能实时持久化,宕机恢复后可能会缺少几分钟的数据
不足
RDB快照
在redis.conf中配置appendonly on
启用
每秒写入日志,宕机恢复最多损失一秒的数据
日志文件的可读性高,并且可以自由修改,在恢复redis时可以从AOF日志中删除一些致命的指令,如flash all
占用磁盘空间较大
恢复速度相对RDB慢
aof文件中记录的是每个指令,当文件膨胀到一定程度的时候,可以使用压缩指令,将redis中现存的key重写一份到AOF中
手动压缩
除了手动压缩外,redis也会自动压缩AOF,触发条件为AOF大小为上一次大小的两倍且文件大于64M
自动压缩
会将新的命令先放入缓冲区中,等压缩完成后再写入aof
文件压缩时有新命令怎么办?
文件压缩
AOF日志
redis默认是使用RDB,可以将两种都开启
生产环境选择哪一个
持久化机制
轻量级客户端,有redis的基础操作
Jedis
高级客户端,支持同步和异步,哨兵集群,pipeline等
Lettuce
分布式客户端,有一些分布式集合
Redission
常用客户端
2.x以前默认使用Jedis,2.x以后默认使用Lettuce
Spring的RedisTemplate
客户端
使用Lua脚本加锁,数据结构使用的是Hash结构
使用Hash结构的key作为分布式锁的关键字,field保存线程号,用于验证可重入标识,value则是重入次数
加锁时,key不存在则新建锁,可以存在且线程号一样,则重入,否则返回上一个锁的过期时间
加锁
先判断key是否存在,不存在则广播解锁成功消息
key存在,判断线程号是否相等,不相等不允许解锁
key和线程号都相等,则扣减重入次数,如果重入次数在扣减后大于0,则给这个数据重新设置过期时间
如果重入次数等于0,则解锁成功,并广播解锁成功消息
解锁
加锁与解锁
使用watch dog监听客户端的锁,每10秒一次,如果监听时未释放锁,则给所得过期时间再加30秒
正常情况下一个客户端不可能无限持有锁,我们在写业务的时候,需要把加锁时间控制一个合理的范围内
客户端持有锁超时
使用高可用集群,Master宕机后由slaver接管
使用RedLock,一种过半提交的思想,集群中有多个Master,只有超过半数的Master都获取锁成功,才表示客户端获取锁成功
如果Master还未同步完数据就宕机了
客户端宕机问题
分布式锁
批量添加key,再一次性执行,即一次连接执行多个指令,避免每个指令都连接一次redis服务
一次性操作大量的key
pipeline会缓存客户端的指令,达到一定的条件才会执行,不适用于对实时性要求高的业务
pipeline最多的时候会积累8M的数据才会执行
pipeline会出现的问题
管道pipeline
以数据库的数据为准,分为实时一致性和最终一致性
原则与标准
对于复杂的多表查询组合的缓存数据而言,更新的方法会比较复杂,所以选择删除缓存的方式,下次查询的时候自动放入到缓存中
更新还是删除
并发条件下,在更新数据库之前,可能会有其他的线程将旧数据更新回缓存中
先删缓存再更新数据库
并发条件下,在删除缓存之前,其他线程可能拿到的是旧数据
先更新数据库再删缓存
先删除缓存,再更新数据库,然后延时一定时间后再次删除缓存。这个延时很重要,需要等到所有的并发事务更新了缓存之后,否则缓存中会存储旧的数据
延时双删
删除顺序
双写一致性
对于某些特定的业务,可以凭借经验得知热Key,比如秒杀
在客户端加入监控,使用哈希表统计key出现的次数,这种方式容易内存泄漏
使用Monitor监控,但这种方式会降低redis的性能
自己抓包处理,按照RESP协议解析数据
热点数据的确定
大量的热点key同时失效,请求打到了数据库,导致数据库崩溃
现象
在给key设置过期时间的时候加上一个随机值,让缓存不要同时失效
解决
缓存雪崩
与雪崩类似,单个热点key失效,导致大量的并发请求打到了数据库
设置热点key永不过期,并且在查询时加锁,让第一个线程去更新缓存,其他的线程从更新后的缓存中去获取
缓存击穿
不断的请求数据库不存在的key,这种请求会一直绕过缓存,落到数据库中,往往是被人恶意访问
对访问的参数做验证,对于不合理的参数直接返回,例如小于0的id
使用布隆过滤器过滤掉数据库不存在的key
对于布隆过滤器的漏网之鱼,可以将查询到的null保存到redis
缓存穿透
三大高并发问题
使用BitMap的数据结构,非常节省内存,100万个key可能不到1M
数据结构
1.缓存预热,将key加载到布隆过滤器中
2.判断请求的key是否存在于布隆过滤器中
判断方式
数据库中没有,但布隆过滤器返回有。(注:布隆过滤器中没有,数据库一定没有)
误判指的是什么
是因为Hash算法导致的,Hash算法就一定会有Hash碰撞,可能出现某个key在Hash后在布隆过滤器中有,但在数据库中没有
为什么存在误判
增加位图的容量
将一个key多Hash几次,分布在位图的不同位置
如何减少误判
误判
布隆过滤器
高并发问题
实践
redis是一种基于内存,使用键值对存储的非关系型数据库
什么是redis
高性能:将一些在数据库查询相对较慢的数据缓存到redis中,可以获得更快的查询速度
高并发:在并发环境下,大量的请求直接发送的数据库,数据库可能承受不了大量的访问压力。使用redis可以在并发环境下保护数据库
为什么要使用redis
生命周期角度:本地缓存的生命周期随JVM关闭而结束,Redis中的缓存理论上是可以一直存在的
分布式角度:本地缓存的作为范围在当前JVM中,而Redis作为分布式缓存可作用在多个服务中
为什么不用HashMap(guava实现的缓存)
Redis支持更多的数据结构
Redis功能更加丰富,支持持久化、事务、lua脚本、pipeline等
Redis自带分布式高可用的实现,sentinel或cluster
为什么不用Memcached
可以达到10万QPS
使用redis-benchmark性能测试工具可以测试QPS
redis有多快
完全基于内存操作,并使用Hash表格式做存储
redis请求单线程,避免创建、关闭线程、上下文切换带来的性能消耗(CPU不是redis的性能瓶颈,单核就已经够用了)
使用同步非阻塞的多路复用模型,多个socket共用一个线程
redis为什么快
基础
存储格式:key-vlaue
数据库缓存
分布式数据共享,如session、token
计数器
生成全局唯一id
限流
应用场景
String
存储格式:key-field-value
存储类型:键值对
String格式能做的,Hash都能做(redisson使用hash类型实现的分布式锁)
用键值对存储对象
key - 用户id , field - 商品id ,value - 商品数量
购物车
Hash
存储格式:key-有序链表
有序列表:如评论列表
简单的队列使用
List
存储格式:key-无序集合
抽奖(spop弹出)
点赞、签到、打卡(不重复特性)
共同好友、条件筛选(交集)
Set
存储格式:key-score-member
排行榜
Zset(Sorted Set)
在线用户统计,布隆过滤器
BitMap
基于基数的统计,比如网站访问量,日活、月活,uvpv
Hyperloglogs
记录地理信息位置,做经纬度计算
Geo
流计算
Streams
其它
数据类型
使用pubulish发布消息,subscribe订阅消息
在redis集群中会用到发布订阅功能,但是实际业务中还是建议使用专业的消息队列
发布/订阅 pub/sub
muti - 开启事务
exec - 执行事务
discard - 取消事务
类似乐观锁:监视一个key的变化,如果事务运行时,被监视的key已经改变,则事务执行失败
watch - 监视
命令
事务不能嵌套,使用多个muti的效果是一样的
开启事务后,每个redis指令都会放入的队列中,执行exec时按顺序执行
redis的事务不会回滚,某个指令错误后,之前已经执行的指令不会回滚到之前的值
特点
事务
一个脚本中可以有多个操作,并将这些操作组合成一个原子性的整体来执行
可以将脚本写在文件中,作为一个指令组来重复执行
Lua脚本在执行时,会阻塞其他客户端的请求
在客户端用使用 eval 执行脚本
\
如果有返回值在redis.call前边加上return
脚本中调用redis的操作
eval \
如何对脚本传参
常用语法
如果脚本中没有对key做操作,使用script kill 干掉脚本
如果脚本中对key有更新,就杀不掉了,只能强制停止服务,shutdown nosave
脚本超时
可以将Lua脚本生成摘要来使用,使用 script load
使用evalsha可以通过摘要来执行脚本
摘要
Lua脚本
高级功能
将一个节点 slaveof ip port 就可以加入某个主节点作为从节点
使用slaveof no one 可以退出主节点,成为独立的节点
如何设置从节点
一个节点成为某个节点的从节点之后,会清空自己的数据,然后从主节点同步数据
主节点使用后台线程生成一个RDB文件,传输给从节点同步数据
在RDB生成和传输期间产生的新的命令,会被放入到缓冲区中,待从节点同步完成后执行
全量复制
主节点的每隔一秒,就会传输命令给从节点同步
如果从节点是从宕机中恢复,则会根据主从节点的偏移量对比来进行同步
增量复制
复制策略
用来备份主节点的数据,在主节点宕机后可以手动切换成从节点继续服务
作用
宕机时需要人为切换,时效性不足且节点较多的时候比较麻烦
没有解决高可用的问题,客户端只能访问主节点
作用于不足
主从
监控redis是否正常运行
Redis的Master宕机后,自动选举出新的Master,实现故障转移
故障转移后,通知客户端新的MasterId
功能
Sentinel哨兵是特殊的redis服务器
由3台哨兵+3台redis组成(奇数个即可)
每个哨兵服务器都监控所有的redis服务器
哨兵服务器不会互相监控,他们通过监控同一个master在感知互相的存在
架构
哨兵服务器每秒会给redis发送Ping指令,指定时间内收不到回复(默认30秒)会把改服务标记为下线状态,这一步叫主观下线
然后,哨兵会询问其他哨兵服务器,这个redis节点是否下线,超过半数以上的哨兵认为该节点下线,该节点就会被正式认为下线,这一步叫客观下线
服务下线
先在哨兵服务集群中通过Raft算法选出一个Leader,由哨兵Leader来发起Redis服务器的选举
谁来选举?
1.断开哨兵的时长超过阈值就会失去被选举权
2.按照配置文件中的优先级进行选举
3.优先级相同,选择数据偏移量大的
4.偏移量相同,选择进程id小的
选举条件
从节点使用slaveof no one 从退出之前主节点的关联,然后slaveof 新的master节点的ip和端口,加入到新的主节点
更新从节点
故障转移
哨兵只解决了自动切换主从的问题,没有做到对数据的分片
哨兵的不足
哨兵
redis集群的分布式方案,解决高可用和数据分片的问题
一个redis集群中会包含多个redis的主从节点
概念
使用hash槽对redis进行分片,一共有16384个哈希槽
给每个redis节点分配一段哈希槽,在使用的时候使用CRC16算法对key做hash,再对16383取模,获取到一个hash槽的位置
然后将数据存放到hash槽对应的redis节点中
分片逻辑
对于有共同特征的数据,我们喜欢它们可以落到同一个节点上,我们可以在key后面加上{hash tag}
对于有hash tag的key,在计算hash槽的时候,只会使用大括号中的数据进行hash槽计算,我们使用相同的hash tag就可以让不同的数据落到同一个槽里面
让数据落到指定的节点上
即客户端连接的节点,不是实际计算出的hash槽所在的redis节点
这种情况需要更换客户端的连接,连接到正确的节点中,可以使用客户端工具实现
客户端连接的节点与hash槽所在节点不一致
新节点上线或者节点下线,都会将hash槽重新分配
这种情况,redis会根据新的hash槽来重新分配数据,将数据迁移到正确的节点中
节点上下线的数据转移
Redis Cluster自动做主从切换
主节点挂了怎么办
常见问题
集群
高可用
Redis
0 条评论
下一页
为你推荐
查看更多