Redis

262 阅读8分钟

Redis

  1. 数据结构:Redis是一个Hash结构的K-V数据库,类似HashMap,Key是字符串对象,Value有5个

    1. 第一个String,底层是一个SDS,简单动态字符串

      1. 保存了字符串的长度Len,用的时候可以直接返回这个成员变量 STRLEN
      2. 修改的时候它会先检查分配给buf数组的总长度,可以防止缓存区溢出
      3. 还有flags变量,表示SDS的类型,5种类型对应不同的初始长度,可以根据字符串的大小来分配内存空间
      4. 存储对象的json, 常用的字典,配置,登录的token
      5. 常规计数,连续登录,密码错误多少次,给他锁定
      6. 分布式锁,加锁加过期时间,nx,px 看门狗
    2. 然后List是一个双向链表,增删快,查询慢

      1. 数据少的时候用压缩列表ziplist存,这是一块连续的内存空间
      2. 数据多的时候用quicklist存,这是双向链表和压缩列表的组合,ziplist作为双向链表的节点
      3. 提供了rpush,rpop等操作,可以做简单队列用
    3. Hash,类似于HashMap,存储用户信息 hset,hget

      1. 数组+链表的结构
      2. 通过索引可以快速定位数组下标
      3. Hash冲突的时候会存到链表里,查询和增删都快
    4. Set,类似于HashSet,点赞,好友关系 add,smembers

      1. 内部键值对,无序,唯一,自动去重
      2. 元素全是整数,并且数量少于512,会用整数集合intset存,这是一个有序集合
      3. 不满足intset的用hash表存
    5. Zset,排行榜 zadd,zrange

      1. 在Set的基础上,加了一个Score,是有序的
      2. 底层用到了跳表和hash表
      3. 跳表就是链表+多级索引,类似于有序数组,可以进行二分查找,时间复杂度是O(logN)
  2. 持久化:就是将内存中的数据,持久化到磁盘上,然后定期同步或者备份到云上

    1. RDB:每隔一段时间,进行数据持久化,他是一份一份的数据快照,save 10 1 #10秒1次key变更save

      问题:容易造成数据丢失,比如新数据还没来及做快照,Redis重启了

    2. AOF:(Append only File)只允许追加不允许修改的文件,保存所有写操作来实现持久化,需要修改配置文件开启

      redis.conf : appendonly yes ,问题:文件很大,恢复很慢 (策略:always,everysec,no)

    3. Redis使用混合持久化,开启配置aof-use-rdb-preamble yes

  3. 缓存穿透:查不到,缓存没有,数据库也没有,然后大量的无效Key过来请求,压力直接到数据库

    1. 布隆过滤器:有请求过来的时候先通过过滤器去判断数据库有没有这个值,有就返回,然后更新缓存,没有返回Null
    2. 缓存空值:布隆没有删除的操作,数据库把这个Key删了,布隆里还有,所以可以对那些删除的Key在缓存里存个空值
  4. 缓存击穿:热点Key过期或缓存里直接没有,但数据库有,这时候比较大的并发过来,会直接穿过缓存把压力打到数据库上

    1. 设置热点Key永不过期
    2. 加分布式锁
  5. 缓存雪崩:有大量热点Key同时失效,或者Redis直接down机不可用了,都直接导致大量的请求穿过缓存直接访问数据库

    1. Redis主从+哨兵模式去实现高可用
    2. 服务降级,短期内将不重要的服务先停掉
    3. Redis持久化,快速恢复缓存数据,一般重启,自动从磁盘中加载数据恢复内存中的数据
  6. 双写一致性

    1. cache aside pattern(缓存+数据库读写的模式) + 延迟双删(避免脏读)

    2. 读的时候,先读缓存,缓存没有的话,那么就读数据库,然后取出数据后放入缓存,同时返回响应

    3. 更新的时候,先删除缓存,再更新数据库,然后再删一遍缓存(根据读业务逻辑的耗时来定)

      1. 脏读:删缓存之后操作数据库之前,有个读请求过来了,就会读到旧数据
  7. 过期Key的删除

    1. TTL keyName 看key的剩余过期时间,-2表示不存在,-1表示永久有效,否则返回剩余时间
    2. 定时删除:用定时器轮询的方式去删除过期Key,消耗CPU资源,但内存不会浪费
    3. 惰性删除:只有当访问到这个Key的时候,才会判断是否过期,要不要删,会占用内存
    4. 定期删除:每隔一段时间,随机扫描一定数量的Key,判断是否过期,Redis同时使用惰性和定期删
  8. 淘汰策略

    1. 当 Redis 内存满了,在进行Set 的时候,就会触发淘汰策略
    2. NoEviction默认策略:对于写请求不再提供服务,直接返回错误
    3. LRU:最近最少使用
    4. LFU:最近使用次数最少的
    5. TTL:将要过期的
    6. Random:随机的 在redis.config中配置
  9. 为什么Redis单线程模型效率高

    1. 单线程指的是Redis对外提供的读写服务是单个线程完成的,其他的持久化,异步删除是其他线程做的

    2. 纯内存操作

    3. 单线程避免多线程频繁上下文切换的问题

    4. 核心是非阻塞的IO多路复用机制

      1. 使用一个线程来检查多个文件描述符FD的就绪状态,如果有一个就绪就返回

      2. 复用有三种方式:select,poll,epoll,前两种的操作方式都是遍历,时间复杂度是O(N),线程不安全

      3. Redis默认epoll,线程安全,通过回调函数的方式来操作,时间复杂度是O(1)

      4. Epoll是Linux提供的系统实现,谁有数据,就处理谁的请求,不会阻塞

      5. 核心方法有3个

        1. epoll_create 创建epoll对象,对红黑树和双向链表初始化

          1. 红黑树主要存储了需要进行状态监控的文件描述符FD
          2. FD:一个被打开的文件的索引
          3. 双向链表就是就绪列表
        2. epoll_ctl 注册监听,绑定事件,读写事件发生的时候,通过回调函数CallBack把事件放入就绪列表

        3. epoll_wait 读取就绪列表,通过epoll这种方式,Redis就不需要一直轮询检查到底有没有实际的请求发生,避免CPU资源的浪费,Nginx也是用的epoll的方式,不过两者epoll的模式不一样

          1. Redis水平触发LT:事件发生的时候,epoll_wait会通知程序去处理,如果这次没有处理完,下次调用的的时候,它还会通知,就是你一直不处理,它就一直通知你,会降低效率
          2. Nginx边缘触发ET:它只会通知你一次,直到这个文件描述符出现第二次可读写事件才会通知,这种模式比LT效率高,系统不会充斥大量你不关心的就绪文件描述符
  10. IO模型

    1. BIO:同步阻塞IO,读写请求都会被阻塞,直到读到数据,或完成写入操作
    2. NIO:同步非阻塞IO,不管数据准没准备好,都会直接返回
    3. AIO:异步非阻塞IO,数据拷贝阶段完全由操作系统处理,而应用程序只需要等待通知
  11. Redis事务,执行顺序

    1. 开始事务 multi(标记事务开始)
    2. 命令入队 queued (一行一行的命令进入队列等待执行)
    3. 执行事务 exec(触发事务)
    4. 单个Redis命令的执行是原子性的,但Redis事务的执行并不是原子性的。
    5. 即中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
  12. 主从+哨兵 etc/redis/sentinel.conf 默认监听端口26379

    1. 一主多从,master用来写,多个slave用来读,用slaveof去设置从节点,设置好后,主从的数据会进行自动同步(主从复制)

      1. slave节点向master发送数据同步命令
      2. master收到命令后,创建RDB快照文件,发给slave,一起发的还有缓存的所有写命令(RDB时候有写的操作)
      3. 复制初始化完成后,master每一次写,都会同步到slave
    2. 用哨兵集群去监控Redis集群状态,是通过定时任务去做的,当发现Redis主节点Down掉后,Sentinel会发起选举(Raft),最终选举的Leader去完成Redis集群主从切换的过程,切换完后还会通知Client客户端新的Master地址

    3. 哨兵定时任务和下线

      1. 每10秒一个info命令,发现slave节点,确定主从关系
      2. 每2秒哨兵之间互相通信,交换对集群的监控状态
      3. 每1秒每个哨兵会对整个集群包括Redis集群和其他Sentinel做心跳检测,判断节点存活
      4. 主观下线:一个Sentinel判断Redis主节点down掉
      5. 客观下线:半数以上Sentinel判断主节点down掉