Redis相关
什么是redis?redis可以干什么?
Redis是一个c语言编写的nosql数据库(不仅仅是sql,泛指非关系型数据库,一般把非关系型数据库称为nosql数据库),数据在内存中以键值对的形式存储,读写速度快,提供数据持久化方式.
常常被广泛应用到做缓存
Redis使用场景
1.缓存
2.计数器—>用作动态数据的变化
3.排行榜—>数据结构,zset按照分数排序
4.数据排重—>去除重复数据
5.消息队列—>redis中有List结构
6.分布式锁
Redis线程模型
redis是单线程模型还是多线程模型?
redis6.x之前是真正意义上的单线程,对外提供的键值存储服务主要流程是单线程,也是网络IO和数据读写是由单个线程完成的
redis6.x之后印度多线程指的是网络请求过程采用多线程,对键值读写指令依旧是单线程处理,保证了线程安全.
为什么redis执行命令时单线程模型速度也很快?
1.基于内存操作:redis的所有数据都存储在内存中,读写速度都是内存级别,所以新能较高.
2.基于哈希表结构存储,通过key可以快速的在哈希表中找到对应的数据
3.避免上下文切换,由于单线程模式,所以不存在切换的开销.
Redis持久化
为什么redis要提供持久化机制?
除了缓存数据之外,还将一些数据存储在redis中,这样服务器如果断电,内存中的数据就会丢失,所以redis提供持久化功能.
Redis为我们提供了持久化的机制,分别是 **RDB(Redis DataBase)**和 AOF(Append Only File)
**RDB方式:**redis中默认的持久化机制(默认开启),当满足条件时,会对内存中的数据进行拍照,以快照的方式把数据存储到 .rdb文件中
快照(把key:value数据直接存储到rdb文件中)
触发持久化规则:在redis.conf文件中
也可以执行save命令
**AOF方式:**也是redis中持久化的一种方式,默认没有启动,需要在redis.conf文件中配置
修改redis.conf 配置文件,开启AOF机制
appendon1y no #默认是不开启aof模式的,改为yes开启.
appendfilename appendonly.aof #默认的文件名是appendonly.aof,可以通过appendfilename参数修改
aof方式以执行日志的方式记录,将所有写操作的命令按顺序追加记录到"appendonly.aof"文件中,还原数据时逐行执行命令以完成数据恢复的工作
AOF同步机制
appendfsync always #每次修改都会 sync。消耗性能
appendfsync everysec #每秒执行一次 sync,可能会丢失这1s的数据(默认)
重启redis生效
Redis 事务
redis中的事务相比于mysql事务,简单很多.
redis中事务保证同一个事务在执行时,事务中的多条命令执行时,不会有其他事务插入到中间执行,因为redis是单线程.
redis事务执行时,不报哼多条指令执行的原子性,多条指令执行时,中间如果有错误命令,不会影响其他命令的执行.
mutil //命令开启事务set key value //添加命令到事务中set key valueexec //执行事务中的命令
Key过期策略
redis中key维护一个状态,表示是否过期,当定时时间到期后,将状态改为已过期,并没有立即删除key,在redis中,有两种策略删除过期的key
**1.惰性删除:**当key过期后,在下次使用此key时,发现key已经过期,然后将过期的key删除.
不足之处:浪费内存空间.
优点:不需要额外的线程定时顶点的跟踪删除
**2.定期删除:**每隔指定的周期,对redis中过期的key进行清理.
Redis 和mysql如何保证数据一致
此问题如何尽可能的保证redis和mysql中的数据保持一致(主要发生修改时)
**采用延时双删策略:**在更新mysql之前先删除redis中的数据,在mysql没有完全更新数据完成时,其他线程可以先读取旧的数据,在mysql数据更新完成后,再次删除redis中的数据,后来的线程确保读到的就是最新的mysql中的数据
缓存穿透、缓存击穿、缓存雪崩
数据查询处理流程
缓存穿透
key对应的数据在数据库中不存在,每次先查询缓存,缓存中没有,然后查询数据库,数据库中也不存在,返回一个null,没有往缓存中放.每次都还是直接查询mysql,相当于给缓存穿透
解决方案:
1.当mysql中查询不到时,可以向redis中存储一对key-value,value可以为null/-1,表名mysql中不存在
2.对参数进行校验
3.布隆过滤器:是一个二进制数组,用于判断元素是否存在.
使用k个hash函数对某个值进行哈希计算,计算出来的哈希值存储到对应数组位置,把数组原来的的0变为1.查询是否存在时,同样用k个hash函数计算哈希值,如果每个位置上都是1,表明数据可能存在,只要有一个位置是0,那么数据肯定是不存在的.会存在一定的误判率.
优点: 1. 时间复杂度低,增加和查询元素的时间复杂为O(N) 2. 保密性强,布隆过滤器不存储元素本身 3. 存储空间小,布隆过滤器是非常节省空间的
缺点: 1. 误判 2. 无法删除
1.添加依赖
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1-jre</version></dependency>
2.配置布隆过滤器
@Configurationpublic class BloomFilterConfig {@Beanpublic BloomFilter<String> bloomFilter() {//100000:布隆过滤器容量0.01为误判率
return BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()),100000, 0.01);}}
3.注入测试
@AutowiredBloomFilter bloomFilter;@GetMapping(path = "/test")public CommonResult test(){bloomFilter.put("aa");System.out.println(bloomFilter.mightContain("aa"));System.out.println(bloomFilter.mightContain("bb"));return new CommonResult(200, null,"查询成功");}
缓存击穿
数据在数据库中存在,但在redis中的热点key过期了,这时有大量的请求到来,同时请求到mysql,导致mysql压力过大(一般在秒杀这类场景中发生).
解决方法:
1.热点数据设置较长的过期时间
2.跑定时任务,在缓存失效前刷进新的缓存
3.加锁,可以给查询mysql的代码进行加锁,一个线程查询完后,把数据放到redis中,对访问mysql进行控制
(上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第 一个查询数据的请求上使用一个互斥锁来锁住它. 其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后将 数据放到redis缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存.)
缓存雪崩
大量的热点key过期或者redis服务出现问题,大量的请求访问到mysql
解决方法:
1.随机设置key失效时间,避免大量key集体失效。
set(Key,value,time + Math.random() * 10000);
2.若是集群部署,可将热点数据均匀分布在不同的Redis库中也能够避免key 全部失效问题
3.跑定时任务,在缓存失效前刷进新的缓存