目 录CONTENT

文章目录

Redis常见面试

Josue
2022-03-30 / 0 评论 / 0 点赞 / 133 阅读 / 4,276 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-03-30,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1、五种基本类型底层数据结构

1.1、字符串

image-20220330120409712

其中:embstr和raw都是由SDS动态字符串构成的。

唯一区别是:raw是分配内存的时候,redisobject和sds各分配一块内存,而embstr和redisobject是在一块儿内存中。

1.2、列表

image-20220330120734346

1.3、hash

image-20220330120946596

1.4、set

image-20220330121029089

1.5、zset

image-20220330121236402

2、缓存雪崩

image-20220330121316676

2.1、举例

双十一期间,所有用户一打开淘宝就是进入首页,首页的压力非常大,为了提高并发,将网站首页数据都缓存到redis里,所有的rediskey失效时间都是3小时。
双十一当天大量用户刺手狂欢,这时候3个小时过去了,redis里首页的key缓存全部失效,这时候redis里查询不到数据了,只能去数据库中查询,造成数据库无法响应挂掉。

**总结:**在高并发下,大量缓存key在同一时间失效,大量请求直接落在数据库上,导致数据库岩机。

2.2、解决方案

  • 随机设置key失效时间,避免大量key集体失效。

    setRedis(Key, value, time + Math.random()* 10000)
    
  • 若是集群部署,可将热点数据均匀分布在不同的Redis库中也能够避免key全部失效问题。

  • 不设置过期时间

  • 跑定时任务,在缓存失效前刷进新的缓存

3、缓存穿透

image-20220330121852178

3.1、举例

某人用脚本疯狂发送请求,查询id=-1的数据,redis并没有这样的数据,这时候就穿透redis,直接
打到了数据库上,导致数据库挂了。

总结:redis缓存和数据库中没有相关数据(例用户直接携带id<=0的参数不断发起请求),redis中没有这样的数据,无法进行拦截,直接被穿透到数据库,导致数据库压力过大岩机。

3.2、解决方案

  • 对不存在的数据缓存到redis中,设置key,value值为null不管是数据为null还是系统bug问题),并设置一个短期过期时间段,避免过期时间过长影响正常用户使用。
  • 拉黑该IP地址
  • 对参数进行校验,不合法参数进行拦截
  • 布隆过滤器将所有可能存在的数据哈希到一个足够大的bitmap(位图)中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

4、缓存击穿

image-20220330122521203

4.1、举例

程序员将商品的信息存到了redis中,设置了3小时过期。竞拍了2小时59分,突然商品在redis里的key数据过期了,导致该key的大量请求,都打到了数据库,直接导致数据库挂掉了,服务无法响应。

总结:某一个热点key,在不停地打着高并发,当这个热点key在失效的一瞬间,持续的高并发访问就击破缓存直接访问数据库,导致数据库岩机。

4.2、解决方案

  • 设置热点数据“永不过期
  • 加上互斤锁:上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁锁住它,其他的线程走到这一步就拿不到锁,等着第一个线程查询到了数据,然后将数据放到redis缓存起来。后面程序进来发现有了缓存,就直接走缓存了。
#简单的分布式锁实现
public String get(key){
    String value = redis.get(key);
    if(value == null){	//代表缓存值过期
        //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
        String keynx = key.concat(":nx");
        if(redis.setnx(keynx))
    }
}

5、缓存雪崩、缓存穿透、缓存击穿的区别

  • 缓存雪崩是大面积的kye缓存失效

  • 缓存穿透是redis中不存在这个缓存key

  • 缓存击穿是某一热点key突然失效

6、单线程的redis为什么这么快

Redis有多快?官方给出的答案是读写速度10万/秒,为什么单线程的Redis速度这么快?原因有以下几点:

纯内存操作

  • Redis是完全基于内存的,所以读写效率非常的高,当然Redis存在持久化操作,在持久化操作是都是fork
    子进程和利用Linux系统的页缓存技术来完成,并不会影响Redis的性能。
  • 单线程操作:单线程并不是坏事,单线程可以避免了频繁的上下文切换,频繁的上下文切换也会影响性能的.
  • 合理高效的数据结构
  • 采用了非阻塞IO多路复用机制:多路I/O复用模型是利用select、poll、epoll可以同时监察多个流的IO事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有IO事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这工种做法就避免了大量的无用操作。

7、Redis的数据过期淘汰策略

Redis中数据过期策略采用定期删除+惰性删除策略。

7.1、定期删除和惰性删除策略

  • 定期删除策略:Redis启用一个定时器定时监视所有的key,判断key是否过期,过期的话就删除。这种策略可以保证过期的key最终都会被删除,但是也存在严重的缺点:每次都遍历内存中所有的数据,非常消耗CPU资源,并且当key已过期,但是定时器还处于未唤起状态,这段时间内key仍然可以用。

  • 惰性删除策略:在获取key时,先判断key是否过期,如果过期则删除。这种方式存在一个缺点:如果这个工key一直未被使用,那么它一直在内存中,其实它已经过期了,会浪费大量的空间。

7.2、定期删除和惰性删除策略如何工作

这两种策略天然的互补,结合起来之后,定时删除策略就发生了一些改变,不在是每次扫描全部的key了,而是随机抽取一部分key进行检查,这样就降低了对CPU资源的损耗,情性删除策略互补了为检查到的key,基本上满足了所有要求。

但是有时候就是那么的巧,既没有被定时器抽取到,又没有被使用,这些数据又如何从内存中消失?没关系,还有内存淘达机制,当内存不够用时,内存淘汰机制就会上场。Redis内存淘汰机制有以下几种策略:

  • volatile-liru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

  • volatile-random:从已设置过期时间的数据集(server.dblij].expires)中任意选择数据淘汰

  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)

  • alkeys-random:从数据集(senver.dblj.dict)中任意选择数据淘汰

  • no-eviction:禁止驱逐数据,永不过期,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!(默认值)

4.0版本后增加一下两种

  • volatile-lfu:从已设置过期时间的数据集(server.dbli].expires)中挑选最不经常使用的数据淘达

  • allkeys-lfu:当内存不足以容纳新写入数据,在键空间移除最不经常使用的key

8、redis的线程模型是怎么样的

redis内部使用文件事件处理器file event handler,这个文件事件处理器是单线程的,所以redis才叫做单线程的模型。它采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器进行处理。

文件事件处理器的结构包含4个部分:

  • 多个socket

  • IO多路复用程序

  • 文件事件分派器

  • 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)

    多个socket可能会并发产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个socket,会将socket产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。

9、redis和memcached的区别

  • **存储方式不同:**memcache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;Redis有部份存在硬盘上,这样能保证数据的持久性。

  • 数据支持类型:memcache对数据类型支持相对简单;Redis有复杂的数据类型

  • 使用底层模型不同:它们之间底层实现方式,以及与客户端之间通信的应用协议不一样,Redis自己构建了vm机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。value值大小不同:Redis最大可以达到1gb;memcache只有1mb。

10、redis实现事务

Redis通过MULTI、EXEC、WATCH等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。

在传统的关系式数据库中,常常用ACID性质来检验事务功能的可靠性和安全性。在Redis中,事务总是具有原子性(Atomicity)、一致性(Consistency)和隔离性(lsolation),并且当Redis运行在某种特定的持久化模式下时,事务也具有持久性(Durability)

11、一致性(延迟双删)

延迟双删,双删就是先删除缓存,然后在更新完数据库后再删一次。

**目的:**是为了删除在写MySQL期间读线程可能把脏数据再次读到Redis里,延迟的时间参照一次从MySQL读数据并写入Redis的时间

12、Redis集群,集群原理

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

13、检索特定的key

假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?

  • 使用keys指令可以扫出指定模式的key列表,keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复.
  • scan指令可以无阻塞的提取出指定模式的key列表,但是需要去重

14、redis异步队列

一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

15、Redis如何实现延时队列

使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。

0

评论区