redis 回收算法
介绍
redis是会在数据达到一定程度之后,超过了一个最大的限度之后,就会将数据进行一定的清理,从内存中清理掉一些数据<br>Redis默认情况下就是使用LRU策略的,因为内存是有限的<br>LRU:least Recently userd 最近最少使用算法<br>将最近一段时间内,最少使用的一些数据,给干掉,比如说有一个key,在最近1个小时内,只被访问了一次,还有一个key在最近1个小时内,被访问了1万次
缓存清理设置
maxmermory,设置redis用来存放数据的最大的内存大小,一旦超出这个内存大小之后,就会立即使用LRU算法清除掉部分数据<br>对于64bit的机器,如果maxmemory设置为0,那么就默认不限制内存的使用,直到耗尽机器中所有的内存位置<br>maxmemoiry-policy,可以设置内存达到最大闲置后,采取什么策略来处理
清理策略
可以通过maxmemory-policy key 来设置内存达到最大内存后,采取什么策略来处理<br><br>volatile-lru -> 根据LRU算法删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。<br>allkeys-lru -> 根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。<br>volatile-lfu -> 根据LFU算法删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。<br>allkeys-lfu -> 根据LFU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。<br>volatile-random -> 随机删除过期键,直到腾出足够空间为止。<br>allkeys-random -> 随机删除所有键,直到腾出足够空间为止。<br>volatile-ttl -> 根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。<br>noeviction -> 不会删除任何数据,拒绝所有写入操作并返 回客户端错误信息,此 时Redis只响应读操作。<br>
缓存设计
缓存更新
1、先删除redis中的缓存<br>2、更新mysql 的数据<br>3、查询的时候在增加redis的缓存
缓存穿透
描述
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求。由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
缓存击穿
描述
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
解决
1、设置热点数据永远不过期。
2、接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些 服务 不可用时候,进行熔断,失败快速返回机制。
3、布隆过滤器。bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小
4、加互斥锁
雪崩击穿
1、redis集群彻底崩溃
2、缓存服务大量对redis的请求hang住,占用资源
3、缓存服务大量的请求打到源头服务去查询mysql,直接打死mysql
4、源头服务因为mysql被打死也崩溃,对源服务的请求也hang住,占用资源
5、缓存服务大量的资源全部耗费在访问redis和源服务无果,最后自己被拖死,无法提供服务
6、nginx无法访问缓存服务,redis和源服务,只能基于本地缓存提供服务,但是缓存过期后,没有数据提供
7、网站崩溃
描述
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是, 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案
1、缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
2、如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
3、设置热点数据永远不过期。
预防措施
事前
发生缓存雪崩之前,怎么去避免redis彻底挂掉<br>redis本身的高可用性,复制,主从架构,操作主节点,读写,数据同步到从节点,一旦主节点挂掉,从节点跟上<br>双机房部署,一套redis cluster,部分机器在一个机房,另一部分机器在另外一个机房<br>还有一种部署方式,两套redis cluster,两套redis cluster 之间做一个数据的同步,redis集群时可以搭建成树状的结构的<br>一旦说单个机房出了故障,至少说另外一个机房还能做些redis实例提供服务
事中
redis cluster已经彻底崩溃了,已经开始大量的访问无法访问到redis了
(1) ehcache本地缓存
所做的多级缓存架构的作用上了,ehcache的缓存,应对零散的redis中数据被清除掉的现象,另外一个主要是预防redis彻底崩溃
多台机器上部署的缓存服务实例的内存中,还有一套ehcache的缓存
ehcache的缓存还能支撑一阵
(2)对redis访问的资源隔离
(3)对源服务访问的限流以及资源隔离
事后
(1)redis数据可以恢复,做了备份,redis数据备份和恢复,redis重新启动起来
(2)redis数据彻底丢失了,或者数据过旧,快速缓存预热,redis重新启动起来
数据类型
String
数据结构:key-value
常用操作:set、get、decr、incr、mget回、msetnc加锁
应用场景:incr点赞计数器
hash
数据结构:Hashmap
常用操作:hget、hset、hgetall
<span style="font-size: inherit;">应用场景:</span><font color="#c41230" style="font-size: inherit;">数据对象存储</font><span style="font-size: inherit;">、session、短网址追踪、更新博客</span><br>
list
数据结构:双向链表,可重复有序集合
常用操作:lpush、rpush、lpop、rpop、lrange
应用场景:消息队列类型,阻塞式发送邮件、<font color="#c41230"><b>lrange:分页查询、数据id保存</b></font>
set
数据结构:value永远为null的Hashmap不可重复无序集合
常用操作:sadd、spop、snenbers、sunion
其它扩展判断某个成员是否在一个set集合内
应用场景:粉丝、关注、抽奖、投票、UV、多个关键词搜索商品
其他扩展-交集、并集、差集
Z-Set
数据结构:使用HashMap和跳跃表(Skiplis)来保证数据的存储和有序不可重复有序集合
常用操作:zadd、zrange、zrem、zcard
子主题
geo
数据结构:
常用操作:geoadd、georadiusByMember、geodist
应用场景:附近人、用户到商家的距离
sorted set
数据结构:zSet
常用操作:zadd、zincrby、zrevrangeWithScores、zrevrangeBySoreWithScores
应用场景:音乐排行榜、商品推荐
Redis持久化意义
1、是做灾难恢复、数据恢复,也可以归类到高可用的一个环节里面去,比如你redis整个挂了,然后redis就不可用了,你要做的事情是让redis 变得可用,或者尽快得可用
2、大量的请求过来,缓存全部无法命中,在redis里根本找不到护具,这个时候就死定了,缓存雪崩问题,所有请求,没有在redis 命中,就会去mysql数据库这种数据源头去找,一下子这么多请求,mysql可能也会挂了
3、redis持久化做好,备份和恢复方案做到企业及的程度、那么即使你的redis故障了,也可以通过备份数据,快速恢复,一旦恢复立即对外提供服务
Redis持久化RDB
优点
1、RDB会经过一段时间复制一份数据快照,这种数据快照非常适合做冷备,能够快速将完整文件发送到一些远程的redis中
2、RDB时 redis依然对外提供读写服务,性能影响非常小,可以让redis保持高性能,redis主进程只是fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可
3、相对于AOF持久化机制来说,redis通过RDB文件恢复的更快,在redis重启时能更快恢复redis可用
缺点
1、redis发生故障的时候,RDB可能会丢失数据,因为RDB是每隔5分钟备份一次快照,或者更长时间,那么这期间的数据就会丢失
2、RDB每次在fork子进程来执行RDB快照数据文件生成的时候,如果数据文件特别的大,可能会导致客户端提供的服务暂停数毫秒,或者数秒
配置
redis.conf文件,也就是//usr/local/redis-6.2.1/conf/redis.conf,去配置持久化<br>save 60 1000<br>每间隔60s,如果有超过1000key发生了变更,那么就生成一个新的dump.rdb文件,就是当前redis内存中完整的数据快照,这个操作也可以手动调用save或者bgsave命令,同步或者异步执行RDB快照生成<br>save可以设置多个,就是多个snapshotting检查点,每到一个检查点,就会去check一下,是否有指定的key数量发生了变更,如果有,就生成一个新的dump.rdb文件
工作流程
(1)redis根据配置自己尝试去生成rdb快照文件<br>(2)fork一个子进程出来<br>(3)子进程尝试去将数据dump到rdb快照文件中<br>(4)完成rdb快照文件的生成之后,就替换之前的旧的快照文件<br>dump.rdb 每次生成一个新的快照,都会覆盖之前老的快照
模拟实验
(1)在redis中保存几条数据,立即停掉redis进程,然后重启redis,看看刚才插入的数据还在不在<br>数据还在,为什么?
此处出现一个知识点,通过redis-cli SHUTDOWN这种方式去停掉redis,其实是一种安全退出的模式,redis在退出的时候会将内存中的数据立即生成一份完整的rdb快照
2、在redis中在保存几条新的数据,用kill -9 粗暴杀死redis进程,模拟redis故障异常退出,导致内存数据丢失的场景<br> 数据不存在了.最新数据没保存到<br>(2)手动奢姿一个sava检查点,sava5 1<br>这次就发现 redis进程异常被杀掉,数据没有进dump文件,几条最新的数据就丢失<br>(3)写入几条数据,等待5秒钟<br>(4)停掉redis进程,在重新启动redis,看刚才插入的数据还在不在<br>存在了<br>
Redis持久化AOF
优点
1、AOF可以更好的保护数据不丢失,一般AOF会每隔1秒通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据
2、AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破碎,也很容易修复
3、AOF文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写,因为在rewrite log的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来,在创建新的日志的同时,老的日志文件还是照常写入,当新的merge后的日志文件ready的时候,在交换新老日志文件即可
4、AOF日志文件的命令通常可读,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall 命令清空了所有数据,只要这个时候后台rewiter还没有发生,那么就可以离职拷贝AOF文件,把最后一条fulshall 命令删除掉。
缺点
1、对于同一份数据来说,AOF的文件要比RDB数据快照更大
2、AOF开启后,支持的QPS会比RDB支持写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,每秒一次fsync性能还是很高的
3、通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来,所以说,类似AOF这种较为复杂的基于命令日志/merge/回放的方式,比基于RDB每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有bug。不过AOF就是避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多
4、唯一的比较大的缺点,其实就是做数据恢复的时候,会比较慢,还要做冷备,定期的备份,不太方便,可能要自己手写复杂的脚本去做,做冷备不太合适
配置
1、AOF持久化,默认是关闭的,默认是打开RDB持久化,appendonly yes,可以打开AOF持久化机制,在生产环境里面,一般来说AOF都是要打开的,除非你说随便丢个几分钟的数据也无所谓,打开AOF持久化机制之后,redis每次接收到一条写命令,就会写入日志文件中,当然是先写os cahce的,然后每隔一定时间在fsycn一下
2、而且即使AOF和RDB都开启了,redis重启的时候,也是优先通过AOF进行数据恢复的,因为AOF数据比较完整
3、可以配置AOF的fsync策略,有三种策略可以选择,一种是每次写入一条数据执行一次fsync,一种是每隔一秒执行和一次fsync,一种是不主动执行fsync
always:每次写入一条数据,立即将这个数据对应的写日志fsync到磁盘上去,性能非常差,吞吐量很低,确保将redis里的数据一条都不丢,那就只能这样了
mysql -> 内存策略,大量磁盘,QPS到多少,一两K,QPS,每秒钟的请求数量<br>redis -> 内存,磁盘持久化,QPS到多少,单机,一般来说,上万QOS没问题
everysec 每秒将 os cache中数量fsync到磁盘,这个最常用的,生成环境一般都这么配置,性能很高,QPS还是可以上万的
no:仅仅redis负责将数据写入os cache 就撒手不管了,然后后面os 自己会时不时有自己的策略将数据刷入磁盘,不可控了
模拟实验
1、开启appendonly yes,kill -9 杀掉redis进程,重新启动redis 进场,发现数据被恢复回来了,就是AOF文件中恢复回来的<br>redis进程启动的时候,直接会从appendonly.aof 中加载所有的日志,把内存中数据恢复回来
2、redis进程启动的时候,直接就会从appendonly.aof中加载所有的日志,把内存中的数据恢复回来<br>redis中的数据其实有限的,很多数据可能会自动过期,可能会被用户删除,可能会被redis用缓存的算法清理掉
3、redis中的数据会不断淘汰掉旧的数据,只有常用的数据会被保留在redis 内存中,其他的数据会被redis用缓存清除算法清除掉
AOF rewrite
redis中的数据其实是有限的,很多数据可能会自动过期,可能会被用户删除,可能会被redis用缓存清除的算法清理掉<br>redis中数据会不断淘汰掉旧的数据,只有一部分常用数据会自动保存在redis内存中<br>所以可能很多之前的已经被清理掉的数据,对应的写日志还停留在AOF中,AOF日志文件就一个,会不断的膨胀到很大很大 <br>所以AOF会自动在后台每隔一定时间做rewrite操作,比如日志里已经存放了针对100w数据的写日志了;redis内存只剩下10万;基于内存中当前的10万数据构建一套最新的日志到aof中,覆盖之前的老日志;确保AOF日志文件不会太大
在redis.conf中,可以配置rewrite策略<br>auto-aof-rewrite-percentage 100<br>auto-aof-rewrite-min-size 64mb<br>比如说上一次AOF rewrite之后,是128mb<br>然后就会接着128mb继续写AOF的日志,比如发现增长的比例,超过了之前的100%(percentage 100 代表比原来大100%),256mb,就可能回去出发一次rewrite<br>但是此时还是要去跟min-size,64mb去比较,256mb > 64mb,(需要当前文件大小大于min-size)才会去触发rewrite<br>
工作流程
1、redis fork一个子进程<br>2、子进程基于当前内存中的数据,构建日志,开始往一个新的临时的AOF文件中写日志<br>3、redis主流程,接收到client新的写操作之后,在内存中写入日志,同时新的日志也继续写入旧的AOF文件<br>4、子进程写完新的日志文件之后,redis主进程将内存中的新日志再此追加到新的AOF文件中<br>5、 用新的日志文件替换掉旧的日志文件
RDB VS AOF
区别
1、RDB也可以做冷备,生成多个文件,每个文件都代表了某一个时刻的完整的数据快照<br>2、AOF也可以坐冷备,只有一文件,但是可以,每隔一定的时间,会copy一份这个文件出来
2、RDB,每次写,都是直接写redis内存,只是在一定的时候 ,才会将数据写入磁盘中<br>AOF,每次都是要写文件的,虽然可以快速些人 os cache中,但是还是有一定的时间开销的,速度肯定比RDB略慢一些
3、AOF,存放的指令日志,做数据恢复的时候,其实是要回放和执行所有的指令日志,未恢复出来内存中的所有数据的<br>RDB,就是一份数据文件,恢复的时候,直接拉取到内存中既可
同时工作
1、不要仅仅使用RDB,因为那样会导致你丢失很多数据
2、也不要仅仅使用AOF,因为那样会有两个问题,第一通过AOF做冷备,没有RDB做冷备,来的恢复速度快<br>第二、RDB每次简单粗暴生成数据快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug
3、综合使用AOF和RDB两种持久化机制,用AOF来保证数据不丢失,作为数据恢复的第一选择,用RDB来做不同的成都的冷备,在AOF文件都丢失或损坏的不可用的时候,还可用RDB来进行快速的数据恢复