Redis八股笔记

26 阅读26分钟

1.基本类型

1.String

  1. 为什么不采用c语言作为字符串具体实现
    1. O(n):因为c语言的字符串结尾是以\0结尾的
    2. 没有很好的扩容机制
    3. 特殊字符无法处理:还是因为\0
  2. 使用java语言的优点(虽然实际还是以c语言为基础,加了别的标识属性的结构体)
    1. 字符串长度获取时间复杂度从 O(n)->O(1)
    2. 减少字符串扩容引起的数据搬运次数
    3. 储存更加复杂的二进制数据
  3. 底层实现
    1. SDS:简单动态字符串
    2. int (整数,8字节)
    3. embstr(短字符串,<=44字节)
    4. raw(长字符串,>44字节)

2.list

  1. 底层实现

    1. Redis3.0版本之前

      1. linkedlist双向链表(元素少于512个,每个元素值都小于64字节)
      2. ziplist压缩列表
    2. Redis3.2版本后都是quicklist

  2. 其中有三种函数,分别是复制函数, 释放函数, 对比函数

  3. 常见API函数:

    1. lpush:向链表左边增加元素
    2. rpush:向链表右边增加元素
    3. lpop:弹出左侧第一个元素
    4. rpop:弹出右侧第一个元素
    5. llen:获得链表长度
    6. lrange:按索引范围获取值

3.hash

底层:

​ 键值对少的时候,键值<=64字节时用ziplist

​ 键值对多或大的时候用hashtable

TIP

hashtable:线程安全(因为hashtable通过锁机制来实现,在每个桶上),且键值都不能为null值。

hashmap:线程不安全,且能有一个null键和多个null值。

  1. 键值对,一对一的实现,一个key对应一个value
    1. 通过键key,在O(1)时间复杂度下获得相对应的值
    2. 因为c语言没有内置哈希表这个数据结构,所以就是Redis是自己实现的Hash表
  2. Redis采用拉链法
    1. 直接找到对应的key然后直接插到单链表的末端
  3. sizemask的作用是拿来防止error,做与运算,防止溢出

4.set

底层

  • intset,整数集合,且元素个数<=512个时。

    • 二分查找
    • 优点:极致的内存效率
  • hashtable,哈希表,元素多且为非整数时。

    • Redis字典(dict),使用哈希表来实现,value被存储为字典的key
    • 优点:通用性强,插入,删除,查找的时间复杂度都是O(1).

编码转换

​ 当你一开始添加的都是整数,集合使用 intset 编码。如果你后来添加了一个非整数字符串(如 "abc"),Redis 会立即将集合的编码从 intset 转换为 hashtable

应用场景:

  • 去重:存储文章所有标签,自动保证标签不重复。
  • 共同好友或者关注(社交关系)
  • 随机事件(抽奖,摇号)

5.ZSet

底层实现:

  • ziplist压缩列表:
    • 元素少于128个,每个member少于64字节。
    • 结构:member和score作为俩个紧挨着的节点,按score值从小到大顺序存储。
  • skiplist(跳表)+hashtable(哈希表)
    • skiplist
      • 只要不满足ziplist条件,就会用这个结构
      • 结构:按score排序,效率为O(log n),二分查找
    • hashtable
      • member为key,score为value,支持根据member查找score,时间复杂度为O(1).

特点

1.有序性:所有元素都由score来排序

2.唯一性:member是唯一的,但score可以重复,如果俩个member的score相同,则再按member的字典排序。

3.高性能:插入,删除,按score或按排名查找的时间复杂度都是O(log N).

应用场景

  • 排行榜:微博热搜,热点话题
  • 带权重的消息队列
    • 将消息为member,将任务执行时间戳为score
  • 延时任务
  • 范围查找:查找90分以上的同学。

二分查找

image-20250512175044920

操作

image-20250512175445048

