Redis事务(不好用,后续用lua脚本)
    Redis事务是基于队列实现的,创建一个事务队列,然后将事务操作都放入队列中,最后依次执行。
    image.png

    1. #开启事务
    2. multi
    3. #添加命令
    4. sadd user:1001:follow 1002
    5. sadd user:1002:follow 1001
    6. sadd user:1001:fans 1002
    7. sadd user:1002:fans 1002
    8. #执行事务
    9. exec
    10. # 取消事务
    11. discard

    事务处理机制:
    语法错误(编译):执行命令的语法不正确。
    例:
    image.png
    执行错误(运行):命令在运行过程中出现错误
    例:
    image.png
    SpringBoot实现事务操作:
    1)修改RedisConfig配置类,开启事务控制
    //开启redis事务控制
    redisTemplate.setEnableTransactionSupport(true);
    2)自定义方法事务测试

    @Test
    @Transactional(rollbackFor = Exception.class)
    public void multiTest(){
        //开启事务
        redisTemplate.multi();
        try{
            redisTemplate.opsForValue().set("lesson","java");
            redisTemplate.opsForSet().add("lesson","eureka","feign","gateway");
            redisTemplate.opsForValue().set("lesson","redis");
            System.out.println(redisTemplate.opsForValue().get("lesson"));
        }catch (Exception e){
            //回滚
            System.out.println("出现异常");
            redisTemplate.discard();
        }finally {
            redisTemplate.exec();
        }
    }
    

    持久化机制:
    Redis将数据保存在内存中。一旦服务器宕机重启,内存中的数据就会丢失。当出现这种情况后,为了能够让Redis进行数据恢复,将内存中的数据保存到磁盘中,避免数据意外丢失,Redis提供了两种持久化机制:RDBAOF
    RDB快照:
    RDB(Redis DataBase)是Redis默认存储方式。其基于快照思想,当符合一定条件(手动或自动触发)时,Redis会将这一刻的内存数据进行快照并保存在磁盘上,产生一个经过压缩的二进制文件,文件后缀名.rdb。
    image.png
    RDB触发条件:
    一、 在redis.conf文件中配置了一些默认触发机制。

    save ""  # 不使用RDB存储  不能主从
    
    # 记忆
    save 3600 1     #表示1小时内至少1个键被更改则进行快照。
    save 300 100    #表示5分钟(300秒)内至少100个键被更改则进行快照。
    save 60 10000  #表示1分钟内至少10000个键被更改则进行快照。
    
            **二、**在redis客户端执行save或bgsave命令,手动触发RDB快照。
    
    #进入客户端
    bin/redis-cli
    
    #执行save命令(同步执行)
    save
    
    #执行bgsave命令(异步子线程执行)
    bgsave
    
    区别:
    
    • save:同步处理,阻塞Redis服务进程,服务器不会处理任何命令,直到RDB文件保存完毕。
    • bgsave:会fork一个和主线程一致的子线程负责操作RDB文件,不会阻塞Redis服务进程,操作RDB文件的同时仍然可以处理命令。

    Redis默认使用的是 bgsave 来保存快照数据。
    image.png
    优点:

    • 基于二进制文件完成数据备份,占用空间少,便于文件传输。
    • 能够自定义规则,根据Redis繁忙状态进行数据备份。

    缺点:

    • 无法保证数据完整性,会丢失最后一次快照后的所有数据。
    • bgsave执行每次执行都会阻塞Redis服务进程创建子线程,频繁执行影响系统吞吐率。

    AOF:
    当开启AOF持久化后,Redis会将客户端发送的所有更改数据的命令,记录到磁盘中的AOF文件。 这样的话,当Redis重启后,通过读取AOF文件,按顺序获取到记录的数据修改命令,即可完成数据恢复。
    image.png
    AOF功能实现的整个执行过程可以分为三个部分:命令追加文件写入文件同步
    image.png
    何时将AOF文件同步到磁盘的策略依据就是redis.conf文件中appendfsync属性值:alwayseverysecno
    image.png
    AOF重写优化:
    AOF会将对Redis操作的所有写命令都记录下来,随着服务器的运行,AOF文件内保存的内容会越来越多。这样就会造成两个比较严重的问题:占用大量存储空间数据还原花费的时间多
    为了解决AOF文件巨大的问题,Redis提供了AOF文件重写功能。 当AOF文件体积超过阈值时,则会触发AOF文件重写,Redis会开启子线程创建一个新的AOF文件替代现有AOF文件。 新的AOF文件不会包含任何浪费空间的冗余命令,只存在恢复当前Redis状态的最小命令集合。
    通过修改redis.conf进行重写阈值的配置:

    #当前aof文件大小超过上一次aof文件大小的百分之多少时进行重写。如果之前没有重写过,以
    启动时aof文件大小为准
    auto-aof-rewrite-percentage 100
    
    #限制允许重写最小aof文件大小,也就是文件大小小于64mb的时候,不需要进行优化
    auto-aof-rewrite-min-size 64mb
    
    手动让其进行执行重写:bgrewriteaof
    

    RDB与AOF对比

         1. RDB默认开启,AOF需手动开启。
         1. RDB性能优于AOF。
         1. AOF安全性优于RDB。
         1. AOF优先级高于RDB。
         1. RDB存储某个时刻的数据快照,AOF存储**写**命令。
         1. RDB在配置触发状态会丢失最后一次快照以后更改的所有数据,AOF默认使用everysec,每秒保存一次,最多丢失两秒以内的数据。
    

    生产环境下持久化实践

         1. 如当前只追求高性能,不关注数据安全性,则关闭RDB和AOF,如redis宕机重启,直接从数据源恢复数据。
         1. 如需较高性能且关注数据安全性,则开启RDB,并定制触发规则。
         1. 如更关注数据安全性,则开启AOF。
         1. 黑马头条  选择 即开启AOF  也开启RDB
    

    高可用:
    主从复制:
    image.png image.png
    读写分离,使主写分读。
    持久化优化:
    master和所有的slave都开启持久化的话,性能相对来说比较低。我们可以在从节点上开启持久化、在主节点关闭持久化

      - 当从节点宕机重启后,主节点会自动的将数据同步到从节点上。所以不会出现数据丢失。
      - 当主节点宕机后,可以将从节点提升为主节点(**slaveof no one**),继续对外提供服务。 并且当原先的主节点重启后,使用slaveof命令将其设置为新主节点的从节点,即可完成数据同步。
    

    总结:
    image-20210624164716647.png
    具体步骤:
    1、Slave服务启动,主动连接Master,并发送SYNC命令,请求初始化同步;
    2、Master收到SYNC后,执行BGSAVE命令生成RDB文件,并缓存该时间段内的写命令;
    3、Master完成RDB文件后,将其发送给所有Slave服务器;
    4、Slave服务器接收到RDB文件后,删除内存中旧的缓存数据,并装载RDB文件;
    5、Master在发送完RDB后,即刻向所有Slave服务器发送缓存中的写命令;

    主从复制的作用:

    • 读写分离:主写从读,提高服务器的读写负载能力
    • 负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数量,通过多个从节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量
    • 故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复
    • 数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式
    • 高可用基石:基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案

    哨兵模式:
    哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master。
    image-20210621144247782.png
    注意:

      -  哨兵也是一台redis服务器,只是不提供数据服务 
      -  通常哨兵配置数量为单数 
      -  如果配置启用单节点哨兵,如果有哨兵实例在运行时发生了故障,主从库无法正常<br />切换啦,所以我们需要搭建 **哨兵集群** 
    

    image-20210624165106048.png

    • Redis提供了哨兵的命令,是一个独立的进程
    • 原理 哨兵通过发送命令给多个节点,等待Redis服务器响应,从而监控运行的多个Redis实例的运行情况
    • 当哨兵监测到master宕机,会自动将slave切换成master,通过通知其他的从服务器,修改配置文件切换主机

    Sentinel三大工作任务

    • 监控(Monitoring)
      • Sentinel 会不断地检查你的主服务器和从服务器是否运作正常
    • 提醒(Notification)
      • 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知
    • 自动故障迁移(Automatic failover)
      • 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器
      • 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器

    客观下线和主观下线

    • 主观下线(Subjectively Down, 简称 SDOWN)
      • 哨兵进程会使用 PING 命令检测它自己和主、从库的网络连接情况,用来判断实例的状
        。如果哨兵发现主库或从库对 PING 命令的响应超时了,那么,哨兵就会先把它标记
        为“主观下线”
      • 一个服务器没有在 down-after-milliseconds 选项所指定的时间内, 对向它发送 PING 命令的 Sentinel 返回一个有效回复(valid reply), 那么 Sentinel 就会将这个服务器标记为主观下线
      • 如果检测的是从库,那么,哨兵简单地把它标记为“主观下线”就行了,因为从库的下线影响一般不太大,集群的对外服务不会间断。
      • 如果检测的是主库,那么,哨兵还不能简单地把它标记为“主观下线”,开启主从
        切换。因为很有可能存在这么一个情况:那就是哨兵误判了,其实主库并没有故障。可
        是,一旦启动了主从切换,后续的选主和通知操作都会带来额外的计算和通信开销。

    我们要知道啥叫误判。很简单,就是主库实际并没有下线,但是哨兵误以为它下线了。误判一般会发生在集群网络压力较大、网络拥塞,或者是主库本身压力较大的情况下。

    • 客观下线(Objectively Down, 简称 ODOWN)

    image-20210624170202319.png

    • 指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断
    • 一个 Sentinel 可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线
    • 客观下线条件只适用于主服务器
      • 仲裁 qurum
    • Sentinel 在给定的时间范围内, 从其他 Sentinel 那里接收到了【足够数量】的主服务器下线报告, 那么 Sentinel 就会将主服务器的状态从主观下线改变为客观下线
    • 这个【足够数量】就是配置文件里面的值,一般是Sentinel个数的一半加1,比如3个Sentinel则就设置为2
    • down-after-milliseconds 是一个哨兵在超过规定时间依旧没有得到响应后,会自己认为主机不可用
    • 当拥有认为主观下线的哨兵达到sentinel monitor所配置的数量时,就会发起一次投票,进行failover

    基于pub/sub的客户端事件通知

    • 主库下线事件:
      • +sdown:实例进入主观下线状态
      • -sdown:实例退出主观下线状态
      • +odown:实例进入客观下线状态
      • -odown:实例退出客观下线状态
    • 从库重新配置事件
      • +slave-reconf-sent:哨兵发送SLAVEOF命令重新配置从库
      • +slave-reconf-inpprog:从库配置了新主库,但尚未进行同步
      • +slave-reconf-done:从库配置了新主库,且和新主库完成同步
    • 新主库切换:
      • +swith-master:主库地址发送变化

    高可扩-Redis Cluster分片集群: Redis Cluster 分片集群在Redis 3.0 之后开始支持。
    Redis原理:

    • 数据切片和实例的对应分布关系
      • Redis Cluster 方案:无中心化
        • 采用哈希槽(Hash Slot)来处理数据和实例之间的映射关系
        • 一个切片集群共有 16384 个哈希槽,只给Master分配
        • 具体的映射过程
          1. 根据键值对的 key,按照CRC16 算法计算一个 16 bit 的值;
          2. 再用这个 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽
      • 哈希槽映射到具体的 Redis 实例上
        • 用 cluster create 命令创建集群,Redis 会自动把这些槽平均分布在集群实例上
        • 也可以使用 cluster meet 命令手动建立实例间的连接,形成集群,再使用 cluster addslots 命令,指定每个实例上的哈希槽个数
          注意:需要把 16384 个槽都分配完,否则 Redis 集群无法正常工作
    • 客户端如何定位数据
      • Redis 实例会把自己的哈希槽信息发给和它相连接的其它实例,来完成哈希槽分配信息的扩散
      • 客户端和集群实例建立连接后,实例就会把哈希槽的分配信息发给客户端
      • 客户端会把哈希槽信息缓存在本地。当请求键值对时,会先计算键所对应的哈希槽
      • 但集群中,实例和哈希槽的对应关系并不是一成不变的
        • 实例新增或删除
        • 负载均衡
      • 实例之间可以通过相互传递消息,获得最新的哈希槽分配信息,但客户端是无法主动感知这些变化
    • 重定向机制
      • 如果实例上没有该键值对映射的哈希槽,就会返回 MOVED 命令
        • 客户端会更新本地缓存
      • 迁移部分完成情况下,返回ASK
        • 表明 Slot 数据还在迁移中
        • ASK 命令把客户端所请求数据的最新实例地址返回给客户端
        • 并不会更新客户端缓存的哈希槽分配信息

    集群常用命令:

    CLUSTER INFO   // 打印集群的信息
    CLUSTER NODES  // 列出集群当前已知的所有节点(node),以及这些节点的相关信息。
    
    //节点
    CLUSTER MEET    // 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。
    CLUSTER FORGET  // 从集群中移除 node_id 指定的节点。
    CLUSTER REPLICATE   // 将当前节点设置为 node_id 指定的节点的从节点。
    CLUSTER SAVECONFIG  // 将节点的配置文件保存到硬盘里面。
    CLUSTER ADDSLOTS  [slot ...]  // 将一个或多个槽(slot)指派(assign)给当前节点。
    CLUSTER DELSLOTS  [slot ...]  // 移除一个或多个槽对当前节点的指派。
    CLUSTER FLUSHSLOTS // 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
    CLUSTER SETSLOT  NODE  // 将槽 slot 指派给 node_id 指定的节点。
    CLUSTER SETSLOT  MIGRATING  // 将本节点的槽 slot 迁移到 node_id 指定的节点中。
    CLUSTER SETSLOT  IMPORTING  // 从 node_id 指定的节点中导入槽 slot 到本节点。
    CLUSTER SETSLOT  STABLE // 取消对槽 slot 的导入(``import``)或者迁移(migrate)。
    //键
    CLUSTER KEYSLOT  // 计算键 key 应该被放置在哪个槽上。
    CLUSTER COUNTKEYSINSLOT  // 返回槽 slot 目前包含的键值对数量。
    CLUSTER GETKEYSINSLOT    // 返回 count 个 slot 槽中的键。
    //新增
    CLUSTER SLAVES node-id  // 返回一个master节点的slaves 列表
    

    集群可以做动态扩缩容

    key过期删除策略
    一、定时删除:持续关注key的时间,到期删除
    image.png
    该策略对内存空间足够友好, 但对CPU非常不友好,会拉低系统性能,因此不建议使用。
    二、惰性删除:不持续关注key的过期时间, 而是在获取key时,才会检查key是否过期,如果过期则删除该key
    image.png
    惰性删除对CPU足够友好,但是对内存空间非常不友好,会造成大量内存空间的浪费
    三、定时+惰性:
    image.png

    • 默认每秒运行10次会对具有过期时间的key进行一次扫描,但是并不会扫描全部的key,因为这样会大大延长扫描时间。
    • 每次默认只会随机扫描20个key,同时删除这20个key中已经过期的key。
    • 如果这20个key中过期key的比例达超过25%,则继续扫描。

    修改redis.conf中的hz参数,改变每秒扫描10次这个默认值
    官方建议不要超过100,否则会对CPU造成比较大的压力。

    内存淘汰策略
    当有一些已经过期的key,定期扫描一直都没有扫描到它,而且这些key也一直没有被使用。 那么它们就会一直在内存中存在。同时继续向Redis不断插入新数据,最终造成内存空间不足的问题。此时就用到了内存淘汰策略
    一、修改redis.conf中的maxmemory 来设置最大内存
    在64位操作系统中,如果未设置或设置0,代表无限制。而在32位系统中,默认内存大小为3GB。但是实际生产环境下,一般会设置物理内存的四分之三左右。
    二、当客户端执行命令,添加数据时,Redis会检查内存空间大小,如超过最大内存,则触发内存淘汰策略
    image.pngimage.png
    对于LRUTTL相关策略,每次触发时,redis会默认从5个key中一个key符合条件的key进行删除。如果要修改的话,可以修改redis.confmaxmemory-samples属性值
    策略设置:redis默认使用noeviction,我们可以通过修改redis.confmaxmemory-policy属性值设置不同的内存淘汰策略。
    不同策略的使用场景
    1、Redis只做缓存,不做DB持久化,使用allkeys。如状态性信息,经常被访问,但数据库不会修改。
    2、同时用于缓存和DB持久化,使用volatile。如商品详情页。
    3、存在冷热数据区分,则选择LRU或LFU。如热点新闻,热搜话题等。
    4、每个key被访问概率基本相同,选择使用random。如企业内部系统,访问量不大,删除谁对数据库也造成太大压力。
    5、根据超时时间长久淘汰数据,选择选用ttl。如微信过期好友请求。