redis 初级面试题笔记
一. redis 的数据类型
string
string 可以代表万事万物 想怎么存就怎么存 不管是二进制序列化 还是 JSON 都很合适
list
list 可以当队列用 也可以当栈用
set
适合随机场景 如抽奖
hash
适合存部分字段经常更新的对象 每个属性对应 hash 里的 1 个 key
zset
适合做排名
其他高级类型
bitmaps(位图)
位图使用场景很多 但是需要二进制位运算的基础知识
比如统计每天哪些 ID 的用户登录过系统 每个 ID 对应 1 个位 如果登录过就把图上的 0 变成 1
hyperloglog(基数统计)
也适合做统计,比如统计每天的在线用户数量,和 bitmaps 的区别在于 bitmaps 占用空间大 但是能明确知道哪些用户登录过 hyperloglog 只能统计总人数 而且有误差 统计日活一般可以无视这点误差
geo(地图坐标计算)
可以计算当前点到另一个点的距离 也可以计算当前点的多少范围内有多少点
其他功能
pub/sub(发布订阅)
- 不是很重要的消息可以通过发布订阅来实现
- websocket 转发消息 用户张三和 A 服务器有长连接 但是他要给李四发消息 李四连的服务器是 B 可以通过发布订阅来转发 由于没有消息可靠机制 所以只适合不是特别重要的聊天场景 如培训机构官网的在线客服 数据丢了关系不是很大
pipeline(管道)
可以给管道添加多个指令 一次性发出并执行 但是如果中间某个指令报错 不会回滚 也不会停止 剩下的命令仍然执行
watch(事务监控 key)
配合管道使用 先监控某个 key 然后给管道添加命令 最后执行管道命令 在执行管道的命令之前 监控的 key 有改变 管道中的命令全部取消
二. 缓存穿透
恶意攻击 数据库根本没有该记录 比如 http://xiaokedamowang.cn?phoneId=123
假设这是查询手机的参数 但是我数据库里没有 ID 为 123 的手机
- 缓存 null 作用不大
- 布隆过滤器
三. 缓存击穿
瞬时流量打进来,没有缓存该key 或者 该key失效
- 上锁 并且 双重检查
// 伪代码 Object obj = redis.get(id); if(obj == null){ redis.lock() obj = redis.get(id); if(obj == null){ obj = sql.getById(id); redis.set(id,obj); } redis.ulock(); } return obj;
四. 缓存雪崩
大量 key 失效 导致大量的 三. 缓存击穿 发生
- 随机 key 过期时间
- 重复 三. 缓存击穿 的解决方案
五. 数据库和缓存不一致问题
又称缓存双写不一致问题
- 不处理 或者 减少 key 的过期时间 主要看业务对于数据不一致的容忍度
- 网上有延迟双删策略(只能减少概率 不能杜绝)
- 上锁 如读写锁 或者读不上锁 写的时候上锁
- 阿里 canal
六. 主从不一致问题
- 配置同步因子 趋向于 强一致性
- wait
七. redis 持久化方式和原理
- RDB
RDB 是数据快照,优点是体积小,备份和恢复性能快,缺点是 redis 宕机的时候,丢失的数据多
RDB 是在指定时间间隔内讲内存数据快照写入磁盘, 实际操作是通过 fork 子进程,先写入临时文件 再替换原来的文件
- AOF
AOF 记录的是指令,是追加模式,redis 宕机丢失数据少,支持多种策略 AOF 文件过大的时候会命令压缩合并重写
八. redis 过期删除方式及内存不足的策略
过期 key 删除方式
定时清理
默认情况下 每秒进行 10 次任务 总共执行 250 毫秒 随机检查 100 个 key 删除已过期的 key 如果过期的 key 大于 25% 重复操作
查询该 key 的时候判断是否过期 过期就删除
内存不足的删除策略
- 不删除 插入报错
- 全局选择性移除
- 随机删除
- LRU 最长时间没被使用的 key
- LFU 使用频率最少的 key
- 设置过期时间选择性移除
- 随机删除
- 最接近过期的 key
- LRU 最长时间没被使用的 key
- LFU 使用频率最少的 key
九. 使用 redis 当分布式锁有哪些问题 怎么解决
- 运行时间 超过过期时间 会导致锁失效
开线程循环续费 俗称看门狗
- 追问: 如果超长时间的 STW 卡住了 锁还是超时了 会怎么样
STW结束之后 当前线程会删除别的线程抢到的锁 所以应该上锁的时候设置1个UUID 删除的时候如果不是自己UUID的锁 不要删除
- 再次追问: 在极端情况下 是否还会有问题
在极端情况下 STW结束后(此时锁还没有超时) 查询锁判断UUID是否是自己的 和 删除锁 这2个操作不是原子的 所以还是会删除别的请求上的锁
//伪代码如下 String uuid = UUID.create(); redis.setNx(key,uuid); //....上锁成功 //...业务逻辑 //....超长STW String value = redis.get(key); if(value.equers(uuid)){ //进入if 以后CPU切换 假设0.1秒之后切回来 而且此时正好锁过期 别的请求上了1个新的锁 redis.remove(key);//0.1秒之后 执行下面这行代码就会把别人新上的锁删除 }
解决方案: 使用 LUA 脚本 把查询判断是否是自己 UUID 的操作和删除操作变成原子操作
建议直接使用 redisson 大佬已经实现
- 在主从架构下,主还没有把锁同步到从,但是主挂了,此时从节点里没有上锁,会被抢到
使用 redlock 3 台及以上的单节点 redis 如果同时获取到 N/2+1 的锁,代表上锁成功
十. keys * 有什么缺点 如何解决
keys * 会阻塞 导致别的请求无法响应 换成 scan
十一. redis 如何实现延迟队列
理论上来说不应该使用 redis 做延迟队列 一般延迟队列里的消息比较重要 不能丢失
如果一定要用 可以使用 zset 时间当分数 轮询获取小于当前时间分数的 key
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于