记录--redis

1. 说一下你在项目中的redis的应用场景

redis 有五大常用数据类型:String、List、Hash、Set、Sorted Set

分别有各自的应用场景,一般是作为数据缓存,分布式下提供分布式锁的解决方案

2. redis是单线程还是多线程?

redis 不管那个版本,工作线程只有一个;6.X版本之后提供了 IO 多线程,满足 redis 的串行原子,只不过 IO 多线程后,把输入/输出放到更多的线程里去并行,执行时间段,更快,充分利用 服务器的硬件资源

3. redis存在线程安全的问题吗?为什么?

redis 的工作线程只有一个 ,保证串行执行的原子性,因此 redis 内部是可以保证线程安全的,但是外界使用 api 来操作 redis 时是不能保证的,需要业务上自行保障

4. 遇到过缓存穿透吗?详细描述一下。

缓存穿透 -- redis 中没有,db 中也没有

解决:

  缓存 null 值:对查询结果为 null 的数据进行缓存,长期使用的话需要定期清理,设置短时限失效,例如:30——60秒,最高 5 分钟

  白名单策略:提请预热各种分类的数据 id 对应的 bitmaps,id 作为 bitmaps 的 offset,相当于设置数据白名单,当加载正常数据时放行,加载异常数据时直接拦截;只用布隆过滤器,布隆过滤器的命中问题针对恶意访问造成服务器压力可以忽略

  实时监控:实时监控 redis 命中率与 null 数据的占比:非活动时间段波动--通常检查 3-5 倍,超过 5 倍纳入重点排查对象;活动时段检查 10-50 倍,超过 50 倍纳入重点排查对象;根据倍数不同,启动不同的排出程序,然后使用黑名单进行防控。

  key 加密:对 key 进行业务层传输加密服务,设定校验程序,发现不满足规则的 key ,直接驳回数据访问。

5. 遇到过缓存击穿吗?详细描述一下。

缓存击穿:热点 key 过期失效,redis 没有,db 中有,没有缓存

解决:

  1)预先设定,根据实际业务情况,加大此类 key 的过期时长;

  2)监控访问流量,对自然流量激增的数据延长过期时间或设置永久性  key

  3)后台刷新数据:启动定时任务,在高峰来临之前,刷新数据有效期,确保不丢失

  4)二级缓存:设置不同的失效时间 ,保证不会同时被淘汰

  5)加锁:分布式锁,防止被击穿

6. 如何避免缓存雪崩?

缓存雪崩:大量的 key 过期失效,没有被缓存

解决:

  1)构建多级缓存架构

  2)使用锁和队列:保证不会有大量的线程对数据库一次性进程读写,从而避免缓存失效时大量的请求落到存储库上

  3)设置过期标识更新缓存:记录缓存是否过期,设置提前量,如果过期通过后台程序更新实际 key 的缓存

  4)将缓存时间分散开,错峰失效

解决 :核心就是避免 db 层面的无效或重复请求

  • 请求 redis ,没有数据
  • 大家抢锁,时间复杂度是只发生在 redis 获取不到的情况下
    • 抢到锁的请求到 db
    • 没抢到的返回或sleep
  • db 获取到,更新 redis(针对缓存穿透可以 set key null)
  • 若是 sleep 的,唤醒重复当前流程操作
7. Redis是怎么删除过期key的?
8. 缓存如何回收的?

后台在轮询(serverCron)分段分批的删除那些过期的 key

请求的时候判断是否已经过期

  • 定时删除:对每一个设置了过期时间的 key 都创建了一个定时器,只要到达过期时间就立即删除。该策略可以立即清除过期数据,对内存友好,但是占用过多的 cpu 资源处理过期数据,会影响 redis 的吞吐量和响应时间
  • 惰性删除:当访问一个 key 的时候判断是否过期,过期删除。该策略可以最大限度的节约 cpu 资源,但是对内存不友好。极端情况下,会存在大量过期的 key 不会被访问到,占用过多内存空间
  • 定期删除:每个一段时间扫描 redis 中过期字典中的 key,进行过期 key 删除,通过调整扫描周期和扫描限定耗时,可以使 cpu 和内存资源达到最优的平衡效果
9. 缓存是如何淘汰的

当 redis 的内存达到 maxmemory 的极限时,会通过某种算法来决定清除掉哪些数据,这就是 redis 的缓存淘汰策略