6.高级存储结构

  • BitMap(位图)
    • 本质:string的扩展,用bit位存储(1个字节=8位)
    • 场景:签到(365天只需要46字节),用户在线状态(1亿用户仅需12Mb)
  • HyperLogLog(基数统计)
  • Geo(地理位置)
    • 附近的人,LBS服务

2.持久化

  • Redis是内存型数据库
  • 持久化意思就是:
  • 把内存上的数据同步到硬盘中去,断电后不丢失
  • 缺点,数据易失性,断电后内存数据消失
  • 解决方法RDB(Redis Data Base),AOF(Append Only File)

RDB(Redis DataBase)

  • 通俗:就是把内存中所有数据直接存储到硬盘中!!!!

  • 就是把目前redis内存中的数据,生成一个快照(RDB文件),保存在硬盘中,就是相当于备份,然后文件是以.rdb为后缀的,就是如果发生事故的话,redis就可以通过这个文件,进行文件读取,并且将数据重新载入内存中

image-20250512191815420

  • 上图中不一样的就是

    • 数据里有设置的数据的有效期
      • 一个是有过期时间的数据
      • 一个是没有过期时间的数据
  • 以下是各个基本类型在RDB中的结构

image-20250512192214681

触发RDB条件

  • 手动触发
    • save:执行save指令,主线程执行rdbSave函数,服务器进程阻塞,就是不能处理别的请求了,直接就是卡住了,这个redis
    • bgsave(background save):本质就是这个命令和save差不多,区别就是这个命令会去fork一个子进程,去执行rdb的Save函数,所以就是主线程还是可以执行新请求,不过就是bgsave在子进程上用,不会干扰主进程。
  • 自动触发
    • redis的配置文件写入save m n ,就是当m秒内发生n次此变化,就会自动执行bgsave
    • 意思就是相当于在后台一直跑这个bgsave这个函数到达一定条件就是m秒内发生了n次变化就到达条件,直接执行bgsave。

优缺点

  • 优点:文件小,恢复快,因为是直接加载二进制
  • 缺点:可能丢失最近数据(最近俩次快照间的修改,fork子进程需要大量资源)

AOF(Append Only File)

概念

  • 记录之后所有对Redis数据进行修改的操作
    • 发生事故,redis可以通过AOF文件,将文件中的数据修改命令全部执行一遍,一次来恢复数据(增量恢复)
    • 重启重新执行命令恢复数据

重写与恢复

image-20250512193926701

问:

当AOF执行过程中会产生很多的无效命令以及过期命令,造成AOF文件过大,请问怎么样才能缓解这种情况???

:Redis提供了一个AOF文件重写功能,创建一个新的AOF文件,当有对同一条数据操作或者不同的数据操作而产生新的命令,redis 的AOF会有一个文件重写策略,产生一个新的AOF文件,代替先前的AOF文件,但是俩个AOF文件保存的数据库数据相同的,只是新的不会有冗余的命令

注意:新的AOF文件是不会参考老的AOF文件的,而是由当前Redis的数据状态产生的。

触发AOF条件:

  • 手动触发:
    • bgrewriteaof
    • 执行这个命令,然后redis就会根据当前数据库的状态生成一个AOF文件,并且把老版的AOF文件替换掉,旧的会被删掉
  • 自动触发:
    • 配置文件中设置appendonly yes开启
      • appendonly yes策略
        • Always:就是同步写回,意思就是在每个命令执行完毕后直接将命令写入到磁盘文件中,就是AOF文件中(数据基本保证了可靠性,但是redis性能大大减低)
        • Everysec:就是每秒写回,在每个命令执行完成后,直接被写入到文件的内存的缓冲区,然后每过1s,redis就会把这个缓冲区的命令写到磁盘的AOF文件中(发生事故最多丢失1s内的数据,性能影响不大)
        • No:这个NO就是将操作命令全部写到Redis缓冲区,至于什么时候将缓存数据写到磁盘,这得看操作系统决定(出了问题,数据不知道有没有,但是性能影响最小)
      • Redis得appendonly yes默认策略是Everysec
      • 注意:其实No的效率跟Everysec差不多,建议用Everysec

