• 为什么用缓存?
  • 高性能:减少查询同一个数据时的响应速度
  • 高并发:减少数据库的承载压力(2000/s),缓存走内存,天然支撑高并发
  • 缓存的不良后果:
  • 缓存与数据库的双写不一致
  • 缓存雪崩
  • 缓存穿透
  • 缓存并发竞争
  • Redis和memcached区别(单线程、NIO、异步)
  • Redis支持服务器端数据操作:数据类型更多,功能更全
  • 内存使用效率对比:简单key-value时memcached效率高,hash结构存储Redis高
  • 性能对比:Redis单线程,小数据时Redis高;大数据时多线程的memcached高
  • 集群模式:memcached不支持集群,Redis支持
  • Redis单线程模型:
  • 文件事件处理器,单线程,通过IO多路复用程序同时监听多个socket,通过socket上的事件选择对应的事件处理器处理事件
  • AE_READABLE:socket可读(连接操作、客户端对Redis执行write操作,close操作)
  • AE_WRITABLE:socket可写(客户端对Redis执行read操作)


  • Redis单线程模型为什么效率高?
  • 纯内存操作(事件处理器在内存操作,性能高1ms)
  • 核心基于非阻塞IO多路复用机制(不处理、直接压队列)
  • 单线程避免了多线程的频繁上下文切换问题
  • Redis有哪些数据类型?
  • string
  • hash
    • 结构化数据,存放对象
      • key=150
      • value={
      •   “id”: 150,
      •   “name”: “zhangsan”,
      •   “age”: 20
      • }
  • list
    • 列表性数据结构(微博粉丝)
      • key=某大v
      • value=[zhangsan, lisi, wangwu]
  • set
    • 无需集合,自动去重
  • sorted set
    • 去重&排序
  • Redis的过期策略、内存淘汰机制
  • 定期删除+惰性删除
    • 每隔100ms抽取设置过期时间的key,检查是否过期后删除(定期)
    • 获取某个key时,redis检查是否过期(惰性)
  • 内存淘汰

1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了

2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)

3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的key给干掉啊

4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)

5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key

