• 2.redis缓存数据库学习


    学习思维导图

    在这里插入图片描述

    在这里插入图片描述

    重要参考连接

    redis命令大全
    中文命令说明,通俗易懂
    官网英文命令,权威全面,且美观,查询方便

    reids客户端工具推荐

    重要知识点记录

    🏐缓存问题

    🥏缓存穿透

    查询的结果在数据库中不存在,所以也不会写入到缓存,这样如果一直查询这个不存在的结果,缓存就会一直处于失效状态,这样就是一个可攻击的漏洞,增大访问量,数据库就瘫痪了。

    解决办法

    • 请求处理的时候过滤掉请求值明显不符合规范的请求
    • 对于不存在的请求也要生成对应的缓存
    • 使用布隆过滤器(一直数据结构,查询很快,o(1)级别)把这种查询加入黑名单,直接过滤掉

    🥏缓存击穿

    缓存击穿,就是查询的缓存数据突然失效了,过期时间到了,这时候上来一阵大的访问量,来不及生成缓存,导致数据库崩溃。

    解决办法

    • 热点数据持久化
    • 代码做服务限流,熔断和降级
    • 访问缓存模拟锁,第一次查询没有数据,构建key和value设置值为0,这个线程去查询数据,其他线程过来看到值value为0,就等待重试。

    🥏缓存雪崩

    缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

    解决办法

    • 构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等)
    • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
    • 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
    • 设置热点数据永远不过期。
    • 记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。

    🥏缓存污染(或者满了)

    缓存污染问题说的是缓存中一些只会被访问一次或者几次的的数据,被访问完后,再也不会被访问到,但这部分数据依然留存在缓存中,消耗缓存空间。
    缓存污染会随着数据的持续增加而逐渐显露,随着服务的不断运行,缓存中会存在大量的永远不会再次被访问的数据。缓存空间是有限的,如果缓存空间满了,再往缓存里写数据时就会有额外开销,影响Redis性能。这部分额外开销主要是指写的时候判断淘汰策略,根据淘汰策略去选择要淘汰的数据,然后进行删除操作。

    解决办法

    • 选用合适的淘汰策略
    • 减少不必要数据的缓存
    • 增大缓存 我会建议把缓存容量设置为总数据量的 15% 到 30%,兼顾访问性能和内存空间开销.
    🏑缓存淘汰策略

    Redis共支持八种淘汰策略,分别是
    noevictionvolatile-ttlvolatile-randomvolatile-lruvolatile-lfuallkeys-lruallkeys-randomallkeys-lfu 策略。

    noeviction

    • 该策略是Redis的默认策略。在这种策略下,一旦缓存被写满了,再有写请求来时,Redis 不再提供服务,而是直接返回错误。这种策略不会淘汰数据,所以无法解决缓存污染问题。一般生产环境不建议使用。 其他七种规则都会根据自己相应的规则来选择数据进行删除操作。

    volatile-ttl

    • 这种算法判断淘汰数据时参考的指标比随机删除时多进行一步过期时间的排序。Redis在筛选需删除的数据时,越早过期的数据越优先被选择。

    volatile-random

    • 这个算法比较简单,在设置了过期时间的键值对中,进行随机删除。因为是随机删除,无法把不再访问的数据筛选出来,所以可能依然会存在缓存污染现象,无法解决缓存污染问题。

    volatile-lru LRU算法:

    • LRU 算法的全称是 Least Recently Used,按照最近最少使用的原则来筛选数据。这种模式下会使用 LRU 算法筛选设置了过期时间的键值对。 Redis优化的LRU算法实现: Redis会记录每个数据的最近一次被访问的时间戳。在Redis在决定淘汰的数据时,第一次会随机选出 N 个数据,把它们作为一个候选集合。接下来,Redis 会比较这 N 个数据的 lru 字段,把 lru 字段值最小的数据从缓存中淘汰出去。通过随机读取待删除集合,可以让Redis不用维护一个巨大的链表,也不用操作链表,进而提升性能。 Redis 选出的数据个数 N,通过 配置参数 maxmemory-samples 进行配置。个数N越大,则候选集合越大,选择到的最久未被使用的就更准确,N越小,选择到最久未被使用的数据的概率也会随之减小。

    volatile-lfu

    • 会使用 LFU 算法选择设置了过期时间的键值对。 LFU 算法:LFU 缓存策略是在 LRU 策略基础上,为每个数据增加了一个计数器,来统计这个数据的访问次数。当使用 LFU 策略筛选淘汰数据时,首先会根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出缓存。如果两个数据的访问次数相同,LFU 策略再比较这两个数据的访问时效性,把距离上一次访问时间更久的数据淘汰出缓存。
      Redis的LFU算法实现: 当 LFU 策略筛选数据时,Redis 会在候选集合中,根据数据 lru 字段的后 8bit 选择访问次数最少的数据进行淘汰。当访问次数相同时,再根据 lru 字段的前 16bit 值大小,选择访问时间最久远的数据进行淘汰。 Redis 只使用了 8bit 记录数据的访问次数,而 8bit 记录的最大值是 255,这样在访问快速的情况下,如果每次被访问就将访问次数加一,很快某条数据就达到最大值255,可能很多数据都是255,那么退化成LRU算法了。所以Redis为了解决这个问题,实现了一个更优的计数规则,并可以通过配置项,来控制计数器增加的速度。
      参数 :
      • lfu-log-factor ,用计数器当前的值乘以配置项 lfu_log_factor 再加 1,再取其倒数,得到一个 p 值;然后,把这个 p 值和一个取值范围在(0,1)间的随机数 r 值比大小,只有 p 值大于 r 值时,计数器才加 1。
      • lfu-decay-time, 控制访问次数衰减。LFU 策略会计算当前时间和数据最近一次访问时间的差值,并把这个差值换算成以分钟为单位。然后,LFU 策略再把这个差值除以 lfu_decay_time 值,所得的结果就是数据 counter 要衰减的值。
      • lfu-log-factor设置越大,递增概率越低,lfu-decay-time设置越大,衰减速度会越慢。 我们在应用 LFU 策略时,一般可以将 lfu_log_factor 取值为 10。
        如果业务应用中有短时高频访问的数据的话,建议把 lfu_decay_time 值设置为 1。可以快速衰减访问次数。 volatile-lfu 策略是 Redis 4.0 后新增。

    allkeys-lru

    • 使用 LRU 算法在所有数据中进行筛选。具体LFU算法跟上述 volatile-lru 中介绍的一致,只是筛选的数据范围是全部缓存,这里就不在重复。

    allkeys-random

    • 从所有键值对中随机选择并删除数据。volatile-random 跟 allkeys-random算法一样,随机删除就无法解决缓存污染问题。

    allkeys-lfu

    • 使用 LFU 算法在所有数据中进行筛选。具体LFU算法跟上述 volatile-lfu 中介绍的一致,只是筛选的数据范围是全部缓存,这里就不在重复。 allkeys-lfu 策略是 Redis 4.0 后新增。

    🥏缓存一致性

    缓存一致性问题,就是数据更新之后,缓存也需要更新,以确保数据缓存一致性。
    目前有四种缓存更新策略Cache Aside Pattern,Read/Write Through Pattern,Write Behind Caching Pattern

    Cache Aside Pattern

    1. 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
    2. 命中:应用程序从cache中取数据,取到后返回。
    3. 更新:先把数据存到数据库中,成功后,再让缓存失效。

    Read/Write Through Pattern

    • Read Through 套路就是在查询操作中更新缓存,也就是说,当缓存失效的时候(过期或LRU换出),Cache Aside是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对应用方是透明的。
    • Write Through 套路和Read Through相仿,不过是在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)
      也就是缓存的操作都被第三方代理了,写程序的时候不用自己定义这个缓存更新逻辑了,比如mybaits二级缓存

    Write Behind Caching Patter

    • Write Back套路,一句说就是,在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作飞快无比(因为直接操作内存嘛 ),因为异步,write backg还可以合并对同一个数据的多次操作,所以性能的提高是相当可观的。

    🏐事务

    redis的事务是先放入队列,然后依次执行的,执行过程是隔离的,不会被其他线程打断,如果遇到错误,后面的任务依然执行,不会回滚(redis事务执行过程中是不会出现错误的,除非你程序写错了,程序写错了,回滚也没意义)

    redis的事务不支持回滚,需要借助锁实现整个事务的回滚,也就是乐观锁,可以保证事务的回滚。
    事务回滚了,就不会重试了,根java voliatle关键字一样这就产生一些问题,比如秒杀系统,1000个人抢1000个库存,因为这时候访问时并发的,不同的事务也在并发执行,使用了乐观锁,所以不会阻塞,所以会出现大量抢到资源,但是操作慢了被别的操作修改值而产生回滚,最终抢占失败,库存还有剩余。

    对于事务的线程安全问题说明

    • redis不存在线程安全问题,线程安全问题是由于多线程并发抢夺公共资源导致的,解决这种问题的方式是通过加锁,把线程放入队列依次执行来解决的。而redis本身就是单线程模型,所有操作自动排队。

    但是两个个问题

    • 虽然任务是排队执行的,但是资源没有锁定,同一个客户端的不同操作可能排队的顺序不一样导致操作被分开插入别的操作了。

      • 采用乐观锁,进行事务回滚
      • 使用事务,把一个客户端的操作隔离,实现原子化。
    • 虽然加了锁watch到数据了,但是高并发场景下,多个线程watch到同一个版本的值了,虽然,后面的事务会依次提交,但是最后进行版本判断的时候只有第一个事务会通过验证,其他事务全部失败

      • 使用Lua脚本,原子操作,自带回滚,不需要锁

    谈事务就会谈到事务的ACID原则
    reids对acid原则的适配情况如下:

    • 原子性atomicity:Redis的事务是原子性的:所有的命令,要么全部执行,要么全部不执行,单位还是命令而不是事务但是可以通过watch加锁实现回滚
    • 一致性consistency:redis事务可以保证命令失败的情况下得以回滚,数据能恢复到没有执行之前的样子,是保证一致性的,除非redis进程意外终结。通过加锁或者Lua脚本实现
    • 隔离性Isolation:redis事务是严格遵守隔离性的,原因是redis是单进程单线程模式(v6.0之前),可以保证命令执行过程中不会被其他客户端命令打断。但是,Redis不像其它结构化数据库有隔离级别这种设计。
    • 持久性Durability:redis事务是不保证持久性的,这是因为redis持久化策略中不管是RDB还是AOF都是异步执行的,不保证持久性是出于对性能的考虑。

    🥏事务中的锁

    • 关系型数据库中,事务中的锁保证的事务的隔离性,可以独享资源进行原子操作,而事务的原子性回滚通过undo和redo日志保证。
    • redis中事务的原子性通过事务队列和本身操作或者lua脚本来保证,底层通过单线程配合保证事务执行是原子的不能被插入别的操作,而回滚是通过乐观锁来实现的,这个锁的功能和关系数据库就明显不同了,正常加锁是为了独享资源进行成功的操作,而这里加锁虽然是为了的独享资源,但是发现独享失败后,操作也随之丢掉

    🏐redis事件机制

    redis的事件机制和Netty的事件机制类似,都是采用了NIO的模式实现的。具体参考如下链接:
    Redis事件机制原理

    🏐redis哨兵机制

    在上文主从复制的基础上,如果注节点出现故障该怎么办呢? 在 Redis 主从集群中,哨兵机制是实现主从库自动切换的关键机制,它有效地解决了主从复制模式下故障转移的问题。

    • 哨兵是集群分布的
    • 哨兵个数要是奇数个,方便投票选举新的主节点
    • 选举新的主节点后,旧节点恢复了也不会再次成为主节点

    哨兵机制的实现和原理
    实现:

    • 哨兵机制是给一种分布式的结构,可以部署到其他的服务器和redis分开,也可以放在一起,配置的时候只需要给主服务器节点配置哨兵即可。

    原理:

    • 通过redis的订阅发布机制实现的。

    下线确认

    • 主观下线:任何一个哨兵都是可以监控探测,并作出Redis节点下线的判断;
    • 客观下线:有哨兵集群共同决定Redis节点是否下线;

    主从切换

    • 为了避免哨兵的单点情况发生,所以需要一个哨兵的分布式集群。作为分布式集群,必然涉及共识问题(即选举问题);同时故障的转移和通知都只需要一个主的哨兵节点就可以了。
    • 哨兵的选举机制其实很简单,就是一个Raft选举算法: 选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举
    • 进行转移
      • 在这里插入图片描述

    🏐redis集群(数据分片)

    集群就是使用一个以上是数据库或者应用来配合完成工作的软件组织方式。集群分为两种组织形式,一种是主从复制下的一主多从,另一种是多主多从(多个主从集群,主节点之间分片管理不同范围的数据(使用hash slot来分配默认2的14次方16384个)(主从节点之间异步复制))

    • 主从复制和哨兵机制保障了高可用,就读写分离而言虽然slave节点扩展了主从的读并发能力,但是写能力和存储能力是无法进行扩展,就只能是master节点能够承载的上限。如果面对海量数据那么必然需要构建master(主节点分片)之间的集群,同时必然需要吸收高可用(主从复制和哨兵机制)能力,即每个master分片节点还需要有slave节点,这是分布式系统中典型的纵向扩展(集群的分片技术)的体现;所以在Redis 3.0版本中对应的设计就是Redis Cluster。
      参考链接
    • 不同的主节点分片之间通过消息总线进行服务器通信的,Redis6之后对集群优化,产生了集群代理,由集群代理统一进行集群数据调度

    🥌redis优化

    redis优化过程如下:对指标进行监控指标可视化指标预警优化策略

    监控指标
    服务器系统数据采集(CPU,内存,磁盘,网络)
    Redis Server数据采集(Redis慢查询,Redis持久化,Redis复制,Redis连接数,Key命中,内存,存活)
    Redis响应时间数据采集
    Redis监控Screen

    监控方案
    采集指标来源: redis-export
    收集管道:fluentd
    DB: Prometheus
    view和告警: Grafana及插件

    性能调优
    在这里插入图片描述

    redis的对象机制

    Redis 必须让每个键都带有类型信息, 使得程序可以检查键的类型, 并为它选择合适的处理方式.
    所以redis就是让每给key或者value都遵循redisObject数据结构。记录每个key的类型,编码方式,和底层实现,以及一些最新访问时间
    请添加图片描述
    redis的对象机制图
    在这里插入图片描述

  • 相关阅读:
    源码选择指南:比较流行的同城外卖跑腿系统解决方案
    点云从入门到精通技术详解100篇-面向无人驾驶的三维点云目标检测
    Netty 入门
    哈希索引和自适应哈希索引
    linux下Nerdtree安装方法
    SQL命令及MariaDB(一)
    数据结构和算法(6):图
    CV (3)- Loss Functions and Optimization
    玩具机器人脚本适合场景
    基于SSM实现毕业设计管理系统
  • 原文地址:https://blog.csdn.net/qq_37771209/article/details/126530308