混合持久化(redis4.0+)

  • 配置:aof-use-rdb-preamble yes
    • aof文件开头存rdb快照,后续存增量命令,兼顾rdb恢复快和aof安全性

选择建议

​ 数据不重要用RDB,重要用AOF(everysec),极致安全用混合。


3.缓存

1.内存淘汰

因为一般数据比缓存大,就是比如数据有20个g,但是高速缓存只有2个g 这样,所以当在缓存中满了的情况下,redis会采用淘汰策略

  • 先进先出FIFO算法
    • 就是队列那样,先进缓存的老元素被淘汰 ,然后最新元素进入。
  • 最近最少使用LRU(Least Recently Used)
    • 实现思路:双向链表+哈希表形式储存
    • 通俗:就是按照数据中被使用数据时间排序,然后把末尾的数据(键以及对应的值)删掉,再把最新的加上去
    • 就是当某个key被访问到了,然后程序会通过哈希表迅速定位到这个节点(时间复杂度O(1)),并将改节点调整到链表最开始的位置。
  • 最不经常使用LFU(Least Frequently Used)
    • 按照访问次数淘汰
    • 就是俩个表:键值对映射表+次数到链表的映射表(都是哈希表),以及双向链表管理同次数的key
    • 淘汰时找到最小次数的链表的末尾节点。
    • 键值对映射表:
      • 存每个 Key 对应的 Value(实际数据)、访问次数(count)最近访问时间(lastTime)(处理 “次数相同” 时的淘汰顺序)。
    • 次数到链表的映射表:
      • 相同访问次数的 Key 放在同一个双向链表中,方便快速找到 “当前次数最小的所有 Key”,以及对 Key 的访问次数变化进行分组管理。

注意:!!!!!!!!

​ 如果内存满了redis需要淘汰老数据了,默认采用的是最近最少使用LRU算法


2.过期删除

就是缓存中的数据超过了预设的有效期,然后系统自动将这些过期数据从缓存中移除的机制

关键概念

过期字典,就是redis会默认为每个数据库维护一个过期字典,存储了所有的过期时间的键。

Key:就是时间戳对应的键

Value:就是过期时间戳

有三种删除模式:

  • 定时删除主动删除
    • 核心:设置缓存的时候设置一个定时器,时间已到达就立马触发删除操作。
    • 实现:每个键值对附带一个时间戳,存入缓存时,就启动一个单独的线程或者定时器,到期后直接删除这个键。
    • 优点:过期数据立即删除,不会使内存占用超标
    • 缺点:大量的定时器会消耗很多的CPU资源(特别时缓存量很大的时候),而且定时器的创建和管理成本也很高。
  • 惰性删除
    • 核心:程序取值的时候查看该数据是否过期,如果没过期,就直接返回,过期就删除该键,并返回null
    • 优点:服务器运算资源占用小
    • 缺点:造成某些数据长期占用内存,不被删除
  • 定期删除
    • 核心:redis默认每10秒执行一下随机抽取一定数量的键(默认20个),检查并删除过期的键,条件是如果本轮的过期键比例超过25%,就会继续抽取删除,直到比例低于25%或者达到最大的执行时间(25ms)
    • 实现:(通过redis.conf配置)
      • hz:每秒执行定期删除的次数(默认是10,范围是1-500)
      • active-expire-effort:删除力度(默认是1,范围是1-10)
    • !!!:每隔一段时间,跑主动删除,不跑主动删除时,就是执行惰性删除策略

注意

Redis底层是采用惰性删除定期删除结合的方式


3.缓存一致/缓存更新

​ 指的是缓存中的数据跟数据库数据保持同步的状态,但现实是缓存数据跟数据库数据不同步一致,具体表现为:当数据库更新时,缓存中的旧数据需要被及时清理或更新,避免请求读取了脏数据。

  • 核心思想:当缓存数据有更新值,采用删除缓存数据。

  • 更新策略(核心点)

    • 延迟双删
      • 实现:先删除缓存,再更新数据库,然后延迟一段时间,再删除缓存
      • 目的:解决数据库更新未完成,缓存被加载旧数据问题
      • 注意:延迟时间要大于数据库更新的时间

