面试系列笔记之redis篇

面试系列笔记-Redis

本文来自 https://github.com/TrumanDu/knowledge/blob/main/%E7%BC%93%E5%AD%98.md

Redis必知知识点

String

内部编码int(8字节长整型)/embstr(小于等于44字节字符串)/raw(大于44个字节字符串)

适用场景:共享session、分布式锁,计数器、限流、缓存

List

内部编码:`ziplist(同时满足:所有字符串元素的长度都小于64字节,元素数量小于512个)、quicklist

适用场景:消息队列,文章列表

1
2
3
4
lpush+lpop=Stack(栈)
lpush+rpop=Queue(队列)
lpsh+ltrim=Capped Collection(有限集合)
lpush+brpop=Message Queue(消息队列)

ziplist是一段连续内存,节省内存空间。

quicklist 存储了一个双向列表,每个列表的节点是一个ziplist,

Set

内部编码intset(整数集合)、hashtable(哈希表)如果集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用hashtable编码

适用场景:用户标签,生成随机数抽奖、社交需求

Hash

内部编码ziplist(压缩列表)哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用hashtable(哈希表)

适用场景

SortSet

内部编码ziplist(压缩列表)skiplist(跳跃表)当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码

适用场景:排行榜,社交需求(如用户点赞)。

其他

  • Geospatial 存储地理位置信息
  • Hyperloglog 基数统计算法的数据结构,可以用来统计数量
  • Bitmap 位图,解决大数据利器

Redis事务

Redis 可以通过 MULTIEXECDISCARDWATCH 等命令来实现事务功能。不支持原子性,没法进行rollback。

原理:

MULTI 可以等待输入多个命令进入队列,调用EXEC命令后,才真正执行所有命令。

DISCARD 命令取消一个事务,它会清空事务队列中保存的所有命令。

WATCH 命令用于监听指定的键,当调用 EXEC 命令执行事务时,如果一个被 WATCH 命令监视的键被修改的话,整个事务都不会执行,直接返回失败。

Redis过期策略和内存淘汰策略

过期策略

  • 定时扫描,删除过期数据
  • 惰性删除,当访问一个key时,如果该key过期,则删除,返回数据不存在。

定时删除逻辑:

1
2
3
4
5
Specifically this is what Redis does 10 times per second:

1. Test 20 random keys from the set of keys with an associated expire.
2. Delete all the keys found expired.
3. If more than 25% of keys were expired, start again from step 1.

内存淘汰策略

  1. volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
  2. volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
  3. volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
  4. volatile-lfu:从已设置过期时间的数据集挑选使用频率最低的数据淘汰。
  5. allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
  6. allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。
  7. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  8. no-enviction(驱逐):禁止驱逐数据,这也是默认策略。意思是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失。

热key问题

1.什么是热key?

访问评率高的key

热key带来的危害:可能会导致服务器资源不足,影响正常服务。

2.如何发现?

产生原因:

  • 热点新闻,秒杀等场景,读远远大于写的场景
  • 请求分片集中

如何发现:

  • 客户端统计
  • 服务代理层发现
  • -hotkeys(4.0+), redis-cli -h 192.168.0.110 -p 6379 --hotkeys

3.如何避免和解决?

  • 使用客户端缓存
  • redis集群扩容,增加master分片
  • 将热点key分散到不同master节点

大key问题

redis value最大值为512M

避免在 Redis 中存储 bigkey,建议:

  • String:大小控制在 10KB 以下
  • List/Hash/Set/ZSet:元素数量控制在 1 万以下

我们目前发现这类场景,是使用rct离线分析rdb文件得出,当然官方还有bigkey,debug等命令。

Redis高可用实现

  • 主从模式
  • 哨兵模式
  • Cluster集群模式,通信使用Gossip协议

常用的Gossip消息分为4种,分别是:ping、pong、meet、fail。

Cluster分布式算法是Hash slot算法。整个分为16384个slot。

Redis持久化

  • RDB
  • AOF
  • 混合类型

Redis rehash原理

使用两个hash表,渐进式扩容与rehash。在rehash过程,写操作操作的是新的hash,读是先读新的hash,如果读不到再去读旧的hash。

缓存雪崩/ 缓存穿透/ 缓存击穿

缓存雪崩

什么是缓存雪崩?

缓存雪崩是因为大面积的缓存失效,打崩了DB;原因是由于缓存过期时间一致,导致在一段时间内大量缓存过期,请求全部转发到DB,DB瞬时压力过重雪崩。

如何解决?

原因是因为缓存和DB处理能力不同。大量的请求落在DB,承受不了,进而崩溃。

  1. 解决办法就是避免同时过期,可以增加在ttl再增加随机值
  2. 使用集群,将热点数据落在不同节点,避免同时失效
  3. 设置热点数据永不过期,有更新操作就更新缓存就好了

缓存穿透

什么是缓存穿透?

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。而用户(黑客)不断发起请求,这就是漏洞。

如何解决?

  1. 对于空数据同样写入缓存,null
  2. 利用布隆过滤器
  3. 黑名单、用户鉴权

缓存击穿

什么是缓存击穿?

缓存击穿是指一个Key非常热点,在不停地扛着大量的请求,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发直接落到了数据库上,就在这个Key的点上击穿了缓存。并发的请求可能会瞬间把后端DB压垮。

如何解决?

  1. 永不过期,然后通过定时job去刷新缓存
  2. 使用互斥锁(mutex key):这种解决方案思路比较简单,就是只让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据就可以了。

分布式锁,Redlock

分布式锁实现原理是set命令+lua脚本。redisson使用watch dog解决锁过期问题

参考

  1. Redis夺命20问
  2. Redis最佳实践:7个维度+43条使用规范,带你彻底玩转Redis | 附实践清单