Redis八股总结

2024 年 9 月 26 日 星期四(已编辑)
2
这篇文章上次修改于 2024 年 9 月 26 日 星期四,可能部分内容已经不适用,如有疑问可询问作者。

Redis八股总结

Redis

数据结构

Redis底层的数据结构

  • String

    • 可以是字符串、整数或浮点数

    • 对整个字符串或字符串的一部分进行操作; 对整数、浮点数进行自增或者自减操作。

  • List

    • 一个链表,链表上的每个节点都包含一个字符串

    • 对链表的两端进行push、pop,读取单个或者多个元素;根据值查找或者删除元素

  • Set

    • 包含字符串的无序集合

    • 包含基础的方法,增加获取删除、计算交并查集

  • Hash

    • 包含键值对的无序散列表

    • 包含添加、删除、获取单个元素

  • Zset有序集合

    • 和散列一样,用于存储键值对

    • 字符串成员与浮点数之间的有序映射; 元素排序由分数的大小决定; 增加、获取、删除单个元素以及根据分支范围或成员来获取元素

  • BitMap、HyperLogLog、GEO、Stream

数据结构的常用场景

  • String

    • 缓存对象

    • 常规计数

    • 分布式锁

    • 共享session信息

  • List

    • 消息队列。生产者需要自行实现全局唯一id 不能以消费组形式消费数据
  • Hash

    • 缓存对象

    • 购物车

  • Set

    • 聚合计算

    • 点赞、共同关注、抽奖活动

  • Zset

    • 排序场景

    • 排行榜

    • 电话

    • 姓名排序

  • BitMap

    • 二值状态

    • 签到

    • 是否登录

    • 连续签到用户总数

  • HyperLogLog

    • 海量数据基数统计

    • 百万级网页UV计数

  • GEO

    • 存储地理位置信息
  • Stream

    • 消息队列

      • 自动生成全局唯一id

      • 支持以消费组形式消费数据

Zset 底层实现

  • 结合哈希表和跳表,实现支持唯一性和有序性的集合。每个元素包含一个唯一的成员和一个可以重复的分数,根据分数进行排序

  • 底层使用跳表和HashTable。跳表用于按分数进行排序存储;字典用于快速查找元素

    • 跳表,在链表的基础上建立多层索引,增删查复杂度logN

      • 特点:
  1. 元素按分数排序:跳表中每个节点都按分数递增排列
  2. 多层索引:跳表有多层链表,每一层链表上的元素都是下一层链表元素的子集。更高的层数意味着更少的元素,查找时可以快速跳过较多的元素,从而加快速度
  3. 时间复杂度LogN 空间复杂度N

    • HashTable,将成员作为键,分数作为值

跳表如何实现

Redis如何实现消息队列

  • 使用Redis的发布订阅功能

    • Redis发布订阅。生产者将消息发布到指定的channel,消费者订阅此channel,接受并处理消息、 该模式支持一对多,也可以实现分组订阅,将不同的消费者分为不同的组,实现广播或者点对点的消息传递。
  • List数据结构

    • 实现消息队列的入队、出队操作。生产者将消息插入List尾部,消费者从List头部获取消息。使用阻塞时的POP操作实现消费者等待新消息到达,也可以使用定时轮询来获取新消息

Redis实现分布式锁

  • 锁的核心在于互斥

  • SETNX (SET If Not Exists) 如果key不存在,才会设置key的值 key已经存在,SETNX啥也不做

  • 释放锁,直接DEL对应的key

  • 使用EXPIRE设置过期时间,避免锁无法被释放 需要

  • 防止误删到其他锁,使用lua脚本确保解锁操作的原子性

Redisson实现分布式锁

  • Redisson是操作redis的Java框架

  • 设置分布式锁的过期时间,避免锁一直被占用导致死锁

  • 为每个锁关联一个线程id和重入次数作为锁value的一部分存储在redis中,避免误删和不可重入

  • redisson自动续期,提高定时任务延长锁的有效期

大量Key集中过期

定期删除执行中,突然遇到大量的key过期,客户端请求需要等待定期清理过期key任务线程执行完成。因为定期任务线程在主线程中执行,导致客户端请求没办法及时处理,相应变慢

解决方式

  • 给key设置随机过期时间

  • 开启延迟释放,redis采用异步的方式延迟释放key使用的内存,将该操作交给单独的子线程处理,避免阻塞主线程

大Key

big Key

  • 一个key对应的value占用内存过大。 例如String类型的value超过1M,复合类型包含的元素超过5000个

  • 产生的原因

    • 程序设计不当:直接使用String类型存储较大的文件对应的二进制数据

    • 业务数据规模考虑不当:使用集合类型未考虑数据量的快速增长

    • 未及时清理数据:哈希中存在大量的冗余键值对

hot Key

一个key的访问次数明显多于其他key。redis实例的每秒请求处理达到5000,每个key每秒访问达2000

出现原因:某个热点数据访问量突然暴增

