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文件共存情况下的恢复流程