image-20250513165441744

  • Read/Write Through 模式
    • 这个模式就是,应用把缓存作为主要的数据读取方式,即是避免缓存击穿。

image-20250513170012711

  • Write Behid模式
    • 实现:将更新步骤放到消息队列中,异步执行。

image-20250513170133600


4.缓存击穿

定义:大量请求查询某一个值,该值在缓存中无,在数据库中有。

场景:非常常见,因为有时候会设置数据过期时间,当数据过期就是找不到值了,所以就会发生缓存击穿

  • 场景问题

    • 热点商品抢购,如iphone16抢购,缓存存了商品的信息,小程序挤满了想要抢货的人,时间一到开抢,但是这个缓存的信息过期了 ,Redis给删了这个信息,于是大量的请求就没打到缓存上,直接请求到后端数据库上。
    • 主要问题:Redis的性能是普通关系型数据库的10-100倍,如果Redis能勉强处理的请求直接打到Mysql上,那么Mysql就处理不过来直接宕机了。
  • 解决方式

    • Mysql方面:减少击穿后的直接流量

      • 实现:直接加分布式锁(互斥锁),保证同时间只有一个线程查询数据库并更新缓存,其他线程等待缓存加载完成后再读取。

      • // 伪代码:使用Redis的setnx实现互斥锁
        String lockKey = "lock:hot_key";
        // 尝试加锁(设置过期时间防止死锁)
        if (redis.set(lockKey, "1", "NX", "EX", 10)) { 
            try {
                // 查询数据库,更新缓存
                String data = mysql.query("SELECT * FROM table WHERE id=热点Key");
                redis.set("hot_key", data, "EX", 3600); // 重新设置缓存
            } finally {
                redis.del(lockKey); // 释放锁
            }
        } else {
            // 未获取到锁,等待一段时间后重试
            Thread.sleep(100); 
            return redis.get("hot_key"); // 再次查询缓存(此时可能已被加载)
        }
        
      • image-20250513172808321

    • Redis方面:

      • 设置热点数据永不过期
      • 热点数据后台启动一个异步线程,重新把数据回填缓存层

5.缓存穿透

定义:查询一个数据库和缓存都不存在的数据,如果刚删除,本来就没有,所以叫缓存穿透

  • 解决方法
    • 1.拦截非法查询请求
    • 2.缓存空对象
    • 3.布隆过滤器
  • 具体实现
    • 拦截非法查询请求:
      • 直接在控制器中配置,直接说明非法请求
    • 缓存空对象
      • 高访问的数据,在缓存中缓存一个空对象,这样也不会直接打到数据库上。
    • 布隆过滤器
      • 是缓存穿透得前置防线,通过极低的空间成本,拦截大量的无效请求。
        • 在请求到达缓存或数据库前,通过布隆过滤器快速判断 Key 是否 “可能存在”:
          • 若判断为 “不存在”(一定正确),直接返回请求(无需查缓存 / 数据库);
          • 若判断为 “可能存在”(可能误判),继续查缓存→查数据库(此时 Key 可能真实存在)

6.缓存雪崩