6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除

  • 手写LRU算法
  • Redis高并发、高可用、缓存一致性:
  • Redis高并发:主从架构,一主多从,单主用来写入数据,单机几万QPS,多从用来查询数据,多个从实例可以提供每秒10W的QPS。如果需要缓存容纳数据量大,用Redis集群。
  • Redis高可用:如果你做主从架构部署,其实就是加上哨兵就可以了,就可以实现,任何一个实例宕机,自动会进行主备切换。
  • 高并发
    • 主从架构,实现读写分离(master写,slave读),水平扩容
    • redis replication->主从架构->读写分离->水平扩容支撑读高并发
    • master开启持久化
    • 主从架构核心原理:
      • master启动线程,生成RDB快照,发送给slave
      • 断点续传,记录offset继续复制
      • 无磁盘复制:在内存创建rdb,发送给slave
  • Redis replication(复制)的完整流程
  • 数据同步的核心机制:
    • master和slave都会维护一个offset
    • backlog:用来全量复制中断后的增量复制
    • master run id:区分master(重启或者数据发生变化)
    • psync:psync runid offset,从节点使用此命令从master node进行复制
  • 全量复制
    • master生成快照
    • 发送给slave node
    • master在生成快照时,将所有新的写命令缓存在内存中,slave保存rdb后,将新的写命令复制
    • slave在接收到rdb后,清空旧数据,加载rdb
    • 如slave开启aof,立即重写aof
  • 增量复制
    • 全量复制中断
    • master用runid和offset从backlog中获取到丢失数据,发送给slave
  • heartbeat
    • master默认每隔10秒发送一次heartbeat,salve node每隔1秒发送一个heartbeat
  • 异步复制
    • master每次接收到写命令之后,现在内部写入数据,然后异步发送给slave node
  • 高可用性
    • 基于哨兵(sentinal)的高可用,故障转移(failover),主备切换
  • 哨兵:
    • 哨兵的作用:
  1. 集群监控,负责监控redis master和slave进程是否正常工作
  2. 消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
  3. 故障转移,如果master node挂掉了,会自动转移到slave node上
  4. 配置中心,如果故障转移发生了,通知client客户端新的master地址
  • 分布式,哨兵集群运行
  • 主备切换数据丢失问题:
  • 异步复制导致:
  • master->slave复制是异步的,可能没复制完,master宕机
  • 集群脑裂导致:
  • master主节点,出现异常性的相同工作的两个节点
  • client可以和旧master写数据,恢复后旧master成为slave后数据丢失
  • 解决:
  • min-slaves-to-write 1
  • min-slaves-max-lag 10
  • 要求至少有1个slave,数据复制和同步的延迟不能超过10秒
  • 如果说一旦所有的slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候,master就不会再接收任何请求了
  • 哨兵的核心原理:
  • sdown和odown转换:
  • sdown主观宕机
  • odown客观宕机
  • quorum指定数量
  • 哨兵的自动发现机制:
  • __sentinel__:hello这个channel里发送一个消息
  • slave配置的自动纠正
  • slave->master选举算法
  • 跟master断开连接的时长
  • slave优先级
  • 复制offset
  • run id
  • Redis重启数据恢复(数据持久化)
  • 持久化的意义:数据备份、故障恢复
  • 两种持久化:RDB、AOF
  • RDB:对Redis中数据执行周期性的持久化
  • AOF:对于每条写入命令作为日志,以append-only模式写入一个日志文件中(更加完整)
  • Redis重启后,通过回放AOF日志中的指令重新构建数据集
  • AOF大到一等程度后,rewrite操作,构建更小的AOF文件
  • RDB优劣势:
  • 优点:(冷备)
    • 生成多个数据文件,适合冷备。AOF需要写脚本处理。
    • RDB对Redis性能影响小,fork子进行进行RDB持久化
    • RDB重启恢复速度快
  • 缺点:
    • 恢复效果差,数据丢失
    • fork子进程时,数据文件特别大,会对客户端服务暂停数毫秒
  • AOF优劣势:
  • 优点:
    • 保护数据不丢失
    • append-only写入,没有磁盘寻址开销,写入性能高,文件不易损坏
    • AOF日志文件过大rewrite对客户端读写性能影响小
    • 日志文件可读
  • 缺点:
    • 日志文件大
    • 支持的写QPS低
    • 健壮性低
  • 结合RDB和AOF同时使用
  • Redis集群模式
  • 分布式数据的核心算法,数据分布的算法:
  • redis cluster——hash slot算法
  • 每个master持有部分slot
  • 节点间的内部通信机制
  • 集群的元数据维护:集中式(ZooKeeper)、gossip
    • gossip协议:元数据分散,降低压力,更新有延迟(meet ping pong fail)
  • 10000端口:用于节点间通信,为服务接口+10000,ping->pong
  • 交换信息的内容:故障信息、节点的增删、hash slot信息。。。
  • jedis: Redis的java client客户端
  • smart jedis:本地维护hashslot->node的映射表,缓存,不需要节点moved重定向
  • hash tag手动指定对应slot,set key1:{100}
  • 高可用与主备切换:
  • pfail(主观宕机) fail(客观宕机)
  • 与哨兵类似
  • Redis缓存雪崩与缓存穿透
  • 缓存雪崩:
  • 事前:Redis高可用(主从+哨兵、Redis Cluster)
  • 事中:本地ehcache缓存(系统内部小缓存)+hystrix限流(限流降级组件)
  • 事后:Redis持久化,快速恢复缓存数据
  • 缓存穿透:
  • 没查到,写空值到缓存中(set -999 UNKNOWN)
  • 使用布隆过滤器或者压缩filter提前拦截
  • 缓存与数据库双写数据一致性(读写串行化):
  • 缓存+数据库读写模式:cache aside pattern
  • 读。先缓存,再数据库,读完放入缓存
  • 更新,先删除缓存(缓存复杂计算得到、不会频繁访问),再更新数据库
  • 简单场景:先删缓存,在修改数据库
  • 复杂场景(删除缓存,修改数据库之前,读后缓存旧数据,数据库新修改)
  • 数据库与缓存更新读取操作进行异步(线程)串行化
    • 内存队列放请求,一个队列对应一个线程,串行进行操作
  • 读请求长时阻塞
    • 队列里写请求过多(部署多个服务,分摊数据更新)
    • 加多机器,每个机器上部署的服务实例处理更少的数据
  • 多服务实例部署的请求路由
    • 同一个商品的读写请求,路由到同一台机器上
    • nginx,hash路由功能
  • 热点商品的路由问题(读写请求高)
    • 商品更新清空缓存,导致读写并发,更新频率不高影响不大
  • Redis并发竞争问题:
  • 多客户端并发写一个key
  • CAS类的乐观锁方案
  • zookeeper提供分布式锁

  • 每个value的时间戳要比缓存中的时间戳更新,才可以写
  • 生产环境中的Redis怎么部署