redis 初级面试题笔记

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(发布订阅)

      1. 不是很重要的消息可以通过发布订阅来实现
      2. websocket 转发消息 用户张三和 A 服务器有长连接 但是他要给李四发消息 李四连的服务器是 B 可以通过发布订阅来转发 由于没有消息可靠机制 所以只适合不是特别重要的聊天场景 如培训机构官网的在线客服 数据丢了关系不是很大
    • pipeline(管道)

      可以给管道添加多个指令 一次性发出并执行 但是如果中间某个指令报错 不会回滚 也不会停止 剩下的命令仍然执行

    • watch(事务监控 key)

      配合管道使用 先监控某个 key 然后给管道添加命令 最后执行管道命令 在执行管道的命令之前 监控的 key 有改变 管道中的命令全部取消

二. 缓存穿透


恶意攻击 数据库根本没有该记录 比如 http://xiaokedamowang.cn?phoneId=123

假设这是查询手机的参数 但是我数据库里没有 ID 为 123 的手机

  1. 缓存 null 作用不大
  2. 布隆过滤器

三. 缓存击穿


瞬时流量打进来,没有缓存该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 失效 导致大量的 [三. 缓存击穿](#三. 缓存击穿) 发生

  1. 随机 key 过期时间
  2. 重复 [三. 缓存击穿](# 三. 缓存击穿) 的解决方案

五. 数据库和缓存不一致问题


又称缓存双写不一致问题

  1. 不处理 或者 减少 key 的过期时间 主要看业务对于数据不一致的容忍度
  2. 网上有延迟双删策略(只能减少概率 不能杜绝)
  3. 上锁 如读写锁 或者读不上锁 写的时候上锁
  4. 阿里 canal

六. 主从不一致问题


  1. 配置同步因子 趋向于 强一致性
  2. wait

七. redis 持久化方式和原理


  1. RDB

RDB 是数据快照,优点是体积小,备份和恢复性能快,缺点是 redis 宕机的时候,丢失的数据多

RDB 是在指定时间间隔内讲内存数据快照写入磁盘, 实际操作是通过 fork 子进程,先写入临时文件 再替换原来的文件

  1. AOF

AOF 记录的是指令,是追加模式,redis 宕机丢失数据少,支持多种策略 AOF 文件过大的时候会命令压缩合并重写

八. redis 过期删除方式及内存不足的策略


过期 key 删除方式

  1. 定时清理

    默认情况下 每秒进行 10 次任务 总共执行 250 毫秒 随机检查 100 个 key 删除已过期的 key 如果过期的 key 大于 25% 重复操作

  2. 查询该 key 的时候判断是否过期 过期就删除

内存不足的删除策略

  1. 不删除 插入报错
  2. 全局选择性移除
    • 随机删除
    • LRU 最长时间没被使用的 key
    • LFU 使用频率最少的 key
  3. 设置过期时间选择性移除
    • 随机删除
    • 最接近过期的 key
    • LRU 最长时间没被使用的 key
    • LFU 使用频率最少的 key

九. 使用 redis 当分布式锁有哪些问题 怎么解决


  1. 运行时间 超过过期时间 会导致锁失效

开线程循环续费 俗称看门狗

  • 追问: 如果超长时间的 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 大佬已经实现

  1. 在主从架构下,主还没有把锁同步到从,但是主挂了,此时从节点里没有上锁,会被抢到

使用 redlock 3 台及以上的单节点 redis 如果同时获取到 N/2+1 的锁,代表上锁成功

十. keys * 有什么缺点 如何解决


keys * 会阻塞 导致别的请求无法响应 换成 scan

十一. redis 如何实现延迟队列


理论上来说不应该使用 redis 做延迟队列 一般延迟队列里的消息比较重要 不能丢失

如果一定要用 可以使用 zset 时间当分数 轮询获取小于当前时间分数的 key

  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    250 引用 • 244 回帖 • 573 关注
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    316 引用 • 1389 回帖 • 49 关注

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • xiaokedamowang
    捐赠者 作者

    写太多的话不方便背 初级的话答出一些点就差不多了