Redis 八股文

重点

redis是单线程还是多线程 

Redis为什么是单线程的?

什么是缓存穿透?怎么解决

什么是缓存击穿?怎么解决?

什么是缓存雪崩?怎么解决?

数据库与缓存不一致如何解决

基础

redis是怎么删除过期key

缓存是如何淘汰的

描述一下持久化原理

Redis都有哪些使用场景

Redis数据类型

Redis 有事务吗?

扩展

如何进行缓存预热

Redis怎么实现分布式锁

分布式锁容易出现的问题

Redis如何做内存优化?


重点

redis是单线程还是多线程 

无论什么版本,工作线程是单线程

Redis6.0之前是单线程的,Redis6.0之后在网络IO处理方面引入了多线程;
redis内部使用了基于epoll的多路服用,也可以多部署几个redis服务器解决单线程的问题;
redis主要的性能瓶颈是内存和网络;
内存好说,加内存条就行了,而网络才是大麻烦,所以redis6内存好说,加内存条就行了;
而网络才是大麻烦,所以redis6.0引入了多线程的概念,
redis6.0在网络IO处理方面引入了多线程,如网络数据的读写和协议解析等,需要注意的是,执行命令的核心模块还是单线程的。

Redis为什么是单线程的?

  1. 代码更清晰,处理逻辑更简单;
  2. 不用考虑各种锁的问题,不存在加锁和释放锁的操作,没有因为可能出现死锁而导致的性能问题;
  3. 不存在多线程切换而消耗CPU;

什么是缓存穿透?怎么解决

缓存穿透‌是指查询一个在缓存中和数据库中都不存在的数据,导致每次请求都要查询数据库的问题。

如何解决:

  1. 使用布隆过滤器‌:布隆过滤器是一种数据结构,利用极小的内存可以判断大量的数据“一定不存在或者可能存在”。通过哈希函数将数据映射到布隆过滤器中,用户发送的请求会先被布隆过滤器拦截,一定不存在的数据就直接拦截返回,从而避免对数据库进行无效查询。可以使用bitmap做布隆过滤器。

  2.  

    空缓存处理‌:当数据库中不存在该数据时,将空结果(如null)缓存一段时间,避免缓存被频繁访问而导致数据库压力增大。这种方法可以减少对数据库的直接访问,但需要注意不要无限期地缓存空结果,以免浪费缓存空间。

什么是缓存击穿?怎么解决?

当前key是一个热点key(例如一个秒杀活动),并发量非常大。 重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的SQL、多次IO、多个依赖等。 在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。

为了解决缓存击穿问题,可以采取以下策略:

  • 使用互斥锁‌:在获取数据时,使用分布式锁(如‌Redis的分布式锁)来控制同时只有一个请求去后端获取数据,其他请求需要等待锁释放。这样可以防止多个请求同时穿透到后端存储系统。‌
  • 设置热点数据永不过期‌:对于一些热点数据,可以将其永不过期,确保即使数据过期后,仍然可以从缓存中获取。‌
  • 使用缓存预热‌:在系统启动或低峰时段,预先加载热点数据到缓存中,以减少因缓存过期导致的击穿问题。‌

什么是缓存雪崩?怎么解决?

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力,造成数据库后端故障,从而引起应用服务器雪崩。

 解决:

1、避免缓存集中失效,不同的key设置不同的超时时间

2、增加互斥锁,控制数据库请求,重建缓存。

数据库与缓存不一致如何解决

数据库和缓存之间的不一致通常发生在以下几种情况:

缓存更新滞后:当数据在数据库中被修改后,如果缓存没有及时更新或清除,则会导致后续请求读取到过期的缓存数据。
并发写操作:多个客户端同时对同一数据进行写操作时,可能导致一部分更新丢失或覆盖。
网络延迟:在网络不稳定的情况下,更新命令可能未能及时到达目标系统。
故障恢复:系统出现故障后重启,如果没有正确的恢复机制,可能会导致数据状态不一致。

解决策略

参考:缓存不一致问题四种解决方案

  • 延迟双删策略‌:先删缓存,再更新数据库,然后经过一段时间后再删除缓存中的数据,以避免因网络延迟等原因造成的不一致问题。
  • 同步删除:更新请求进来,我们先更新数据库再删除缓存

基础

redis是怎么删除过期key

Redis 使用两种方式来删除过期的 key:

  1. 惰性删除:当 key 过期时,并不立即删除它,而是等到下次访问这个 key 时再检查它的过期时间,如果已经过期则删除它。

  2. 定时删除:Redis 默认每 100 毫秒会随机抽查一些设置了过期时间的 key,检查并删除其中已经过期的 key。

这两种方式结合使用,确保过期的 key 最终会被删除,但不会在访问 key 时阻塞当前操作。

缓存是如何淘汰的

Redis缓存淘汰是指当缓存数据超过最大内存限制时,Redis 会根据一定的淘汰策略来删除一些不常用或者不重要的数据,以便为新的数据腾出空间。

Redis 提供了以下几种淘汰策略:

  1. noeviction: 不进行任何淘汰,当内存不足时,会返回错误给客户端。

  2. allkeys-lru: 当内存不足以容纳更多数据时,使用最近最少使用算法(LRU)来淘汰键。

  3. allkeys-random: 随机淘汰键,不考虑使用频率。

  4. volatile-lru: 只对设置了过期时间的键进行LRU淘汰。

  5. volatile-random: 随机淘汰设置了过期时间的键。

  6. volatile-ttl: 淘汰设置了过期时间的键,优先淘汰生存时间(TTL)短的键。

