Redis 常见面试题(一)

@[TOC]Redis 常见面试题

缓存穿透

说明

穿透指的是数据既不存在于缓存中,也不存在于数据库,那么对该数据的访问就会透过缓存,请求数据库直至压垮数据库,如用一个不存在的用户ID访问,可能就会被恶意利用频繁攻击。

解决

  1. 使用布隆过滤器存储所有的有效数据,这样查询就在外部被拦截不会到缓存层、数据库层进行访问了
  2. 查询的空结果仍然缓存起来,但是设置的过期时间比较短,不超过5分钟,这样也会带来无效缓存比较多的问题

击穿

说明

缓存中没有但是数据库中有的数据,一般是指缓存过期造成的。当大量请求读取缓存发现没有,就会转去读数据库,造成瞬间数据库压力爆表。

解决

  1. 设置热点数据永不过期
  2. 上层接口限流
  3. 加锁互斥,控制资源访问

雪崩

说明

缓存中的数据同时失效,造成查询压力到数据库层,与击穿不同的是,击穿查的是同一条数据,而雪崩指的是大量的数据。

解决

  1. 过期时间设置成随机的,使用固定时间+随机时间组合的方式
  2. 设置热点数据永不过期
  3. 将缓存数据分配到不同的分布式缓存服务器中

滑动窗口

本质上采用zset来实现。
比如限制用户的访问行为。
那么实现方式是以行为作为key,用户的uid或毫秒时间戳作为value,同时当前时间戳作为score值,用来指定窗口范围。这样当每次一个新的行为来时就维护一下时间窗口,剔除无效数据,
但是这种也有缺点,那就是窗口内的数据不能很大,比如限制10s内100万次的访问,那么这种方式就比较占内存了。

漏斗限流

漏斗的容量是有限的,剩余容量代表着可以继续进行操作的数量,漏斗的流速代表着系统允许行为的最大频率。
每次灌水时都会触发漏水机制,给漏斗留出空间。
Redis 4.0 提供了一个限流 Redis 模块,redis-cell,提供了原子粒度的限流:
cl.throttle testKey:test 50 30 60 1,即允许testKey的test行为,容量50,每60s最多30次访问,在限流指令中,如果被拒绝可以使用重试或者丢弃策略,如果使用重试机制,那么可以取该指令返回值数组中的第四个值进行sleep后重试。

新闻推荐

在阅读新闻时,系统是如何做过不重复推荐过去已阅读的新闻;
在邮件系统中,系统如何过滤掉垃圾邮件的。
可能尝试的方案是将所有数据全部存到数据库中,再判断是否存在的方式,但是这样方式对于空间占用就比较大了。这时就需要布隆过滤器了。
可以将Bloom Filter理解成一个不太精确的set,可以做到去重,但是不一定那精准(判断一个值是否存在,如果判断存在,实际上可能不存在;如果判断不存在,那么肯定是不存在的),可以通过参数控制精准度,这时就需要在空间和精准度上的权衡了。
指令:bf.add bf.madd bf.exists bf.mexists
每个布隆过滤器底层是使用位数组和几个不同的hash函数,在每次的add操作时,都是通过hash将值分配到位数组中的几个位置,然后置1,查询的时候也是同样的道理。

UV 统计

一天内同个访客多次访问仅计算一个UV
页面浏览量或点击量,用户每1次对网站中的每个网页访问均被记录1个PV
区别在于UV要实现去重的操作,比较简单的实现是通过set来实现去重统计,以页面作为key,每次访问将用户ID作为value塞到set中,最后计数即可,但是这种方案在用户访问量巨大时存储会非常庞大。
而HyperLogLog 提供不精确的去重计数方案,标准误差是 0.81%。
指令:pfadd pfcount
HyperLogLog需要占用12KB的存储空间,底层会根据实际数据大小选择使用稀疏矩阵存储还是稠密矩阵存储,当数据量少时使用稀疏矩阵,数据量大时再迁移到稠密矩阵。

签到统计

如要统计用户一段时间的签到记录,如果使用常规的key-value形式,那么占据的内存会越来越大,用户量一旦上来后存储更加不够。
可以使用位图的方式进行存储,本质上就是string的底层实现,byte数组。
指令:setbit bitcount bitpos bitfield

延迟队列

使用zset来实现,将延迟时间作为score来存储,任务体序列化后作为value,然后多个线程轮询(轮询时间一般是500ms或1s)该zset获取到期的value进行处理,多个线程防止单线程挂掉。
指令:zrangebyscore zrem
Zrangebyscore 返回有序集合中指定分数区间的成员列表

分布式锁

单节点锁:

一般使用setnx
set key value [EX seconds] [PX milliseconds] [NX|XX]
缺点:存在单点问题

Jedis

常规的使用setnx加上过期时间实现单机锁
缺点:锁不可重入

Redisson

原理:使用hash存储锁,key为锁名,field为随机字符串+线程ID,value为1,同一线程每次调用都是将value加1

多节点锁

单节点锁,虽然可以通过增加slave备份,但是redis的主备是异步的,在master将锁同步到slave之前就挂掉了,那么slave升到master后新生成的锁就会失去意义了。
当然也有解决方案:
设置key-value的绝对唯一,每次判断是否存在锁时同时判断key和value都是对应的才有效。
扩展来看,可以将单节点锁扩展到多节点上,即RedLock,思想是大多数节点上加锁成功才算取到锁。
RedLock可以看成是同步算法;即使进程间(多个电脑间)没有同步时钟,但是每个进程时间流速大致相同。

总结

  1. TTL时长 要大于正常业务执行的时间+获取所有redis服务消耗时间+时钟漂移
  2. 获取redis所有服务消耗时间要远小于TTL时间,并且获取成功的锁个数要 在总数的一般以上:N/2+1
  3. 尝试获取每个redis实例锁时的时间要远小于TTL时间
  4. 尝试获取所有锁失败后 重新尝试一定要有一定次数限制
  5. 在redis崩溃后(无论一个还是所有),要延迟TTL时间重启redis
  6. 在实现多redis节点时要结合单节点分布式锁算法共同实现

如何定位大Key

redis-cli -h 127.0.0.1 -p 7001 --bigkeys -i 0.1
每隔100条scan指令就会休眠0.1s,ops不会剧烈抬升,但是扫描时间会变长。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值