常见面试问题

1. 分布式系统中常用的缓存方案有哪些?

  • 客户端缓存:页面和浏览器缓存,app 缓存,h5 缓存,localStorage,sessionStorage
  • CDN:内容存储=数据存储,内容分发=负载均衡
  • Nginx 缓存: 静态资源
  • 服务端缓存:本地缓存,外部缓存
  • 数据库缓存:持久缓存(mybatis,hibernate 多级缓存),mysql 查询缓存
  • 操作系统缓存:page cache,buffer cache

2. 常见的缓存淘汰算法

  • FIFO - 先进先出
  • LRU - 最近最少使用
  • LFU - 最不经常使用

3. Redis 如何配置 Key 的过期时间?他的实现原理是什么?

  • Expire or SETNX
  • 原理:
    • 定期删除:每隔一段时间,执行一次删除过期 Key 的操作(批量脚本)
    • 懒汉式删除:每当使用 get,getset 等指令去获取数据时,判断 key 是否过期,过期的话就先删除再操作
    • redis 同时使用两者,平衡执行频率和执行时长

4. Redis 线程模型,单线程为什么快?

  • 基于 Reactor 开发了网络时间处理器 - 文件事件处理器,采用 IO 多路复用监听多个 Socket
  • 纯内存操作、核心基于非阻塞的 IO 多路复用机制、单线程避免了多线程反复上下文切换的性能问题

5. redis 的常见结构及应用场景

  • string
    • id/id_info 缓存
    • 计数器
    • setnx 分布式锁
      • setnx 的时候会传入一个随机值
      • 解锁:‘if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end’
      • lua脚本
    • 分布式ID
    • sds(动态简单字符串)
      • 读取时间复杂度为 O(1)
      • 惰性删除,留待下次使用
      • 一次分配最大为 128kb
  • list
    • 当队列用,最近100个购买用户信息
    • 当栈用
    • 非实时分页列表,比如小时榜,日榜,周榜
    • 存储微博、微信公众号等消息流数据
    • 等于链表,插入删除 O(1),查找 O(n)
  • hash
    • 相比string获取单个字段节省 序列化与反序列化操作
    • 相当于 HashMap ,由数组加链表组合而成,当一维的 hash 数组碰撞时,用链表串起来
  • set
    • 取交、并、差集
    • 点赞、收藏、关注等…
    • kv 模式
  • sorted set
    • 排行榜功能,实时分页列表,实时榜单等
    • set 基础上增加了分数
    • 结构上是由一个 dict 和一个跳表组成,dict 保证 key 唯一性
    • 插入删除都是 O(log n),查找 O(logn)
    • 一大优点是可以对分数进行范围查找
    • 少量数据按照 ziplist 存储,大量数据按照跳表存储

redis 大key 问题是什么?怎么查出来?一般怎么解决?

大key问题呢是说 redis 的 kv 存储中,value值过大,一般超过10kb,我们就会认为这是一个大 key 了。
有执行命令,但是没有专门准备这个内容
大 key 的危害主要是:在redis内存中分布不均;操作耗时;取结果的数据量大,容易造成网络io堵塞
解决方式呢:可以逻辑上对于大 key 的 value 进行拆分和重组。其中 string 类型的大 key 一般不建议存到redis。另外的可以采用 hash 将大 key 拆分

6. redis 集群方案

  • 主从模式
  • 哨兵模式
    • 概念
      • 集群监控:监控主从是否正常
      • 消息通知:故障通知
      • 故障转移:主挂了,移到从身上
      • 配置中心:通知 client 新的 master 地址
    • 本身也是分布式的,具体方案:
      • 通常3个哨兵实例来保证健壮性
      • 即使哨兵自己挂了,还是可以正常工作
      • 不保证数据领丢失,可以说主从结构就不保证
      • 判断故障需要用分布式选举获得大部分哨兵统一才行
  • Redis cluster:服务端 Sharding 计数。采用槽的概念,一共16384个槽,请求发送至任意节点
    • 方案说明
      • 通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据
      • 每份数据分片会存储在多个互为主从的多节点上
      • 数据先写入主节点,再阻塞同步至从节点
      • 同一分片之间不保证强一致性
      • 扩容时需要把旧节点数据也迁移一部分至新节点
    • gossip 协议,多主多从
  • sorted set
    • 实时分页列表,如实时榜单
  • 一般就是当缓存用
  • 单线程模型
  • 目的是服务无状态:session,token等;分布式锁
  • 无锁化?

2. 单线程还是多线程?

  • 工作线程都是单线程:
    • 操作是原子的单指令 pipeline
    • 事务 vs pipeline :事务执行期间是原子的;执行失败就是失败,其他指令继续执行,没有回滚 –> redis 少使用事务 && 事务内的指令少 + 快
  • 6.x+版本出现了 io 多线程

  • 详细理解 io 多线程 :内核,网络通信(懂了再说)
  • 单线程,满足 redis 的串行原子;io 多线程以后,把输入/输出放到线程中并行,好处是:执行时间快;更好的压榨系统及硬件的资源

3. redis 存在线程安全的问题么?为什么?

  • 参考问题2,redis核心是单线程串行,业务使用的时候需要自行保障线程安全

4.

6. 缓存如何淘汰的?

内存空间不足
淘汰机制
lru,lfu,random,ttl
全空间
设置了过期时间的key的集合中

7. 如何进行缓存预热?

提前加载数据(很难判断哪些是真正的热数据,常常会出现缓存失败的情况)
开发逻辑上要应对差集数据造成的 击穿,穿透,雪崩

8. 数据库和缓存不一致如何解决?

恶心点的使用事务,但是意义不大,场景多为读多写极少,仅仅在秀肌肉
业务写db,然后redis更新缓存
业务写到消息队列中,redis和db同时消费数据,同时更新
redis缓存,更倾向于允许稍微的时差
总思路是减少db操作

9. redis 主从不一致如何解决?

redis 默认弱一致性,主从异步同步
分布式锁不能用主从,可以用单实例、分片集群、redlock –> redisson
配置中可以配置同步因子,总趋向于强一致性

10. redis 持久化原理

当前线程阻塞服务
后台异步进程完成持久化

11. 并发超量,redis 崩溃后如何处理?

雪崩击穿穿透处理方案

12. 为啥使用setnx

原子,不存在即创建
分布式锁,用 set k v nx ex 不存在,过期时间,避免死锁

设计短链
base64加密解密。
热url,满了怎么办?对内部?对外部?双方隔离?布隆判存在性?

qps 十几万?保证库存?
分布式事务。看看科科的写法