可以通过配置文件或者 CONFIG SET 命令动态设置淘汰策略。

描述一下持久化原理

Redis的持久化主要有两种方式:RDB(Redis DataBase)和AOF(Append Only File)。

 

  1. RDB:定时将内存中的数据快照保存到磁盘的一个压缩二进制文件中。可以配置定时任务来控制快照的频率。当Redis需要持久化数据时,Redis会fork()一个子进程来处理数据保存工作,父进程继续处理客户端请求。

  2. AOF:每个写命令都通过append()操作保存到文件中。在服务重启时,通过重放这些命令来恢复数据。AOF文件中的命令通常是不可变的,因此可以确保数据的持久性

AOF

系统提供了 fync 和 fdatasync 两个同步函数,它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘,以及三种策略:

always:同步写回,每个写命令执行完立刻同步地将日志写回磁盘。(性能最差,最多丢失一个写指令的数据)
everysec(默认):每秒执行一次。(是性能和数据安全性的折中方案,最多也就丢一秒的数据)
no:根据操作系统和资源的情况,一定时间执行一次,时间不确定。(性能最好,可能会丢失上次同步AOF文件之后的所有写命令数据)

 

Redis都有哪些使用场景

  • 缓存‌:Redis可以用作应用程序的缓存层,减少对数据库的读取压力,提高数据访问速度。例如,热点数据缓存(如报表、明星出轨等)、对象缓存、全页缓存等‌。
  • 会话存储‌:在Web应用中,Redis可以用来存储用户的会话信息,如登录状态、购物车内容等。通过将会话数据存储在Redis中,可以实现无状态的服务器之间共享用户相关的状态数据‌。
  • 分布式锁‌:在分布式系统中,Redis可以用于实现分布式锁,确保在多个节点之间共享资源的一致性。通过使用Redis的原子操作命令,如SETNX,可以实现分布式锁的功能‌。
  • 排行榜和计数器‌:Redis支持原子操作,非常适合实现实时排行榜、点赞数、访问计数等功能。利用Redis的INCR命令,可以轻松实现计数器的功能‌。
  • 消息队列‌:Redis可以用作消息队列系统,用于处理异步任务,例如邮件发送、后台任务处理等。通过使用Redis的列表(List)数据结构,可以实现消息的发布和订阅‌。
  • 实时分析‌:Redis可以用于实时分析,如用户行为分析、实时统计信息等。利用Redis的高速性能,可以实时处理和分析大量数据‌。
  • 此外,Redis还用于实现全局ID生成、限流、位统计等场景。例如,利用Redis的INCR命令可以实现计数器的功能,用于生成全局唯一的ID;利用限流功能可以控制对资源的访问频率,防止过载;位统计则利用Redis的Bitmap数据结构,以节省空间的方式实现大数据量的统计‌。

 

Redis数据类型

  • string(字符串): 基本的数据存储单元,可以存储字符串、整数或者浮点数。
  • hash(哈希):一个键值对集合,可以存储多个字段。
  • list(列表):一个简单的列表,可以存储一系列的字符串元素。
  • set(集合):一个无序集合,可以存储不重复的字符串元素。
  • zset(sorted set:有序集合): 类似于集合,但是每个元素都有一个分数(score)与之关联。

Redis 有事务吗?

Redis是有事务的,redis中的事务是一组命令的集合,这组命令要么都执行,要不都不执行,

redis事务的实现,需要用到MULTI(事务的开始)和EXEC(事务的结束)命令 ;discard(取消事务)

Redis的事务除了保证所有命令要不全部执行,要不全部不执行外,还能保证一个事务中的命令依次执行而不被其他命令插入。同时,redis的事务是不支持回滚操作的。若在事务队列中存在命令性错误(类似于java编译性错误),则执行EXEC命令时,所有命令都不会执行
 

扩展

如何进行缓存预热

1、应用启动时预热

  • 使用启动监听事件实现缓存预热。
  • 使用 @PostConstruct 注解实现缓存预热。
  • 使用 CommandLineRunner 或 ApplicationRunner 实现缓存预热。
  • 通过实现 InitializingBean 接口,并重写 afterPropertiesSet 方法实现缓存预热。

参考:SpringBoot实现缓存预热的几种常用方案

2、定时任务预热

3、‌使用消息队列提高预热效率

Redis怎么实现分布式锁

参考:Redis实现分布式锁的7种方案

1、使用Lua脚本(包含SETNX + EXPIRE两条指令)

‌2、使用set 的扩展命令   set id value  NX EX 100

  • NX :表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁,才能获取。
  • EX seconds :设定key的过期时间,时间单位是秒。
  • PX milliseconds: 设定key的过期时间,单位为毫秒
  • XX: 仅当key存在时设置值

3、使用Redisson框架

分布式锁容易出现的问题

  1. 锁未被释放
  2. B锁被A锁释放了
  3. 数据库事务超时
  4. 锁过期了,业务还没执行完
  5. Redis主从复制的问题

Redis如何做内存优化?


1、缩短键值的长度

缩短值的长度才是关键,如果值是一个大的业务对象,可以将对象序列化成二进制数组;
首先应该在业务上进行精简,去掉不必要的属性,避免存储一些没用的数据;
其次是序列化的工具选择上,应该选择更高效的序列化工具来降低字节数组大小;
以JAVA为例,内置的序列化方式无论从速度还是压缩比都不尽如人意,这时可以选择更高效的序列化工具,如: protostuff,kryo等
2、共享对象池

对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。 除了整数值对象,其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。

3、字符串优化

4、编码优化

5、控制key的数量

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值