危害

  • 占用大量的cpu、带宽,影响redis实例对其他请求的正常处理

性能优化

使用批量操作减少网络传输,降低往返时间

  • Redis命令执行分为四步:
  1. 发送命令
  2. 命令排队
  3. 命令执行
  4. 返回结果

使用原生批量操作

  • MGET获取一个或多个指定的key,对应MSET HMGET获取哈希表中一个或者多个指定字段的值,对应HMSET

对于不支持批量操作的命令,使用pipeline将一批Redis命令封装成一组,一次性提交到Redis服务器,只需要一次网络传输。

优化方案

  • 缩短键值对的存储长度

  • 使用延迟删除特性

  • 设置合理的过期时间

  • 金庸长耗时的查询命令

  • 使用slowlog优化耗时命令

  • 使用pipeline批量操作数据

  • 避免大量数据同时失效

  • 对于不重要的数据关闭持久化功能

Redis、MySQL数据缓存一致性

读数据,选择旁路缓存策略,cache命中则直接从db加载数据到cache;

写数据,先更新db,再删除cache

布隆过滤器

一种空间效率搞得概率性数据结构,用于判断元素是否存在集合中。基于位数组和哈希函数

雪崩、击穿、穿透

缓存雪崩

  • 大量缓存数据同一时间过期,或者Redis服务器宕机,此时有大量的用户请求,无法在Redis中处理,于是全部请求数据库,导致数据库压力骤增。

  • 解决方式

      1. 设置随机过期时间,避免大量的键同时过期
      1. 实现缓存预热:系统启动或缓存失效前,提前将热门数据加载到缓存中,避免在关键时刻大量请求直接访问后端服务
      1. 使用分布式缓存:将缓存数据分布在多个缓存节点上,分散请求负载
      1. 设置熔断机制:缓存失效,直接返回默认值或者默认值,避免直接访问后端
      1. 实时监控:监控缓存状态,异常则报警

缓存击穿

  • 热点数据过期或者失效,大量请求访问该数据,导致请求直接访问数据库

  • 解决方式

      1. 设置热点数据永不过期,或者设置较长的过期时间
      1. 加互斥锁、分布式锁:访问热点数据,保证只有一个线程访问后端,其他线程等待结果。当第一个线程获取到数据,其他线程可以直接从缓存中获取
      1. 限制并发访问:控制请求的流量

缓存穿透

  • 大量请求查询不存在缓存或者数据库中的数据,导致直接访问后端

    • 原因:
  1. 恶意请求,故意查询不存在的数据
  2. 高并发请求,缓存无法名字
  • 解决方式

      1. 布隆过滤器:用于快速判断一个元素是否存在集合中。查询请求到达时,使用布隆过滤器判断请求对应的数据是否存在缓存或者数据库中,避免无效查询
      1. 缓存空值处理:将空结果也缓存起来,设置一个短过期时间,避免频繁查询。
      1. 异步加载缓存:缓存未命中,异步加载数据到缓存中,通过互斥锁或者分布式锁保证一个线程加载数据
      1. 热点数据永不过期
  1. 限制恶意请求:加验证码,控制访问频率

缓存淘汰和过期删除

内存淘汰

  • 当内存满了,redis触发内存淘汰策略,淘汰掉不必要的内存

过期删除

  • 将已过期的键值对进行删除,惰性删除+定期删除

持久化

如何保证数据不丢失

  • 持久化

    • 将数据从内存中存储到持久化存储介质中的过程,以便在程序重启或者系统崩溃能够从存储介质中恢复数据

      • RDB,RedisDataBase持久化:快照方式持久化,将某一时刻的内存数据,以二进制的方式写入磁盘

        • 将某一时刻的内存快照,以二进制方式写入磁盘的持久化机制。

        • 优点: 速度快,相对于AOF,RDB持久化速度更快,只需要在指定的时间间隔内将数据从内存写入到磁盘 空间占用小,数据压缩在二进制文件中 恢复速度快,可靠性高

        • 缺点:指定时间间隔保存,会丢失一些数据 实时性差:定期执行

      • AOF,Append Only File持久化:文件追加持久化,记录所有非查询操作命令,并以文本形式追加到文件中

        • 将Red的每个非查询操作命令都追加到文件中

        • 优点:数据不易丢失、实时性好,数据可读性强(纯文本的形式)

        • 缺点:写入性能低、占用磁盘大、文件可能出现损坏

      • 混合持久化,二者混合。写入时,当前数据以RDB形式写入文件的开头,再将后续的操作命令以AOF的格式存入文件。既保证重启速度,又降低数据丢失

        • 复杂度高、可读性差、兼容性差
  • 集群运行

    • 将单个redis服务器换成集群。

      • 主从同步

        • 主要存储数据的节点作为主节点,其他节点复制主节点的数据
      • 哨兵模式

        • 主节点崩溃需要手动恢复,于是哨兵模式自动恢复。哨兵模式监控主节点
      • Redis Cluster

        • 拥有多个主节点
  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...