Redis八问

一: 为什么使用Redis

redis的优点:
1.运行在内存中,读写速度快,110000次和80000次每秒
2.也可以持久化
3.支持丰富的数据类型(string , set , hash , list , zset)

从性能上考虑: 加入有的sql请求需要长时间来执行,就特别适合存储在内存中,后面每次相同的请求就直接去内存中获取数据,无需每次访问数据库,这样提高效率
从并发上考虑: 在大量并发的情况下,大量请求都去访问数据库,可以把结果缓存到redis中,无需所有请求都去访问数据库,提高并发量

redis的特性
1.速度快
2.键值对的数据结构服务器
3.丰富的功能
4.简单稳定
5.持久化
6.主从复制
7.高可用和分布式缓存
8.客户端语言多

二:使用Redis的缺点

1.缓存和数据库双写一致性的问题

2.缓存雪崩问题

3.缓存击穿问题

4.缓存的并发竞争问题

三:单线程的Redis为什么这么快

1.基于内存的存储(读110000 , 写80000)
2.c语言编写-无需编译
3.主线程是单线程–
4.多路复用
5.RESP协议 --从.aof文件可以看得出

理解redis单线程工作模型

为什么设计为单线程:?
1.单线程减少了cpu资源的争用,避免上下文切换
2.基于内存读写,单线程读写耗时更小

单线程模型读写效率高:
1.纯内存操作
2.核心是基于非阻塞的IO多路复用机制
3.单线程反而避免了多线程的频繁上下文切换问题

四:Redis的数据类型和使用场景

1.String类型: 一般做一些复杂的计数功能的缓存

2.Hash类型: 单点登陆

3.List类型: 做简单的消息队列的功能

4.Set类型: 做全局去重的功能

5.SortedSet类型: 做排行榜应用,取topN操作;延时任务;做范围查找

五:Redis的过期策略和内存淘汰机制

1. 1.Redis采用的过期策略是定期删除 + 惰性删除策略

定期删除

redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的 key。

Redis 默认会每秒进行十次过期扫描(100ms一次),过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。

从过期字典中随机 20 个 key;
删除这 20 个 key 中已经过期的 key;
如果过期的 key 比率超过 1/4,那就重复步骤 1;

redis默认是每隔 100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。

惰性删除

所谓惰性策略就是在客户端访问这个key的时候,redis对key的过期时间进行检查,如果过期了就立即删除,不会给你返回任何东西。

定期删除可能会导致很多过期key到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,即当你主动去查过期的key时,如果发现key过期了,就立即进行删除,不返回任何东西.

1. 2.内存淘汰机制

目的:避免过期的key占用太多资源

1.no-eviction:不会继续服务写请求(DEL请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。
2.volatile-lru:尝试淘汰设置了过期时间的 key,最少使用的 key 优先被淘汰。没有设置过期时间的key不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。(这个是使用最多的)
3.volatile-ttl:跟上面一样,除了淘汰的策略不是 LRU,而是 key 的剩余寿命 ttl 的值,ttl 越小越优先被淘汰,即淘汰将要过期的数据.
4.volatile-random:从已设置过期时间的数据集(server.db[i].expires)中随机选择数据淘汰
5.allkeys-lru:区别于 volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不只是过期的 key 集合。这意味着没有设置过期时间的 key 也会被淘汰。
6.allkeys-random:从全体的key集合(server.db[i].dict)中任意选择数据淘汰

六:Redis和数据库双写一致性的问题

1.最终一致性

先更新数据库,再更新缓存

先更新数据库再更新缓存的问题,线程A去写数据库,线程B去读缓存,这个时候存在数据库和缓存的数据不一致的情况,那么读取的数据就不是最新的数据
如果有多个线程同时去更新数据库和更新缓存,如果A线程和B线程先后更新了数据库,然后A线程出现问题,滞后了,这时候B线程就会优先于A线程去更新缓存,接着A又去更新缓存,那么就会出现缓存和数据库的数据不一致了.
一般不建议使用

先删除缓存,再更新数据库

先删除缓存再更新数据库的问题,同时有两个线程进行操作,A线程去删除缓存,B线程去读取数据,当A删除缓存后,B读取缓存失败,就会去读取数据库,如果A未及时更新数据库,那么B读取的数据库的数据就是旧的数据,而且B还会把读取的数据库缓存到内存中,待A更新完成数据库后,就会造成缓存和数据库数据的不一致问题

解决方案:
采用延时双删策略
就是在A线程更新数据库后再更新一次缓存(之间的间隔时间视具体业务而定,一般在读业务的耗时基础上增加百毫秒以上)

先更新数据库,再删除缓存

可能出现的问题,当缓存失效的时候,线程A读取到旧的数据库数据,然后线程B去跟新数据库接着跟新了缓存,最后线程A又跟新了一遍缓存,就会造成数据库和缓存数据不一致,这种问题概率相对比较小.因为线程A的读速度是比较快的,比线程B的写速度要快的多,一般情况下都是A先跟新缓存然后B再跟新缓存,就不会造成数据不一致的问题,所以正常情况下不会发生不一致的问题,这种方案相对更好.

2.强一致性

1.如果对缓存的要求是强一致性的,请不要使用缓存

七:如何应对缓存穿透和缓存雪崩问题

1.缓存穿透:

 问题描述: 大量不存在的非法数据请求同时间落到数据库中,由于缓存中没有数据,可能导致缓存穿透
 解决思路:
      1.业界普遍用法--互斥锁排队,根据key获取value,如果value值为空就锁上,直到load数据再释放锁.如果没有获取到所得线程就等待一段时间后重试. 分布式环境需要分布式锁,单机模式使用同步锁即可.  缺点: 就是增加逻辑复杂度,降低并发量,治标不治本
      2.接口限流与熔断 , 降级 : 在重要的接口中做好限流策略,防止恶意攻击,同时要降级准备,当接口中的某些服务不可用的时候,进行熔断,失败快速返回机制
      3.布隆过滤器,bloomfilter,类似于一个hash set,可以快速判断一个元素是否存在集合中.其默认容错率为0.03

解决方案:
把订单数据存储到布隆过滤器中,有压缩,记录数据的状态,查询数据之前,先过一遍布隆过滤器Bloom Filter,判断是否存在该数据,如果存在再允许查询数据库.
实现布隆过滤器的方式: guava

2.缓存雪崩:

 问题描述:缓存在同时间大量失效,致使大量请求到数据库
 解决思路:
      1.加锁排队--类似缓存穿透
      2.建立备份缓存, a缓存设置失效时间,b缓存永久有效,先读取a缓存,没有再查找b缓存,还有设置更新缓存
      3.设置缓存失效的时候加上一个随机时间长度,错开缓存失效时间,避免缓存集中失效

解决方案:
1.基本解决方案–在代码块中加锁,先让一个线程请求查询数据库,其余的阻塞等待,等待查询结果加入到redis缓存中,其余的请求全部去查询redis即可

八:如何解决Redis并发竞争Key的问题

问题描述: 多个redis客户端同时执行set key操作,引起的并发问题.

  1. redis本身就是单线程的,按照先到先执行的原则,其余的阻塞
    2.另外也可以把set操作串行化,在队列中排队执行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值