定义:一大批被缓存的数据,同时失效,此时对于这一批的数据请求就全打到数据库上,导致数据库宕机。缓存击穿是单点的数据,缓存雪崩是多条数据

  • Mysql:装互斥锁,减少并发量,让请求在合理范围内

  • Redis

    • 设置热点数据不过期

    • 分析失效时间,尽量让失效时间点分散 (过期时间加随机值)

    • 缓存预热,在上线前,根据当天情况分析,将热数据直接加载到缓存系统

    • 缓存高可用方案

      • 主从+哨兵

      • 服务降级/限流

        • 降级:就是限制掉一部分服务,保全一些核心服务

          • 自动降级-熔断器模式
            • 关闭:直接调用数据库
            • 打开:当一段时间内,请求的失败率/超时率达到了一个阈值,熔断器会跳匝,进入打开状态,触发降级策略
            • 半开:打开一段时间后,自动进入半开状态,尝试放行一个请求去访问数据库,如果成功就关闭熔断器,恢复服务,如果还是失败,则继续保持打开状态。
            • 降级策略返回数据:
              • 默认值
              • 静态值/缓存值
              • 空值
              • 友好提示
          • 场景:大促期间,降级“商品评价,推介系统”等服务,保障下单,支付业务。
        • 限流:就是将并发请求数量限制在一个能够正常处理的阈值之内。超出阈值直接被拒绝,排队,引导至降级流程

          • 如何实现?(常见算法)

          1. 计数器算法 (Fixed Window Counter)

          • 原理:在固定的时间窗口(如1秒)内,对请求进行计数,如果超过阈值就拒绝请求。
          • 缺点:无法应对窗口临界点的突发流量。例如,限制每秒100次请求,可能在最后100ms和下一个窗口的前100ms涌入200个请求,依然会打垮系统。

          2. 滑动窗口算法 (Sliding Window Log)

          • 原理:是计数器算法的改进,将窗口细化成更小的区间,并滑动计算。能更好地应对突发流量,但更耗资源。
          • 例子:将1秒分为10个100ms的格子,统计当前时间前1秒内所有格子的请求总数。

          3. 漏桶算法 (Leaky Bucket)

          • 原理:请求像水一样以任意速率流入“漏桶”,而漏桶以恒定的速率向外漏出请求(进行处理)。当流入的请求超过桶的容量时,多余的请求就会被丢弃或排队。
          • 优点平滑流量,无论请求多猛烈,系统都按恒定速度处理。
          • 缺点:无法应对突发流量(即使系统有空闲,处理速率也是固定的)。

          4. 令牌桶算法 (Token Bucket) - 最常用

          • 原理:系统以一个恒定的速率向一个桶里放入“令牌”。每个请求需要先从桶中获取一个令牌,只有拿到令牌的请求才会被处理。当桶里没有令牌时,请求将被拒绝或等待。
          • 优点既能平滑流量,又能允许一定程度的突发流量。如果桶中有积攒的令牌,突发请求可以一次性拿走多个令牌进行处理,非常适合大多数业务场景。
          • 例子:网关限制某个API每秒只能被调用100次(每秒向桶中放100个令牌)。如果某一秒没有请求,桶里会攒下100个令牌,下一秒最多可以处理200个请求(100个新令牌+100个攒下的令牌),然后恢复恒定速率。

总结:降级与限流的协同作战

在面对缓存雪崩这类灾难时,降级和限流是协同工作的:

  1. 第一道防线:限流:在网关或应用入口,使用令牌桶算法等手段,将每秒进入系统的总请求数限制在数据库和服务所能承受的范围之内。多余的请求直接被快速失败(返回“系统繁忙”),避免它们冲击下游。
  2. 第二道防线:降级:对于成功通过限流关卡进入系统的请求,如果它们访问的缓存已经失效且数据库压力巨大,则通过熔断器机制,直接让这些请求走降级逻辑(返回默认值、旧缓存),绝不让他们再去压榨已经濒临崩溃的数据库

4.事务

​ Redis的事务的本质就是一组命令的集合。redis的事务就是排他性一致性顺序性地执行一个队列中的命令。

特点

无隔离级别

  • redis的事务是批量操作命令的,在发送EXEC命令前就被放入了队列的缓存,但是不会被执行,也就不会有些查询在事务内看到事务的更新,事务外的查询也不能看到。

无原子性

  • redis的单条命令是原子性的,但是事务是没有原子性的,而且没有回滚,只要执行开始,命令都会按照顺序执行下去,哪怕出错也是。

弱一致性

  • 执行过程中如果键被其他命令修改,事务仍按照原值执行

