redis 設置退出後清空 如何退出redis_redis订阅怎么退出


  • 为什么要 Redis ?

传统的关系型数据库如 MySQL 已经不能适用所有的场景了,比如秒杀的库存扣减,App 首页的访问流量高峰等等,都很容易把数据库打崩,所以引入了缓存中间件,目前市面上比较常用的缓存中间件有 Redis 和 Memcached 。

  • Redis 有哪些数据结构?

字符串 String、字典 Hash、列表 List、集合 Set、有序集合 SortedSet。
考虑一个 Key-Value 结构,
String 是 Redis 中最简单的数据类型。在 Key-Value 中,一般所说的数据类型就是 value 的类型,有很多种。而 Key 类型只有一种,就是字符串类型。
数据类型可以是一个列表,List 的底层是数组,元素可以重复。内部使用双向链表实现,所以获取越接近两端的元素速度越快,通过索引访问时会比较慢
Redis 本身可以看作一个大 Hash,其字符串类型的键关联到字符串或者链表之类的数据对象。而 Redis 中的数据类型也可以再次使用 Hash,其字段和值必须是字符串类型。
集合 Set 类型值具有唯一性,常用操作是向集合添加、删除、判断某个值是否存在,集合内部是使用值为空的散列表实现的。
有序集合和集合一样也是 String类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。通过哈希表实现。
HyperLogLog 用于计算独立元素个数,原理是计算其存在到底有多稀有的数据。
Geo 在Redis3.2版本提供,支持存储地理位置信息,提供计算某个半径内的所有节点的 API ,用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能。数据类型为 zset。
Pub/Sub 实现订阅功能。
BloomFilter Redis中布隆过滤器的数据结构就是一个很大的位数组和几个不一样的无偏哈希函数(能把元素的哈希值算得比较平均,能让元素被哈希到位数组中的位置比较随机)。向布隆过滤器中添加元素时,会使用多个无偏哈希函数对元素进行哈希,算出一个整数索引值,然后对位数组长度进行取模运算得到一个位置,每个无偏哈希函数都会得到一个不同的位置。再把位数组的这几个位置都设置为1,这就完成了 bf.add命令的操作。正常的 HashMap 使用的是一个哈希函数加开链法。这个使用的是多个hash函数加不解决hash冲突。BloomFilter 可以 解决缓存穿透的问题。一般情况下,先查询缓存是否有该条数据,缓存中没有时,再查询数据库。当数据库也不存在该条数据时,退出。这样导致每次查询都要访问数据库,这就是缓存穿透。缓存穿透带来的问题是,当有大量请求查询数据库不存在的数据时,就会给数据库带来压力,甚至会拖垮数据库。
可以使用布隆过滤器解决缓存穿透的问题,把已存在数据的 key 存在布隆过滤器中。当有新的请求时,先到布隆过滤器中查询是否存在,如果不存在该条数据直接返回;如果存在该条数据再查询缓存查询数据库。

  • 如果有大量的key需要设置同一时间过期,一般需要注意什么?

Redis 毕竟是一个缓存,存在缓存过期的问题。同时过期,严重的话会出现缓存雪崩,我们一般需要在时间上加一个随机值,使得过期时间分散一些。

  • keys 指令有什么特殊之处?

Redis 的单线程特性。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复,有可能 Redis 会被识别为宕机。这个时候可以使用 scan指令, scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。

  • 使用过Redis做异步队列么,你是怎么用的?怎么实现延时队列?

一般使用list结构作为队列, rpush生产消息, lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
如果不用 sleep,list还有个指令叫 blpop,在没有消息的时候,它会阻塞住直到消息到来。
还可以使用pub/sub主题订阅者模式,可以实现 1:N 的消息队列。但是在消费者下线的情况下,生产的消息会丢失,这个时候需要专业的消息队列介入。
延时队列可以使用 sortedset,拿时间戳作为 score,消息内容作为 key 。消费者用 zrangebyscore指令获取N秒之前的数据轮询进行处理。

  • Redis是怎么持久化的?服务主从数据怎么交互的?如果突然机器掉电会怎样?RDB的原理是什么?

