1.Redis简介
主流的应用架构通常会在客户端与存储层之间添加一个缓存层,当缓存层没有要查询的数据时,缓存层可实现对存储层的穿透查询,穿透查询返回数据后,会写到缓存层中。
熔断机制:当我们发现存储层挂掉或者没办法提供服务的时候,可以让客户端的请求直接打在缓存层上,不管存储层有没有获取到数据都直接返回,这样就可以在有损的情况下提供服务。
多路I/O复用模型
传统的I/O阻塞模型:当使用read/write对某一个fd(文件描述符)对文件进行读写时,如果当前fd不可读或者不可写,整个redis服务就不会对该其他fd上的操作做出响应,导致整个服务不可用。由于阻塞模型会阻塞其他fd上的操作,当有多个客户端任务时,往往不会使用IO阻塞模型。
I/O多路复用模型:select方法能够同时监控多个fd的可读可写的情况,返回可读可写的fd的个数,监听的任务交给select后,我们的程序就可以去做别的事情而不会被阻塞了。
采用react模式来实现文件事件处理器,文件事件处理器使用IO多路复用模型,同时监听多个fd,当accept,read,write和close文件事件产生时,文件事件处理器就会回调fd绑定的事件处理器。虽然整个文件处理器是在单线程上运行的,但是通过IO多路复用模块的引用,实现了同时对多个fd读写的监控,提高了网络通信模型的性能,同时也可以保证redis服务的简单。
2.Redis常用的数据类型
3.从海量keys中查询出某一固定前缀的key
面试常问的问题:假设redis中有1亿个key,其中有10万个key是以某一固定的已知的前缀开头的,如何将他们全部找出来???
回答要点:摸清数据规模
1.keys命令
用法:KEYS pattern:查找所有符合给定模式pattern的key
例子:在规模2千万的keys中查找以k1为前缀的key
结果:由于keys命令一次性返回所有满足条件的key,导致客户端被卡住了,也就是说,当redis中数据过多时,对于内存的消耗和redis服务器都是一个隐患。
2.SCAN命令:可以无阻塞的提取出指定模式的key列表,scan每次执行都只会返回少量元素,适用于生产环境。
命令以0最为迭代开始时的游标,返回的游标为0时,表示命令的结束。可使用COUNT指定希望返回的记录的数量。
例子
返回:1):表示当前迭代到了哪里,2):满足条件的key的结果集
根据上一轮迭代返回的游标进行下一轮迭代
4.如何通过Redis实现分布式锁
分布式锁:控制分布式系统共同访问共享资源的一种锁的实现。
需要解决的问题:
- 互斥性:任意时刻只能有一个客户端获取锁
- 安全性:锁只能由持有该锁的客户端删除,不能由其他客户端删除
- 死锁:获取锁的客户端因为某些原因发生宕机,其他客户端再也无法获取到该锁的情况需要避免
- 容错:当部分节点宕机时,保证客户端依旧能够获取到锁
1.SETNX命令:
初期时SETNX被用来实现分布式锁原理:在执行某段代码逻辑之前,先使用SETNX对某个key设置value,如果设置成功,则证明没有其他线程正在执行该段代码,这时,我们的线程就可以顺利的执行该段代码逻辑,如果设置失败,则证明此时有别的线程正在占用该资源,那么当前线程就要等待直至SETNX执行成功。为了让其他线程能够获取到该资源,需要在离开该段代码时,使用EXPIRE命令给key设置一个生存时间,key过期时,会被自动删除。
缺点:当前线程在SETNX命令和EXPIRE命令之间挂掉时,其他线程永远也无法获取到该key
2.SET命令:结合了SETNX和EXPIRE的原子性操作
面试常见问题:当有大量的key同时到达过期时间需要清除时需要注意什么。
5.如何通过Redis实现异步队列
- lpush:向列表首部插入元素
- rpush:向列表尾部插入元素
- lpop:从列表尾部弹出元素
例子:
改进:BLPOP:当list中没有元素时,会阻塞直到list中有元素或者超时。
例子:设置blpop的阻塞时间为30秒,当前list中没有元素,所以blpop会一直阻塞
生产者向list中添加元素
缺点:这样的做法,生产出来的数据只能让某一个消费者消费。
面试常见问题:如何实现只生产一次,让多个消费者消费。
pub/sub命令:主题订阅者模式
- 发送者(pub)发送消息,订阅者(sub)接受消息
- 订阅者(sub)可以订阅任意数量的频道
生产者向某个频道发送消息,该消息会被发送给订阅该频道的三个消费者。
1.在客户端1,2订阅myTopic频道(不需要提前创建)
2.在客户端3中向myTopic频道发送消息
3.监听myTopic频道的客户端1,2都收到了客户端3发送的消息
缺点:消息的发布是无状态的,即发布完消息后无法保证该消息是否被订阅者接受到。对于发布者来说,消息是即发即失的,此时,如果某个消费者在生产者发送消息时下线,重新上线时是无法到接受消息的。
6.持久化方式之RDB
BGSAVE原理
写时复制:当父进程创建子进程时,内核只为子进程创建虚拟空间,父子两个进程使用相同的物理空间,只有父子进程发生更改时,才会为子进程分配独立的物理空间。
7.持久化方式之AOF以及混合模式
RDB-AOF混合持久化方式
在此种方式下,子进程在做AOF重写时,会通过管道从父进程去读取增量数据并缓存下来;在以RDB格式保存全量数据时,也会从管道中读取数据,不会造成管道的阻塞。也就是说,AOP文件的前半段是RDB格式的全量数据,而后半段是Redis命令格式的增量数据
8.Pipeline及主从同步
正常情况下:客户端发送一个命令需要等待redis服务器端的应答,服务器端接收到命令就会去处理相关请求。如果同时需要执行大量的命令,需要等待上一条命令应答后才能执行后面的命令,不仅多了rtt时间,还频繁调用系统io发送网络请求。
使用Pipeline:允许客户端可以一次发送多条命令,而不需要等待上一条命令执行的结果。客户端首先将命令写入到缓冲区中,最后再一次性的发送给redis,可以将多次io往返时间缩减为一次(命令没有依赖的情况下)。
9.Redis集群
如果一台服务器不可用,则受影响的数据仅是沿着该节点与逆时针行走的下一个节点之间的数据,这些数据将会存储到该节点顺时针方向最近的节点上,其他的数据不会受到影响。
如果在系统中新增节点,受影响的数据仅是沿着该新增节点与逆时针行走的下一个节点之间的数据,这些数据需要迁移到新增节点上。
数据倾斜问题:一致性哈希算法在服务器很少的情况下,容易因为数据分布不均匀而造成的数据倾斜,数据倾斜指的是被缓存的对象大部分集中缓存在某一台服务器上。
数据倾斜的解决:引入虚拟节点,即对每一个服务器节点计算多个hash,每个计算结果位置都放置一个此服务器节点(虚拟节点),具体做法:可以在服务器ip或者主机名后面增加编号来实现