目录
一、Redis删除策略
(1)过期数据
Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态
XX :具有时效性的数据
-1 :永久有效的数据
-2 :已经过期的数据或被删除的数据或未定义的数据
问:过期的数据真的删除了吗?
答:不是的
(2)数据删除策略
数据删除策略的目标:
在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕 机或内存泄露
删除策略有:
- 定时删除
- 惰性删除
- 定期删除
1.定时删除
创建一个定时器,到时间立即删除
属于拿时间换空间,对CPU性能要求比较高
2.惰性删除
数据到达过期时间不立即删除,当下次访问的时候才会删除
属于拿空间换时间,对空间的占用比较大
3.定期删除
数据达到过期时间,隔一会儿删除一部分、隔一会儿再删一部分
这样对CPU的性能和空间都会比较友好
定时删除 | 节约内存,无占用 | 不分时段占用CPU资源 |
惰性删除 | 内存占用严重 | 延时执行,CPU利用率高 |
定期删除 | 内存定期随机清理 | 每秒花费固定的CPU资源维护内存 |
(3)逐出算法
当新数据进入redis时,内存不足则就要用到逐出算法了
逐出算法有两种方式:
lru:挑选最近最少使用的数据淘汰
lfu:挑选最近使用次数最少的数据淘汰
二、企业级解决方案
(1)缓存预热
就服务器重启后请求数据过大,会出现“宕机”问题,就可以使用缓存预热。就是在系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候先查询数据库,然后再将数据缓存的问题。
(2)缓存雪崩
指在同一时间段大量的缓存key同时失效,或Redis服务宕机,导致大量请求到达数据库带来大量压力。
解决方案:
- 给不同的key的TTL添加随机值
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
(3)缓存击穿 (解决热点key问题)
就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
解决方案:
- 互斥锁
-
因为锁能实现互斥性。假设线程过来,只能一个人一个人的来访问数据库,从而避免对于数据库访问压 力过大,但这也会影响查询的性能,因为此时会让查询的性能从并行变成了串行,我们可以采用tryLock方法 + double check来解决这样的问题。
-
- 逻辑过期
-
我们之所以会出现这个缓存击穿问题,主要原因是在于我们对key设置了过期时间,假设我 们不设置过期时间,其实就不会有缓存击穿的问题,但是不设置过期时间,这样数据不就一直占用我们 内存了吗,我们可以采用逻辑过期方案。我们把过期时间设置在 redis的value中,注意:这个过期时间并不会直接作用于redis,而是我们后续 通过逻辑去处理。假设线程1去查询缓存,然后从value中判断出来当前的数据已经过期了,此时线程1去获得互斥锁,那么其他线程会进行阻塞,获得了锁的线程他会开启一个 线程去进行 以前的重构数据 的逻辑,直到新开的线程完成这个逻辑后,才释放锁, 而线程1直接进行返回,假设现在线程3过来访 问,由于线程线程2持有着锁,所以线程3无法获得锁,线程3也直接返回数据,只有等到新开的线程2把 重建数据构建完后,其他线程才能走返回正确的数据。 这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据。
-
(4)缓存穿透
客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
如图所示请求先在Redis中查找,找不到就会穿透缓存,进入到数据库中去 ,这样就会导致资源的浪费;而且我们知道数据库能够承载的并发不如redis那么高,如果有大量的请求同时来访问这种不存在的数据,就有可能会出现错误。
解决方案:
- 缓存空对象
缓存空对象就是哪怕这个数据在数据库中不存在,我们也把 这个数据存入到redis中去,这样下次用户来访问这个不存在的数据,那么在Redis中也能找到这个数据,就不会进入到数据库中去了,如图所示:
- 优点:实现简单,维护方便
- 缺点:
- 额外的内存消耗
- 可能造成短期不一致
2.布隆过滤
如上图所示, 布隆过滤器其实采用的是哈希思想来解决这个问题,通过一个庞大的二进制数组,走哈希思 想去判断当前这个要查询的这个数据是否存在,如果布隆过滤器判断存在,则放行,这个请求会去访问redis,哪怕此时redis中的数据过期了,但是数据库中一定存在这个数据,在数据库中查询出来这个数 据后,再将其放入到redis中, 假设布隆过滤器判断这个数据不存在,则直接返回。
- 优点:内存占用较少,没有多余key
- 缺点:实现复杂,存在误判可能
三、Redis面试题
(1)Redis采用单线程,如何保证高并发?
Redis快的主要原因是:
- 完全基于内存
- 数据结构简单,对数据操作也简单
- 使用多路 I/O 复用模型,充分利用CPU资源
(2) 这样做的好处是什么?
单线程优势有下面几点:
- 代码更清晰,处理逻辑更简单
- 不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为锁而导致的性能消耗
- 不存在多进程或者多线程导致的CPU切换,充分利用CPU资源
(3)Redis的持久化方案有哪些?
(4)Redis在项目中的哪些地方有用到?
(5)Redis实现分布式锁
为了避免“超卖“”问题与并发问题,我们引入了分布式锁
分布式锁是一种多节点共享的同步机制,通过在多个节点之间协调访问资源,确保在同一时间只有一个节点能够获取锁并执行关键操作。在电商网站中,可以将每个商品的库存作为共享资源,使用分布式锁来控制并发访问
分布式锁的目的是保证在分布式部署的应用集群中,多个服务在请求同一个方法或者同一个业务操作的情况下,对应业务逻辑只能被一台机器上的一个线程执行,避免出现并发问题 。
分布式锁要满足的条件:
- 多进程互斥:同一时刻,只有一个进程可以获取锁
- 保证锁可以释放:任务结束或出现异常,锁一定要释放,避免死锁
- 阻塞锁(可选):获取锁失败时可否重试
- 重入锁(可选):获取锁的代码递归调用时,依然可以获取锁
(6)如何实现数据库与缓存数据一致
实现方案有下面几种:
- 本地缓存同步:当前微服务的数据库数据与缓存数据同步,可以直接在数据库修改时加入对Redis的修改逻辑,保证一致。
- 跨服务缓存同步:服务A调用了服务B,并对查询结果缓存。服务B数据库修改,可以通过MQ通知服务A,服务A修改Redis缓存数据
- 通用方案:使用Canal框架,伪装成MySQL的salve节点,监听MySQL的binLog变化,然后修改Redis缓存数据(主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费)