redis 提供 8 中缓存淘汰的策略算法:

  • volatile_lru : 在设置了过期时间的 key 空间中,移除最近最不经常使用的 key,和最后一次访问 key 的事件有关
  • allkeys-lru : 在 key 空间中,移除最近最不经常使用的 key
  • volatile-lfu : 在设置了过期时间的 key 空间中,移除最近最少使用的 key,和使用次数有关,淘汰使用次数最少的
  • allkeys-lfu : 在 key 空间中,移除最近最少使用的 key
  • volatile-random : 在设置了过期时间的 key 空间中,进行随机移除
  • allkeys-random : 在 key 空间中,进行随机移除
  • volatile-ttl : 针对设置了过期时间的健值对,根据过期时间先后进行删除,越早过期的越先删除
  • noeviction : 一旦缓冲写满了,再有新的写请求过来时,redis 不再提供服务,直接返回错误

lru : redisObject 中维护了一个 lru 属性,记录 key 的访问时间,当从 redis 中筛选出 N 个数据,比较 lru 属性,淘汰距离现在最久的

10. Redis 是如何持久化的?

redis 提供两种持久化方式 rdb 和 aof

rdb: rdb 是 redis 默认的持久化方式;按照一定的时间将内存的数据以快照的形式保存到硬盘中,通过 配置文件 save 参数来定义快照的周期

aof:将 redis 执行的每次写命令记录到单独的日志文件中,当重启 reis 会重新将持久化的日志中文件恢复数据。当两种方式同时开启时,数据恢复优先考虑 aof 恢复,配置 no、every second、always,只会丢失一秒的数据;

11. Redis 集群实现原理
redis cluster 是一种服务端 sharding 技术。cluster 并没有使用一致性 hash,而是采用 slot 槽的概念,一共分成 10384 ([0, 10383])个槽。将请求发送到任意节点,接受到请求的节点会讲查询请求发送到正确的节点上执行
说明:
  通过 hash 的方式,将数据分片,每个节点均分存储一定哈希槽区间的数据,默认分配了 16384 个槽;
  每份数据分片会存储在多个互为主从的多节点上
  数据写入先写入主节点,再同步到从节点;
  同一分片多个节点间的数据不保持一致性;
  读取数据时,当客户端操作的 key 没有分配在该节点上时,redis 会返回转向指令,指向正确的节点;
  扩容时需要把旧节点的数据迁移一部分到新节点
redis cluster 架构下,每个 redis 需要开发两个端口,如 6379,16379;16379 用来进行节点间通信,进行故障检查、配置更新、故障转移授权。使用二进制 gossip 协议,用于节点间进行高效的数据交换,占用更少的网络宽带和处理时间 
12. Redis 与 MySQL 的双写一致性是如何保证的

延时双删:先删除缓存,再更新数据库,在等待具体读业务逻辑数据的耗时+几百毫秒后,再次删除缓存

删除缓存重试机制:若是第二次删除失败了,那么缓存数据和持久化还是不一致的,所以引入删除缓存重试机制,将删除失败的key放到消息队列中,将要删除的key拉去出来做重试删除的操作

读取binlog异步删除缓存:可以将 binlog 日志采集发送到 mq 队列中,通过 ack 确认送达机制处理这条更新消息,删除缓存,保证数据缓存一致性

为什么要删除缓存而不用更新呢  -- 线程 A 发起一个写操作,先更新了数据库 ,线程 B 发起一个写操作 ,在更新了数据库,由于网络原因,线程 B 先更新了缓存,而后线程 A 更新了缓存,导致缓存保存的还是 A 的数据。

更新缓存相对于删除缓存:1)如果写入的缓存值,是经过复杂计算才得到的话,更新缓存频率高的话,就浪费性能了 ;2)在写数据库场景多,读数据场景少的情况下,数据很多时候还没被读取到,又被更新了,这也浪费性能

13. redis 为什么快?

  1)绝大部分请求是存粹的内存操作,基于内存的操作响应非常快速;

  2)redis工作现场是单线程的,避免了不必要的上下文切换和竞争条件;

  3)redis 才用非阻塞 IO 多路复用 epoll 模型,减少了 IO 操作上的损耗时间

假如 redis 里面有一亿个key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?

  使用 keys 命令可以扫出指定模式的 key 列表;但是 redis 是单线程的 keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复,这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,但是整体所花费的时间会比直接用 keys 指令长。

原文地址:https://www.cnblogs.com/zhuozhang/p/14884647.html