前言
最近有跳槽的打算,准备一些面试的资料,计划是每天想看的时候看一点儿然后积累理论知识,然后来这里顺便记录一下,如果有帮助到你的话请点个赞吧!
🍉什么是redis
是⾼性能的 key-value,纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作
- Redis ⽀持数据的持久化,可以将内存中的数据保存在磁盘中,重启的候可以再次加载进⾏使⽤
- key-value数据多种,不仅是string
- Redis 有着更为复杂的数据结构并且提供对他们的原⼦性操作
- 丰富的特性:可⽤于缓存,消息,按 key 设置过期时间,过期后将会⾃ 动删除
总体来说,它的功能还是多种多样的,⽐如缓存、数据持久化、⽀持事务、⽀持消息队列等
🍈redis中的数据结构,以及常见应用场景
String
介绍:存储字符串类型的key-value
应用场景:临时验证码(例:短信)、计数器、发号器、热点商品卡片(序列化json对象存储)、分布式锁、订单重复提交令牌List
介绍:字符串列表,按照插入顺序排序、双向链表,插入删除时间复杂度O(1)快,查找为O(n)慢
应用场景:简单队列、最新评论列表、非实时排行榜:定时计算榜单,如手机日销榜单Hash
介绍:包含键值对的无序散列
应用场景:购物车(例:店铺名称:商品名称:商品数量)、用户个人信息、商品详情Set
介绍:无序集合
将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略
应用场景:去重、社交应用关注、粉丝、共同好友、统计网站的PV、UV、IP、大数据里面的用户画像标签集合SortedSet
介绍:有序集合
-
用于将一个或多个成员元素及其分数值加入到有序集当中
-
如果某个成员已经是有序集的成员,那么更新这个成员的分数值,分数值可以是整数值或双精度浮点数。
-
有序集合可以看做是在Set集合的的基础上为集合中的每个元素维护了一个顺序值: score,它允许集合中的元素可以按照score进行排序
应用场景:优先级任务、队列、实时排行榜:商品热销榜、体育类应用热门球队、积分榜、朋友圈 文章点赞-取消,逻辑:用户只能点赞或取消,统计一篇文章被点赞了多少次,可以直接取里面有多少个成员
🍊单线程的Redis为什么这么快
- Redis的全部操作都是纯内存的操作;
- Redis采⽤单线程,有效避免了频繁上下⽂切换,(
在redis6.0之后引⼊了多线程io,只是⽤来处理⽹络数据的读写和协议的解析,但执⾏命令还是单线程
)
redis 利⽤队列技术将并发访问变为串⾏访问
,消除了传统数据库串⾏控制的开销队列技术 - 采⽤了⾮阻塞I/O多路复⽤机制
🥭redis的内存用完了会发生什么?
如果达到了设置的上限,Redis 的写命令会返回错误信息( 但是读命令还可以正常返回。) 或者你可以将 Redis 当缓存来使用配置淘汰机制, 当 Redis 达到内存上限时会冲刷掉旧的内容。
🍎Reids的淘汰策略
- noeviction: (
默认
)不删除策略, 达到最大内存限制时, 如果需要更多内存,
直接返回错误信息。大多数写命令都会导致占用更多的内存,有极少数会例外。 - allkeys-lru: 所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
- volatile-lru: 只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
- allkeys-random: 所有key通用; 随机删除一部分 key。
- volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
- volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。
- allkeys-lfu 从数据集(server.db[i].dict)中挑选使用频率最小的数据淘汰,该策略要淘汰的key面向的是全体key集合,而非过期的key集合。
- volatile-lfu 从设置过期时间的数据集(server.db[i].expires)中挑选出使用频率最小的数据淘汰。没有设置过期时间的key不会被淘汰,这样就可以在增加内存空间的同时保证需要持久化的数据不会丢失
🍑redis如何设置过期时间和永久有效
设置过期时间:EXPIRE
设置永久有效:PERSIST
🍒Redis过期键的删除策略
- 立即删除(定时删除)
在设置键的过期时间的同时,创建一个定时器timer,当定时器检测到键的过期时间来临的时候,对键进行立即删除的操作。
这种删除策略对内存是比较友好的,能够保证内存中的key一旦过期就立即从内存中删除。但是当过期的键比较多的时候,占用的CPU的时间也比较多,对服务器的吞吐量也会造成影响。的
- 惰性删除(redis采用)
当发现键过期的时候不会立即删除,但是当每次从键空间中获取键时,检查该键是否过期。如果过期就删除该键,否则,就返回该键。
这种情况下,如果出现大量过期的键,会占用大量的内存。
- 定期删除(redis采用)
每隔一段时间,程序就会对数据库进行一次扫描,删除一定数量的过期的键。实际应用中可以设置每隔多久扫描一次或者一次扫描多久来平衡CPU和内存的资源。
- 总结:redis主要使用了惰性删除、定期删除的策略来解决redis中的过期的键删除问题。
🍍Redis持久化机制
两种持久化机制 RDB 和 AOF 机制
RDB:在指定的时间间隔内将内存中的数据集快照写⼊磁盘写⼊⼀个临时⽂件,持久化结束后,⽤这个临时⽂件替换上持久化的⽂件,达到数据恢复
- 节省磁盘空间
- 可以安全保存到磁盘
- 性能最⼤化,⽤单独⼦进程进程持久化
- 缺点:
数据安全性低。RDB 是间隔⼀段时间
进⾏持久化,如果持久化之间 redis 发⽣故障,会发⽣数据丢失
AOF:
以⽇志的形式
来记录每个写操作(增量保存),将Redis执⾏过的所有写指令记录下来(读操作不记录), 只许追加⽂件但不可以改写⽂件
- 优点:
数据安全,每⼀次命令操作就可以追加到后⾯,即使宕机,也可以通过redis-check-aof来修复 - 缺点:
- ⽂件过⼤,恢复速度慢
- 数据集⼤的时候,⽐RDB要慢
🍌redis缓存穿透
产生这个问题的原因可能是外部的恶意攻击,例如,对用户信息进行了缓存,但恶意攻击者使用不存在的用户id频繁请求接口,导致查询缓存不命中,然后穿透 DB 查询依然不命中。这时会有大量请求穿透缓存访问到 DB。
解决办法
:
- 对不存在的用户,在缓存中保存一个空对象进行标记,防止相同 ID 再次访问 DB。不过有时这个方法并不能很好解决问题,可能导致缓存中存储大量无用数据
- 使用 BloomFilter 过滤器,BloomFilter 的特点是存在性检测,如果 BloomFilter 中不存在,那么数据一定不存在;如果 BloomFilter 中存在,实际数据也有可能会不存在。非常适合解决这类的问题。
🌽redis缓存击穿
就是某个热点数据失效时,大量针对这个数据的请求会穿透到数据源。
解决办法
:
- 可以使用互斥锁更新,保证同一个进程中针对同一个数据不会并发请求到 DB,减小 DB 压力。
- 使用随机退避方式,失效时随机 sleep 一个很短的时间,再次查询,如果失败再执行更新。
- 针对多个热点 key 同时失效的问题,可以在缓存时使用固定时间加上一个小的随机数,避免大量热点 key 同一时刻失效。
🥥redis缓存雪崩
缓存挂掉或者大量的key同时过期,这时所有的请求都会穿透到 DB
解决⽅案:
(1)构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等)
(2)使⽤锁或队列:
⽤加锁或者队列的⽅式保证来保证不会有⼤量的线程对数据库⼀次性进⾏读写,从⽽避免失效时⼤量的并发请求落到底层存储系统上。不适
⽤⾼并发情况
(3)设置过期标志更新缓存:
记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。
(4)将缓存失效时间分散开:
⽐如我们可以在原有的失效时间基础上增加⼀个随机值,⽐如1-5分钟随机,这样每⼀个缓存的过期时间的重复率就会降低,就很难引发集
体失效的事件。
🥑redis分布式锁
先拿 setnx 来争抢锁, 抢到之后, 再用 expire 给锁加一个过期时间防止锁忘记了释放
- 如果在 setnx 之后执行 expire 之前进程意外 crash 或者要重启维护了, 那会怎么样?
setnx 和expire 可以合成一条指令使用