redis常见问题

参考

使用过Redis分布式锁么,它是怎么实现的?

redis的分布式锁主要使用set命令的nx选项抢占锁,以及ex设置过期时间,先nx获得锁并且设置过期时间,这两个过程是原子性的,中间不会被其他命令打断;
另外防止锁超时过期误删锁,一般使用set获得锁时会设置一个唯一值,释放锁的时候需要匹配一下两个值是否一致

什么是一致性哈希算法?什么是哈希槽?

https://www.jianshu.com/p/6ad87a1f070e

redis 的 custer 提供了两个功能:

  • 自动对数据分片,落到各个节点上
  • 即使集群部分节点失效或者连接不上,依然可以继续处理命令

普通哈希算法是hash(key)%number对数量进行取余获得key所在节点位置,如果改变数量会导致原有节点上的数据发生变化;

在一致性哈希算法中,整个哈希空间是一个虚拟圆环。 对节点取哈希值,然后分配到哈希环上。对某个值进行搜索的时候按顺时针搜索找到key第一个存在的节点,即使发生节点数据变化,也只是影响一小部分区间的数据;

一致性哈希算法对于容错性和扩展性有非常好的支持。但一致性哈希算法也有一个严重的问题,就是数据倾斜。

解决方案

  • 对每个真实节点增加多个虚拟节点,然后维护一个虚拟节点和真实节点的映射关系表;(即环上都是)https://geektutu.com/post/geecache-day4.html
  • rredis 集群(cluster)并没有选用上面一致性哈希,而是采用了哈希槽(slot)的这种概念。每个redis节点会维护一部分槽以及槽上的数据,总的槽大概一万六千多,槽可以均匀分布在多个节点
1
2
3
4
5
6
docker run --rm --net=host --name redis-manager  \
-e DATASOURCE_DATABASE='redis_manager' \
-e DATASOURCE_URL='jdbc:mysql://127.0.0.1:3306/redis_manager?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8' \
-e DATASOURCE_USERNAME='root' \
-e DATASOURCE_PASSWORD='123456' \
reasonduan/redis-manager

https://www.fengpt.cn/archives/redis%E9%9B%86%E7%BE%A4%E5%9C%A8%E6%9F%A5%E6%89%BEkey%E7%9A%84%E6%97%B6%E5%80%99%E6%98%AF%E5%A6%82%E4%BD%95%E8%B7%AF%E7%94%B1%E7%9A%84

redis集群哈希槽

redis cluster 包含了16384个哈希槽,每个 key 通过计算后都会落在具体一个槽位上,而这个槽位是属于哪个存储节点的,则由用户自己定义分配。例如机器硬盘小的,可以分配少一点槽位,硬盘大的可以分配多一点。如果节点硬盘都差不多则可以平均分配。所以哈希槽这种概念很好地解决了一致性哈希的弊端。

在容错性和扩展性上,redis对槽位的转移,把故障节点负责的槽位转移到其他正常的节点上。

但一定要注意的是,对于槽位的转移和分派,redis 集群是不会自动进行的,而是需要人工配置的。所以 redis 集群的高可用是依赖于节点的主从复制与主从间的自动故障转移。

使用过Redis做异步队列么,你是怎么用的?有什么缺点?

可以是使用blpoprpushblpop会移除列表头部元素,如果列表没有元素会阻塞直到等待超时或发现元素为止,rpush从尾部写入数据;缺点如下:

  • 消息容易丢失
  • 消息分发策略没有专业的mq丰富,例如kafka的分区,rabbitmq的路由键等等

什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?

缓存穿透

一般缓存系统会先查缓存,值不存在的时候再查数据库;一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。如何避免?

  • 对查询结果为空的情况也进行缓存,缓存时间设置短一点
  • 对一定不存在的key进行过滤,可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤(布尔过滤器)

    缓存雪崩

    当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。如何避免?
  • 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量
  • 不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

Redis的字典是如何实现的?简述渐进式rehash的过程?

https://www.jianshu.com/p/e2697fecac0d
https://www.cnblogs.com/neooelric/p/9621736.html

Redis事务是怎么样的?

redis的事务是通过mutil实现的,mutil开始一个事务,然后将多个命令入队到事务中,最后执行exec命令触发事务。redis的事务有一些不足:

  • 事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做
  • 同的key是有可能分配在不同的Redis节点上的,在这种情况下Redis的事务机制是不生效的

集群可以使用lua脚本吗?

不可以,除非key在同一个redis节点上,可以使用{hash_tag}来是不同的key落到同一个节点上

1
2
3
4
5
6
> CLUSTER KEYSLOT somekey
11058
> CLUSTER KEYSLOT foo{hash_tag}
(integer) 2515
> CLUSTER KEYSLOT bar{hash_tag}
(integer) 2515

keySlot算法中,如果key包含{},就会使用第一个{}内部的字符串作为hash key,这样就可以保证拥有同样{}内部字符串的key就会拥有相同slot

Redis的多数据库机制,了解多少?

单机版有16个数据库,每个数据库的数据相互隔离,集群版只有一个数据库

谈谈你对分布式和集群,微服务的理解?两则之间有什么关系?

  • 分布式是将一个业务拆分多个子业务,多个子业务可以部署不同机器上面,子业务之间通过rpc或消息中间件或其他方式进行通信,如果有一个子业务不可用,那么整个业务就不可用
  • 微服务是将一个业务拆分多个子业务,多个子业务可以部署在不同机器或统一机器上面(和分布式的区别)。
  • 集群是同一个业务部署到多个机器上面,比如用nginx做负载均衡。
  • 分布式下每个子业务都可以做集群,分布式和微服务类似,只是部署方式不一样

redis集群有什么限制

  • 不能批量处理key
  • 不支持事务,多数据库
  • 一些大的键值对象不能映射到不同的节点上

假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

  • 使用keys 指定模式可以获得key列表
  • 因为redis是单线程模式,使用keys命令会导致线程阻塞一段时间,线上服务会停顿,直到命令执行完毕才能恢复
  • 可以用scan命令,无阻塞的,但是会有一定的重复概率

bgsave的原理是什么?

fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。

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

可以将多次IO往返的时间缩减为一次

Redis的同步机制了解么?

  • 第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。
  • 加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

是否使用过Redis集群,集群的原理是什么?

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