Watch命令(关键机制)

  • 用于监视一个或者多个key
  • 如果在exec命令执行前,被watchkey被其他client修改,则这个事务就会失效,会返回事务执行失败。
  • 这就是乐观锁的机制
    • 乐观锁:
      • 就是只在提交数据前做验证对应的数据是否被其他线程修改。
      • 好处:不存在锁的竞争造成堵塞,也不会有死锁。
      • 坏处:如果冲突频繁发生的话(写的占比非常多),就会频繁失败且重试,就会非常影响性能。

5.分布式锁

为了控制分布式锁的有效时长就引入redis中的redission框架,底层就是** setnxlua脚本**(保持原子性)

  • 利用Redis中的setnx命令,SET if not exists如果不存在,则set。
  • lua脚本完成加锁,设置过期时间等操作,就是为了redis加锁
    • 最主要操作就是调用redis命令来保证多条命令执行的原子性
  • 下图就是redis中使用锁的执行流程
    • redis底层有一个Watch dog的机制,来监控这个锁,当a线程拿到了锁后,当业务还没完成,锁要过期,看门狗线程就会按照设定的锁的过期时间的三分之一来续期,默认是每隔10s。

image-20250602141559897

可重入

  • 在下面add2();操作中是可以执行的意味着锁是可以重入的。
    • 判断逻辑:先是判断是不是同一个线程ID,不是同一个线程ID就是可以重入
    • 应用场景:业务复杂,避免多个锁产生相互死锁,
    • 结构:用hash结构记录线程id和重入次数

image-20250602145910572

image-20250602145358722

一致性

  • 上面可重入问题中不能确保主从复制时锁的一致性,所以redis中有RedLcok
  • RedLock:不能只在一个redis实例上创建锁,就是要在多个redis上创建(n/2+1)个锁。
    • 但是官方不建议使用这个方法,因为实现太复杂了,而且高并发场景下性能特别差,多个redis节点的存在运维起来也特别繁琐。

image-20250602152202514

  • 真正为了保持一致性:
    • AP思想:redis,指的是保证高可用性。
    • CP思想:zookeeper,引入它的分布式锁来真正保证数据强一致性。

6.集群

​ 以上从基本数据类型到分布式锁的都是单机运行Redis,这节讲的是,在好几台机器上运行Redis是怎么样的(称为集群)。

1.主从复制

image-20250513204105381

  • ①首先就是三次握手通信(采用TCP协议),主库和从库要想连接就得知道双方的IP和端口号,当主库和从库上线后就进行握手,然后信息验证。
  • ②从库发送PSYNC命令:即是同步命令,开启数据同步过程,发送主库的运行ID和复制进度的偏移量Offset
  • ③握完手开始进入数据同步阶段,有俩种模式:
    • 一是全量复制(全量同步)
      • why:从库第一次连接这个主库(ID不匹配或offset=-1),就会触发全量同步
        • step1:主库生成RDB快照文件(二进制格式),生成RDB快照的同时会将所有新的写命令记录到复制积压缓冲区(用于后面的增量同步),RDB生成完毕通过网络传输到从库
        • step2:从库接收到RDB文件后,直接清除自身当前所有数据,加载RDB文件,将数据写入内存,恢复主库的全量数据状态。
        • step3:RDB加载完毕后,主库将复制积压缓冲区中的写命令(RDB生成以及从库运行RDB文件期间产生的)发送给从库,从库就执行这些命令,确保和主节点数据完全一致,至此全量同步完成。
    • 二是增量同步
      • why:从库和主库曾经连接过(ID匹配而且offset有效),且主库的复制积压缓冲区保留着从库的偏移量,才会触发增量同步。
        • step1:从库发送PSYNC <master_run_id> <offset>命令自报偏移量。
          • 这里注意!!!
          • 主节点检查复制积压缓冲区中是否包含该偏移量之后的命令:
            • 若包含(偏移量在缓冲区范围内)→ 进入增量步;
            • 若不包含(偏移量已被覆盖)→ 触发全量同步(因缓冲区是环形结构,旧命令会被新命令覆盖)。
        • step2:主库从复制积压缓冲区中提取从库偏移量(offset)之后的所有写命令(如 offset=1000offset=2000 的命令),将这些命令发送给从库,从库执行后即可追上主库的最新状态。

