天天看点

Redis基本数据结构以及雪崩、穿透、击穿等

Redis有哪些数据结构?

字符串Sting 、 字典Hash、 列表List、 集合Set、 有序集合SortedSet。此外还有HyperLogLog、 Geo、 Publ/Sub

Redis分布式锁

先拿setnx争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记释放。

如果在setnx之后执行expire之前进程意外crash或者要重启那会怎么样?

set指令可以同时把setnx和expire合成一条指令来用

如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

redis是单线程的,keys指令会导致线程阻塞一段时间,线上的服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复率,整体花费的时间比直接使用keys指令长。

scan命令

SCAN命令的有SCAN,SSCAN,HSCAN,ZSCAN。

SCAN的话就是遍历所有的keys

其他的SCAN命令的话是SCAN选中的集合。

SCAN命令是增量的循环,每次调用只会返回一小部分的元素。所以不会有KEYS命令的坑。

SCAN命令返回的是一个游标,从0开始遍历,到0结束遍历。

scan命令就是对这个一维数组进行遍历。每次返回的游标值也都是这个数组的索引。limit参数表示遍历多少个数组的元素,将这些元素下挂接的符合条件的结果都返回。因为每个元素下挂接的链表大小不同,所以每次返回的结果数量也就不同

Redis做异步队列

使用redis做异步队列,正常使用list结构作为队列,rpush生产消息,lpop消费消息,当lpop没有消息的时候,要适当sleep一会再重试。在不采用sleep时候,list还有指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。生产的任务使用pub/sub主题订阅者模式,就可以实现1:N的消息队列

redis如何实现延时队列?

使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理

redis持久化的几种方式

redis提供两种方式进行持久化,一种是RDB持久化(原理是将redis在内存中的数据库记录定时dump到磁盘上的RDB持久化),另外一种是AOF持久化(原理是将redis的操作日志以追加的方式写入文件)

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,导入成功后,再替换之前的文件,用二进制压缩存储。

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

比较:

1、aof文件比rdb更新频率高,优先使用aof还原数据。

2、aof比rdb更安全也更大

3、rdb性能比aof好

4、如果两个都配了优先加载AOF

当突然机器掉电,数据是否完整?如果不完整会丢失多少数据

丢失的数据取决于AOF日志sync属性的配置,如果不要求性能,在每条写指令都sync一下磁盘,就不会丢失数据,但在高性能的要求下,一般都是定时,最多会丢失1s数据

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

缓存穿透:一般的缓存系统按照key去缓存查询,如果不存在key对应的value,就会去数据库查询,当有一些恶意的请求故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。

如何避免? 1、对查询为空的情况也进行缓存,缓存时间设置短一点,或者key对应的数据insert之后清理内存 2、对一定不存在的key进行过滤,可以使用布隆过滤器,将在查询范围外的key进行过滤

缓存雪崩:当服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候会给后端带来很大的压力,导致系统崩溃。

如何避免? 1、在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如某个key只允许一个线程查询数据和写缓存,其他线程等待。 2、做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1失效时间设置短期,A2设置为长期 3、不同的key,设置不同的过期时间,让缓存失效时间点尽量均匀。

缓存击穿: 缓存击穿指的是缓存中没有,但是数据库存在的数据,当这个热点数据到期,该热点数据请求的用户并发量特别大,导致都穿过redis直接请求数据库,造成数据库压力增大。

如何避免? 1、设置热点数据永不过期 2、接口限流。在重要的接口我们都必须做好限流,防止恶意用户刷接口。3、使用互斥锁,常用的做法使用mutex。就是在缓存失效的时候,不立即去请求数据库,而是使用缓存工具的某些成功操作返回值的操作,比如redis的setnx 去set一个mutex key,当操作返回成功时候,再进行load db的操作回设缓存。

继续阅读