Redis拾遗

  • 本文整理自剑指Java面试-Offer直通车

  • 主流应用架构

  • Memcache和Redis的区别

    • Memcache

      • 代码层次类似Hash

      • 支持简单数据类型

      • 不支持数据持久化存储

      • 不支持主从

      • 不支持分片

    • Redis

      • 数据类型丰富

      • 支持数据磁盘持久化存储

      • 支持主从

      • 支持分片(Redis3.0+) 

  • 为什么Redis能这么快

    • 完全基于内存,绝大部分请求是纯粹的内存啊哦做,执行效率高

    • 数据结构简单,对数据操作也简单

    • 采用单线程,单线程也能处理高并发请求,想多核也可启动多实例

    • 使用多路I/O复用模型,非阻塞IO

  • 多路I/O复用模型

    • FD

      • File Descriptor,文件描述符

      • 一个打开的文件通过为疑似的描述符进行引用,该描述符是打开文件的元数据到文件本身的映射

    • 传统的阻塞I/O模型

    • epoll/kqueue/evport/select

      • Redis根据不同的操作系统,选择不同的多路复用函数

      • 优先选择时间复杂度为O(1)的I/O多路复用函数作为底层实现

      • 以时间复杂度为O(n)的select作为保底

      • 基于react设计模式监听I/O事件

      • Select系统调用

  • 说说你用过的Redis的数据类型

    • String

      • 最基本的数据类型

      • 二进制安全

        • 可以包含任何数据,比如JPG图片或者序列化的对象

      • 最大512M

    • Hash

      • String元素组成的字典,适合用于存储对象

    • List

      • 列表,按照String元素插入顺序排序

      • 大约能存储40亿个成员

    • Set

      • String元素组成的无需集合,通过哈希表实现,不允许重复

    • Sorted Set

      • 通过分数来为集合中的成员进行从小到大的排序

    • HyperLogLog

      • 用于计数

    • Geo

      • 支持存储地理位置信息

    • 底层数据类型基础

      • 简单动态字符串

      • 链表

      • 字典

      • 跳跃表

      • 整数集合

      • 压缩列表

      • 对象

  • 从海量Key里查询出某一固定前缀的Key

    • KEYS pattern

      • 查找所有复核给定模式pattern的key

      • 一次性返回左右匹配的key

      • 键的数量过大会导致服务器卡顿

    • SCAN cursor [MATCH pattern] [COUNT count]

      • 基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程

      • 以0作为游标开始一次新的迭代,知道命令返回游标0完成一次遍历

      • 不保证每次执行都返回某个给定数量的元素(甚至会返回0个,只要游标不为0,就不应该结束迭代),支持模糊查询

      • 一次返回的数量不可控,只能是大概率复核count参数 

      • 有可能会返回重复Key

  • 如何通过Redis实现分布式锁

    • 分布式锁需要解决的问题

      • 互斥性

        • 只能有一个客户端获得锁

      • 安全性

        • 锁只能被持有锁的客户端删除

      • 死锁

        • 获取锁的客户端未能释放锁,其他客户端再也无法获取该锁

      • 容错

        • 当部分Redis节点宕机时,客户端应依然能够获取锁

    • SETNX key value

      • 如果key不存在,则创建并赋值

      • 时间复杂度,O(1)

      • 设置成功返回1,设置失败返回0

      • SETNX设置的key将长期存在,分开设置EXPIRE无法满足原子性

    • SET key value [EX seconds] [PX milliseconds] [NX|XX]

        • EX seconds,设置键的过期时间为seconds秒

        • PX millisecond,设置键的过期时间为milliseconds毫秒

        • NX,只在键不存在时,才对键进行设置操作

        • XX,只在键已存在时,才对键进行设置操作

        • SET操作完成时,返回OK,否则返回nil

    • 大量的key同时过期的注意事项

      • 集中过期,由于清除大量的key很耗时,会出现短暂的卡顿现象

      • 解决方法,在key的过期时间上加上一个随机值

  • 如何使用Redis做异步队列

    • 使用List作为队列,RPUSH生产消息,LPOP消费消息

      • 缺点,没有等待队列里有值就直接消费

      • 弥补,可以通过在应用层引入Sleep机制去调用LPOP重试

    • BLPOP key [key...] timeout

      • 阻塞直到队列有消息或者超时

      • 缺点,只能供一个消费者消费

    • 使用pub/sub,主题订阅者模式

      • 发送者pub发送消息,订阅者sub接收消息

      • 订阅者可以订阅任意数量的频道

      • subscribe 频道

      • publish 频道 消息

      • 缺点,消息发布是无状态的,无法保证可达,若发送消息时,没有消费者在线,消息就丢失了

  • Redis如何做持久化

    • RDB(快照)持久化,保存某个时间点的全量数据快照

      • 优点,全量数据快照,文件小,恢复快

      • 缺点,无法保存最近一次快照之后的数据

      • SAVE命令

        • 在主线程中保存快照,阻塞Redis的服务器进程,知道RDB文件被创建完毕

      • BGSAVE命令

        • Fork一个子进程来创建RDB文件,不阻塞服务器进程

      • LASTSAVE

        • 返回上次执行save的时间

        • BGSAVE保存成功后,才会更新此时间

      • 自动化出发RDB持久化

        • redis.conf

          • save 900 1

            • 900秒内有1条写入指令,则产生一次快照

          • save 300 10

            • 300秒内有10条写入指令,则产生一次快照

          • save 60 10000

            • 60秒内有10000条写入指令,则产生一次快照

          • stop-writes-on-bgsave-error yes

            • 当备份进程出错时,主进程停止接受写入操作

          • rdbcompression yes

            • 在备份时,将rdb压缩后再保存,建议设置为no

          • save ""

            • 禁用rdb配置

        • 主从复制时,主节点自动触发

        • 执行Debug Reload时

        • 执行Shutdown且没有开启AOF持久化

      • 缺点

        • 内存数据的全量同步,数据量大会由于I/O而严重影响性能

        • 可能会因为Redis挂掉而丢失从当前至最近一次快照期间的数据

    • AOF(Append-Only-File)持久化,保存写状态

      • 优点,可读性高,适合保存增量数据,数据不易丢失

        • 缺点,文件体积大,恢复时间长 

      • 记录下除了查询以外的所有变更数据库状态的指令

      • 以append的形式追加保存到AOF文件中(增量)

      • redis.conf

        • appendonly yes

          • 开启AOF

        • appendfilename "appendonly.aof"

          • AOF文件的名字

        • appendfsync always/everysec/no

          • 立即写入/每秒写入/交由操作系统决定(一般等到缓存区填满才同步数据到磁盘中)

          • 推荐everysec

      • 日志重写解决AOF文件大小不断增大的问题

        • 调用fork(),创建一个子进程

        • 子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件

        • 主进程持续将新的变动同时写到内存和原来的AOF里

        • 主进程获取子进程重写AOF的完成信号,往新AOF同步增量变动

        • 使用新的AOF文件替换掉旧的AOF文件

    • RDB-AOF混合持久化方式,4.0后默认

      • BGSZVE做镜像全量持久化,AOF做增量持久化

    • Redis数据的恢复

      • RDB和AOF文件共存情况下的恢复流程,AOF优先

  • 使用Pipeline的好处

    • Pipeline和Linux的管道类似

    • Redis基于请求/响应模型,单个请求处理需要一一应答

    • Pipeline批量执行指令,节省多次IO往返时间

    • 有顺序依赖的指令建议分批发送

  • Redis的同步机制

    • 全同步过程

      • Slave发送sync命令到Master

      • Master启动一个后台进程,将Redis中的数据快照保存到文件中

      • Master将保存数据快照期间接收到的写命令缓存起来

      • Master完成写文件操作后,将该文件发送给Slave

      • 使用新的AOF文件替换掉旧的AOF文件

      • Master将这期间收集的增量写命令发送给Slave端进行回放

    • 增量同步过程

      • Master接收到用户的操作指令,判断是否需要传播到Slave

      • 将操作记录追加到AOF文件

      • 将操作传播到其他Slave

        • 对齐主从库,确保从数据库是该操作所对应的数据库

        • 将命令和参数按照Redis的格式写入到响应Slave的缓存中

      • 将缓存中的数据发送给Slave  

  • Redis Sentinel(Redis哨兵)

    • 解决主从同步Master宕机后的主从切换问题

      • 监控,检查主从服务器是否运行正常  

      • 提醒,通过API向管理员或其他应用程序发送故障通知

      • 自动故障迁移,主从切换

  • 流言协议Gossip

    • 每个节点都随机地与对方通信,最终所有节点的状态达成一致

    • 种子节点定期随机向其他节点发送街店列表以及需要传播的消息

    • 不保证信息一定会传递给所有的节点,但是最终会趋于一致