全局哈希表

通过全局哈希表来组织数据,对于发生哈希冲突的时候使用渐进式哈希的解决方式来解决哈希表变慢的情况,这里是用于获取对于确定的类型然后根据类型来获取对应的值的确定的类型,当发生哈希冲突的时候使用链式哈希表的方式来解决冲突

img

img

字符串

  • 简单动态字符串
    • 二进制安全
    • 缓冲区溢出自动扩容

列表

  • quicklist

    img

    quicklist是链表加压缩列表

    对于压缩链表来说:

    • 直接记录首尾位置
    • 每个结构记录前一个节点的长度
    • 每一个节点记录自己占用的空间以及类型

    但是由于紧凑的结构导致要是插入或者更新的时候需要对原来的数据进行迁移可能会导致连续更新,也就是对整个链表空间进行分配,所以一般对于节点数量不太多的时候比较友善

    但是在后续的版本迭代中去掉了压缩列表 改成了listpack 不会发生连续更新的现象了

哈希

  • 快表 quicklist 小于512个 并且所有值小于64字节

  • 哈希表

    对于哈希冲突的时候我们使用链式哈希来解决哈希冲突

    渐进式哈希

    渐进式 rehash 步骤如下:

    • 给「哈希表 2」 分配空间;
    • 在 rehash 进行期间,每次哈希表元素进行新增、删除、查找或者更新操作时,Redis 除了会执行对应的操作之外,还会顺序将「哈希表 1 」中索引位置上的所有 key-value 迁移到「哈希表 2」 上;
    • 随着处理客户端发起的哈希表操作越来越多的时候最终会将所有的数据迁移到新的哈希表上面去,完成rehase的操作

    触发时机

    • 负载因子大于1的时候,并且没有执行rdb快照和aof文件重写的时候进行rehash
    • 负载因子大于五的时候 强制执行rehash操作

SET

  • 整数集合

    本质就是一串连续的数组,会发生升级的操作 升级是在原本的数组上扩展空间 同时按照间隔划分

  • 哈希表

ZSET

  • listpack 小于128个的时候 每个元素值小于64字节

  • 跳表

    跳表适合范围查找,实现难度更加低,内存占用比平衡树更加灵活

为什么redis是单线程

  • redis的单线程是对于网络io和键值对的的读写是单线程的

  • 持久化以及异步删除同步等是由额外的线程实现的

  • 多线程编程模式面临的共享资源的并发访问控制问题

为什么这么快

  • 大部分操作在内存上完成
  • 采用高效的数据结构
  • 网络使用了多路复用机制,非阻塞的模式
img

AOF

  • 先写内存再写磁盘
  • 语法检查 避免写进磁盘了然后才发现命令是错的
  • 不会阻塞当前操作

风险

  • 数据丢失
  • 可能回写磁盘的时候阻塞影响后续的进程、
  • 文件越写越大 导致需要重写日志

回写策略 appendfsync

img
  • Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
  • Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
  • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

重写AOF

  • “一个拷贝,两处日志”

    • 一个拷贝:主线程fork出一个后台bgrewriteaof子进程
    • 两处日志:AOF 缓冲区 aof_buf(崩溃恢复) AOF重写缓冲区(重写日志记录的操作写入AOF文件)
      • aof_buf是主线程程将数据写入AOF前使用的缓冲区
      • aof_rewrite_buf_blocks是AOF重写期间,父线程用于存放差异数据的缓冲区
      • 当子进程重写完成后会给redis发送信号 redis对于信号进行处理 将重写缓冲区写到aof文件去
      • 对新的aof文件进行命名覆盖原来的aof文件aof重写完毕
  • 什么时候触发

    • 没有bgsage
    • 没有aof重写
    • 文件大于最小的文件大小
    • 大于增长比例

过期删除

  • 当设置了过期字典的时候,将key带上过期时间存储到一个过期字典中
  • 查询的时候首先查询是否在过期字典中,不存在就正常读,存在就判断为过期并且删除(惰性删除)

定期删除

  • 每隔一段时间随机从redis获取20个key 判断是否过期 删除过期的
  • 判断执行时间是否超出了上限 要是超过了上限就不迭代了
  • 要是过期的key超过了%25 也就是5个 继续迭代操作

持久化的时候的key

  • RDB
    • 会对key进行过期时间检查
    • 加载阶段
      • 主服务器对键值对进行检查
      • 从服务器直接写入
  • AOF
    • 写入的时候没有被删除一直存在
    • 删除的时候会追加一条del指令
    • AOF重写的时候
      • 会对键值对进行检查 检查是否过期
  • 在主从的模式下会对aof文件增加del命令同步到所有的从库

缓存淘汰策略

数据保障:使用 redis 实现死信重试队列保障数据精准投递不丢失

在设置了过期时间的数据中进行淘汰:

  • volatile-random:随机淘汰设置了过期时间的任意键值;

  • volatile-ttl:优先淘汰更早过期的键值。

  • volatile-lru(Redis3.0 之前,默认的内存淘汰策略):淘汰所有设置了过期时间的键值中,最久未使用的键值;

    在 Redis 的对象结构体中添加一个额外的字段,用于记录此数据的最后一次访问时间

    不用为所有的数据维护一个大链表,节省了空间占用;

    不用在每次数据访问时都移动链表项,提升了缓存的性能;

  • volatile-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰所有设置了过期时间的键值中,最少使用的键值;
    在所有数据范围内进行淘汰:

  • allkeys-random:随机淘汰任意键值;

  • allkeys-lru:淘汰整个键值中最久未使用的键值;

  • allkeys-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰整个键值中最少使用的键值。 #