Redis面试题

1.Redis为什么快

1.基于内存存储

2.高效的数据结构

3.IO多路复用模型

4.单线程

IO多路复用

应用程序从磁盘中读取数据,经过了两个阶段,第一个阶段,使用recvform命令,尝试从内核中加载数据,如果没有数据,那么内核操作硬件拿到数据,这个过程需要等待;第二个阶段,内核把数据加载之后再写给用户的缓存区

如果是阻塞IO,那么从用户发起请求到读取数据,都会处于阻塞状态。

Redis面试题_第1张图片

阻塞IO

非阻塞IO,在使用recvfrom加载数据时候,会立即返回结果而不会阻塞用户进程,如果没有加载到数据会继续发起请求。但是这种方式会发起多次recvfrom命令,造成cpu空转。

IO多路复用,当用户读取数据,不会去调用recvfrom函数,而是调用epoll函数,这个函数会将需要监听的数据交给内核,由内核检查这些数据是否就绪,如果数据就绪了,会通知应用程序来读取数据。这样可以避免发起多次recvfrom命令,降低CPU的压力。

单线程为什么快

而Redis是基于内存操作,执行速度很快,性能瓶颈在于网络延迟而不是执行速度,因此多线程不会带来巨大的性能提升。

而且在多线程的情况下会导致频繁的上下文切换,带来不必要的开销;

同时引入多线程会带来线程安全问题,就必须会使用锁机制来保证安全,这样实现复杂度变高,同时性能也会打折扣。

2.Redis过期Key的删除策略

1.周期删除

设置定时任务,周期性的抽取部分过期的key,然后删除

2.惰性删除

即并不是该key到期了就删除,而是该key过期后,redis查询该key发现过期了,就会把他删除掉

3.Redis内存回收策略

noevction:不淘汰,内存满的时候不允许加入新数据

volatile-ttl:比较key剩余的时间,越短时间就越先删除

allkeys-random:从所有key中随机删除

volatile-random:从设置了过期时间的key中随机删除

allkeys-lru:删除最近最少使用的key

volatile-lru:从设置过期时间的key中,删除最近最少使用的key

allkeys-lfu:删除最少使用的key

volatile-lru:从设置过期时间的key中,删除最少使用的key

3.RDB

Redis进行数据快照,保存到磁盘中(触发条件:save,bgsave,停机,触发rdb条件)

RDB步骤

fork主进程得到一个子进程,共享内存空间

子进程读取内存数据并写入新的RDB文件

用新RDB文件替换旧的RDB文件

4.AOF

Redis处理的每一个写命令都会记录在AOF文件

优化:进行命令重写,比如set key name1,set key name2可以直接合并成set key name2

5.RDB和AOF对比

Redis面试题_第2张图片

6.全量同步

主从节点第一次连接时,从节点无数据,主节点有数据,因此需要执行全量同步,将主节点的数据拷贝到从节点

Replication Id:是数据集的id,每个主节点都有一个Replication Id.

offset:偏移量,用来记录数据同步到了哪里

而第一次同步时,从节点由于是从主节点变过来的因此它的Replication Id和offset和主节点不一致没有,因此可以判断是否是主从第一次连接,第一次连接之后,从节点就得到的Replication Id和offset

完整流程:

slave节点请求增量同步

master节点判断replid,发现不一致,拒绝增量同步

master将完整内存数据生成RDB,发送RDB到slave

slave清空本地数据,加载master的RDB

master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave

slave执行接收到的命令,保持与master之间的同步

7.增量同步

通过repl_baklog文件,repl_baklog文件用于记录redis操作日志和主节点的offset和从节点的offset,主从节点offset的差异,就是需要进行增量同步的部分。

如果出现了网络阻塞,导致主节点的offset超过了从节点的offset,那么从节点尚未复制的数据就会丢失,此时会执行全量同步。

8.Redis的key设计

格式:业务名:数据名:id;不超过44字节;不包含特殊字符;

优点:可读性强;避免key冲突;方便管理;节省内存;

9.Redis大key问题

大key:key占用内存大;key的成员多;key的成员内存大;

危害:网络阻塞;数据倾斜;Redis阻塞;CPU压力

10.缓存雪崩

缓存雪崩是指redis中大量的key在同一时间失效,导致所有的请求都打到数据库中,给数据库造成压力。

解决方案:

1.缓存永不过期,同时利用定时任务在活动结束后删除