主从复制完毕!!注意 :

无论是全量还是增量同步完成后,主从节点会进入持续增量同步阶段,确保后续数据一致!!!

复制积压缓冲区是一个先进先出的队列,存储的是最近主库的数据修改命令

  • 下图是redis断线后的重连机制

image-20250514094030292

2.哨兵模式

主从模式保证不了redis的高可用,因为谁也不知道什么时候主库崩了或者从库崩了或者大家一起崩了,这样前面主从模式配置的一切都用不上了。

  • 前提Redis中提供了哨兵(Sentinel)机制来监控主从集群以及实现主从集群的自动故障恢复。
  • 哨兵(Sentinel)三个作用加一种情况如下
    • 监控:Sentinel基于心跳机制监测状态,默认每隔1s去发送一个PING命令去给每一个节点。
      • 没回应PONG:就是在Sentinel规定的时间内没有得到回应,则发命令的Sentinel主观认为这个节点下线,为主观下线
      • 都没回应:当一个Sentinel发现某个节点主观下线后,就发通知给其他的Sentinel,然后其他的Sentinel也发PING命令给该节点,如果超过指定数量quorum(默认quorum为Sentinel实例的一半),就会被认为客观下线
    • 选举和恢复故障,通知:如果master故障,Sentinel会将一个slave提升为master,故障恢复还是以新的为主。
      • 选择:先是比较优先级(slave-priority)谁高,再是比较谁的offset大,最后比较ID谁靠前,最重要的是比较offset偏移量。
      • 选择后:哨兵向新节点发送SLAVEOF NO ONE命令,让此节点成为主节点,然后通知其他的从节点SLAVEOF命令,从新的主节点开始数据同步,同时也会更新配置文件和内存的数据结构,通知客户端让客户端连接新的主节点。
    • 脑裂:当主节点有三个从节点,而有俩个从节点因为网络原因跟主节点和另一个从节点分离,误判主节点下线,触发不必要的故障转移,原主节点的写操作未同步到新主节点,导致数据丢失。
      • 防止此情况配置
        • min-replicas-to-write 1:表示最少salve节点为1个
        • min-replicas-max-lag 5:表示数据复制和同步的延迟不能超过五秒

3.分片集群

主从复制和哨兵模式可以解决高可用高并发读的问题,但是还有俩个问题:海量数据存储问题高并发写的问题

  • 特征作用
    • 多个master,每个master保存不同数据
    • 每个master有多个slave节点
    • master通过ping监测彼此健康状态
    • 客户端请求可以访问集群任意节点,最后都会转发到正确的节点
  • 存储和读取
    • Redis分片集群中引入了哈希槽的概念,一共有16384个哈希槽。
    • Redis集群将16384个插槽分配到不同的实例
    • 读写数据:根据key的有效部分计算哈希值,对16384取余(有效部分,就是如果key前面有大括号,那么大括号内的即是有效部分,如果没有,则是key本身为有效部分)余数作为插槽,寻找插槽所在的实例。

7.为什么redis

  • why:redis是单线程的,为什么还这么快
    • Redis是纯内存操作,执行速度快,0磁盘IO延迟,访问速度比磁盘快10万倍。
    • 采用单线程,避免不必要的上下文切换可竞争条件,所有操作都是原子性。
    • 使用I/O多路复用模型,非阻塞IO

I/O多路复用模型

实现高效的网络请求

  • 用户空间和内核空间
  • 常见的IO模型
    • 阻塞IO(Blocking IO)
    • 非阻塞IO(Nonblocking IO)
    • IO多路复用(IO Multiplexing)
  • Redis网络模型