1.Redis为什么能这么快?
- Redis完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高
- 数据结构简单,对数据操作也简单
- 采用单线程结构,单线程也能处理高并发请求,想多核也可启动多实例;
- 使用多路I/O复用模型,非阻塞IO
2.Redis常用的数据类型
- String:最基本的数据类型,二进制安全
- Hash:String元素组成的字典,适合存储对象
- List:按照String元素插入顺序排序,先进后出。可以用于最新排行榜的场景
- Set:无序,不可重复。可以利用Redis提供对集合的差并交集用于共同关注,共同点的一些场景
- zset:有序,不可重复。可以用来进行排名
3.从海量Key里面查询出某一固定前缀的key?
SCAN cursor [MATCH pattern] [COUNT count]
基于游标的迭代器,需要基于上一次的游标延续之前那的迭代过程
以0作为游标开始一次新的迭代,知道命令返回游标0完成一次遍历
不保证每次执行都返回某个给定的数量的元素,支持模糊查询
一次返回的数量不可控,只能是大概率符合count参数
例如:scan 0 match k1* count 10 意思是游标从0开始,扫描并返回符合k1开头的key,每次返回10条
4.如何通过Redis实现分布式锁
分布式锁需要解决的问题
互斥性:任意时刻只能有一个客户端获取锁,不能有两个客户端获取锁
安全性:锁只能被持有该锁的客户端删除,不能被其他客户端删除
死锁:获取锁的客户端因为某些原因宕机而未释放锁,导致其他客户端再也无法获取到锁
容错:当Redis某些节点出现问题,客户端依然能够获取和释放锁
SET key value [EX seconds][PX millionseconds][NX|XX]
EX seconds: 设置键的过期时间为多少秒
PX millionseconds:设置键的过期时间为多少毫秒
NX:只在键不存在时,才对键进行设置操作
XX:只在键已经存在时,才对键进行设置操作
SET操作成功完成时,返回oK,否则返回nil
如下图
可以看到,我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time)
,这个set()方法一共有五个形参:
-
第一个为key,我们使用key来当锁,因为key是唯一的。
-
第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用
UUID.randomUUID().toString()
方法生成。 -
第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
-
第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
-
第五个为time,与第四个参数相呼应,代表key的过期时间。
总的来说,执行上面的set()方法就只会导致两种结果:1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。2. 已有锁存在,不做任何操作。
心细的童鞋就会发现了,我们的加锁代码满足我们可靠性里描述的三个条件。首先,set()加入了NX参数,可以保证如果已有key存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁。最后,因为我们将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。由于我们只考虑Redis单机部署的场景,所以容错性我们暂不考虑。
5.如何使用Redis做异步队列
使用List作为队列,RPUSH生产消息,LPOP消费消息
缺点1:没有等待队列里有值就直接消费
弥补:(1)可以通过在应用层引入Sleep机制去调用LPOP重试
(2)BLPOP key[key ...] timeout :阻塞知道队列有消息或超时
缺点2:只能供一个消费者消费
弥补:pub/sub主题订阅者模式凡是订阅了该频道的,一旦该频道发布信息都能收到,但有可能出现消息丢失,只能用更加专业的消息队列技术
subscribe topic 订阅
publish topic “value” 发布
6.Redis持久化
1.RDB(快照)持久化:保存某个时间点的全量数据的快照
缺点:内存数据的全量同步,数据量大会由于I/O而严重影响性能
可能会因为Redis挂点而丢失从当前至最近一次快照期间的数据
手动实现:BGSAVE指令:Fork出一个子进程来创建RDB文件,不阻塞服务器进程
自动触发:根据Redis.conf配置里的SAVE m n定时触发(用的是BGSAVE)
执行Debug Reload
执行Shutdown且没有开启AOF持久化
2.AOF持久化:保存写状态
特点:记录下除了查询以外的所有变更数据库状态的指令
以append的形式追加保存到AOF文件中(增量)
缺点:AOF文件大小不断增大,恢复数据时间长
3.RDB-AOF混合持久化方式
BGSAVE做镜像全量持久化,AOF做增量持久化
7.Redis数据的恢复
RDB和AOF文件共存情况下的恢复流程