2.设置随机过期时间,这里有两种方式,一种是直接生成一个固定时间;另一种是固定时间再加上一个随机时间,但是由于缓存的基数比较大,因此随机过期时间也有可能撞在一起,因此可以结合其他的方式,如分布式锁进行兜底。

10.缓存穿透

缓存穿透是指用户请求一个不存在的数据时,由于redis和数据库都没有数据,因此请求直接打到数据库,给数据库造成压力。

解决方案:

1.缓存空对象:当请求查询redis为空时并且数据库也没有查到时,将该key设置成一个空值存入到redis中,之后访问该key时,发现存在空值,则立刻返回;否则查询数据库,数据库不为空,返回该数据并存入redis中。

2.使用布隆过滤器:将数据库中存量数据存放到布隆过滤器中,如果缓存中不存在数据,则继续查询布隆过滤器,如果布隆过滤器发现不存在,则该数据一定不存在,直接返回。如果存在,则继续查询数据库返回该数据。

缺陷:布隆过滤器存在小概率的误判问题,即布隆过滤器判断存在,但是不一定真的存在该数据。因此攻击者可能利用该缺陷发起恶意攻击,导致大量的请求打到数据库中。针对这个问题可以使用

3.使用锁,当该请求发现缓存不存在时,使用锁机制来实现避免多个相同的请求同时访问数据库,只让一个请求去加载数据,然后将该数据存入redis中。

组合方案:

1.组合缓存空对象方案,针对该攻击者请求的key,缓存空对象到redis中;

2.使用锁机制进行兜底,如果发现布隆过滤器存在之后,使用分布式锁,避免多个相同请求同时访问数据库,只让一个请求访问数据库。

11.缓存击穿

缓存击穿问题是指某一个高并发访问的key失效,导致大量的请求打到数据库中,给数据库造成压力。

1.分布式锁,大量的请求访问该key时,发现redis中不存在,然后获取分布式锁,通过分布式锁限制只让一个请求访问数据库,查到数据后将该数据返回并写入redis中。同时我们使用双重判定锁的方式,让线程获取锁之后再次查询redis,如果缓存中存在该数据了(意味着前一个线程完成了缓存重建),然后就直接返回该数据,不用继续查询数据库,这样可以降低数据库的访问压力。

2.热点数据预加载,在活动开始前,将已知的热点数据写入缓存中,避免海量请求在第一次访问热点数据时都从数据库读取,降低响应时间,降低数据库的压力。

2.不设置过期(或者设置逻辑过期时间)方案,将热点数据不设置过期时间,等热点数据对应的业务活动结束,然后通过后台任务设置过期时间,自动删除。

12.缓存与数据库的一致性问题

缓存和数据库的一致性问题是指在使用缓存的情况下,如果保证缓存和数据库的数据一致。

通常有这么几种方案:

1.缓存双删

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

2.先更新数据库再删除缓存

指用于发起写请求时,首先访问数据库,然后删除相应的缓存,当下一个请求读请求到达时候,发现缓存中没有该数据,因此访问数据库读取到最新的数据,然后将该数据写入缓存中。

但是当缓存过期时,并且读请求的写入Redis的执行时间在写请求更新Redis之后。

3.Binlog异步更新缓存

首先更新数据库的数据,数据库会将数据表数据的变更信息写入binlog日志中,监听到日志文件的变化后,把数据库变更信息发送到消息队列中,程序接收到消息队列中的数据,对缓存做删除。如果删除失败了,程序就把数据再次发送到消息队列中,再做一次删除,实现删除失败后的重试。这种方案还有一种好处就是不会对业务代码造成过多的侵入,我们可以专门起一条协程来监听消息队列,如果收到消息队列中的数据,直接去删除对应的缓存即可,而不必在业务代码中去写。

Redis面试题_第3张图片

先更新缓存再更新数据库有什么问题?

先更新数据库再更新缓存有什么问题?

先删除缓存再更新数据库有什么问题?

13.什么是大key

大key问题主要可以分成这样3种情况,第一种,key本身数据量比较大,如String类型的key,值为5M;第二种,该key的成员数量比较多;第三种,该key的成员占用的空间比较大;

而对于占用多大空间可以算做大key,我觉得这是一个比较主观的问题,它会基于应用程序的需求,硬件的配置,Redis实例的内存大小,通常情况下,String 类型的key存储内容不超过5M,List Set Zset等类型的key成员不超过20000,Hash类型key不超过20000,同时val不应过大。

大Key带来的问题

14.什么是热key

## todo

摘自黑马程序员

你可能感兴趣的:(Redis,redis)