RDB (Redis DataBase)和 AOF (Append Only File)
RDB做镜像全量持久化,AOF做增量持久化。因为RDB会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要AOF来配合使用。在redis实例重启时,会使用RDB持久化文件重新构建内存,再使用AOF重放近期的操作指令来实现完整恢复重启之前的状态。
这里很好理解,把RDB理解为一整个表全量的数据,AOF理解为每次操作的日志就好了,服务器重启的时候先把表的数据全部搞进去,但是他可能不完整,你再回放一下日志,数据不就完整了嘛。不过Redis本身的机制是 AOF持久化开启且存在AOF文件时,优先加载AOF文件;AOF关闭或者AOF文件不存在时,加载RDB文件。
取决于AOF日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。
RDB 原理是 fork和cow。fork是指redis通过创建子进程来进行RDB操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。

  • Pipeline有什么好处,为什么要用pipeline?

Pipline一次打包多个命令执行。
可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有 因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

  • Redis的同步机制了解么?

Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将RDB文件全量同步到复制节点,复制节点接受完成后将RDB镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。后续的增量数据通过AOF日志同步即可,有点类似数据库的binlog。

  • 是否使用过Redis集群,集群的高可用怎么保证,集群的原理是什么?

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

  • Redis雪崩?怎么解决?缓存穿透和击穿?

Redis 的 key 有时效性。
假设缓存当时所有的Key都失效了。此时 1 秒 6000 个请求全部落数据库,数据库必然扛不住,它会报一下警,DBA都没反应过来就直接挂了。此时 DBA 重启数据库,但是数据库立马又被新的流量给打死了。这就是理解的缓存雪崩。如果打挂的是一个用户服务的库,那其他依赖他的库所有的接口几乎都会报错,如果没做熔断等策略基本上就是瞬间挂一片。
在批量往 Redis存数据的时候,把每个Key的失效时间都加个随机值。
如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效
或者设置热点数据永远不过期,有更新操作再同步更新缓存。
缓存穿透——缓存和 DB 都没有这个数据,请求直接穿透所有层级直击 DB 。
缓存击穿——缓存有这个数据,并且被大量访问。可是这个key突然失效了。这个请求如子弹一样突然击中 DB。


  • redis和zk做分布式锁的对比

setnx(set if not exist)、 setex(set expire value)
分布式锁的核心是在在分布式环境中找到同一个节点操作同一个数据。
一个可靠的、高可用的分布式锁需要满足以下几点
互斥性:任意时刻只能有一个客户端拥有锁,不能被多个客户端获取
安全性:锁只能被持有该锁的客户端删除,不能被其它客户端删除
死锁:获取锁的客户端因为某些原因而宕机,而未能释放锁,其它客户端也就无法获取该锁,需要有机制来避免该类问题的发生
高可用:当部分节点宕机,客户端仍能获取锁或者释放锁
Redis 支持 lua 脚本
redis分布式锁 官方叫做RedLock算法,是redis官方支持的分布式锁算法。
这个分布式锁有3个重要的考量点,互斥(只能有一个客户端获取锁),不能死锁,容错(大部分redis节点或者这个锁就可以加可以释放)。
最普通的实现方式,就是在redis里创建一个key,这就算加了分布式锁。
SET my:lock 随机值 NX PX 30000
这个命令就ok,NX的意思就是只有key不存在的时候才会设置成功,PX 30000的意思是30秒后锁自动释放。 别人创建的时候如果发现已经有了就不能加锁了。
释放锁就是删除key,但是一般可以用lua脚本删除,判断value一样才删除:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
为啥要用随机值呢?因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完。此时可能已经自动释放锁了,也就是你创建的这个key已经被删除了。此时可能别的客户端已经获取到了这个锁,也就是他创建了另外一个同名的key,要是你这个时候直接删除key的话,相当于你释放了别人的key,所以得用随机值加上面的lua脚本来释放锁。 但是这样是肯定不行的。
如果是普通的redis单实例,单点故障时分布式锁失效。或者是redis普通主从,那redis主从异步复制,如果主节点挂了,key还没同步到从节点,此时从节点切换为主节点,别人就会拿到锁。
这事的本质就是所有的节点都依赖的是一个统一位置的key,单实例或者主从复制都意味着要强依赖一个实例的稳定性。
解决办法,RedLock算法
这个场景是假设有一个redis cluster,有5个redis master实例。然后执行如下步骤获取一把锁:
1)获取当前时间戳,单位是毫秒;
2)跟上面类似,轮流尝试在每个master节点上创建锁,过期时间较短,一般就几十毫秒;
3)尝试在大多数节点上建立一个锁,比如5个节点就要求是3个节点(n / 2 +1);
4)客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了;
5)要是锁建立失败了,那么就依次删除这个锁;
6)只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁。