Redis
redis缓存击穿
查询一个不存在的数据,mysql查询不到就一直写入不进缓存,所以导致每次都查询数据库
解决方案:1.布隆过滤器 2.可以缓存空数据,查询返回结果为空也一样进行缓存
redis缓存穿透
当一个key过期时大量的请求查询,这些瞬间大量的请求可能会压垮数据库
解决方案:1.使用互斥锁:当缓存失效时,不立即去load数据库,而是使用redis的setnx设置一个互斥锁,当操作成功返回时再进行load数据库操作并且回设缓存,否则重试get缓存的方法。
特点:一致性强,性能差
2.逻辑过期:a.设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间
b.当查询时判断是否过期
c.过期就开通另一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新
特点:高可用,一致性差,性能强
redis缓存雪崩
同一时间段内大量的key过期或者redis服务宕机,导致大量的请求到达数据库,带来巨大压力
解决方案:1.给不同的key的过期时间添加随机值
2.利用redis集群提高服务的可用性
3.缓存降级
4.给业务添加多级缓存
redis双写一致性
当修改了数据库的数据时也要修改缓存的数据,缓存与数据库的数据要保存一致
两种方案:
1.允许延时一致的业务:使用异步通知
a.使用MQ中间件,当更新数据后,通知缓存删除
b.使用canal中间件,不需要修改业务代码,伪装成mysql的一个从节点, canal通过读取binlog数据更新缓存
2.要求强一致的业务:使用redisson提供的读写锁
a.共享锁:读锁readlock,加锁之后,其他线程可以共享读操作
b.排他锁:独占writelock,加锁之后,阻塞其他线程读写操作
redis持久化
当redis实例故障重启后还能获得数据
两种方式:
1.RDB:一个快照文件,把redis内存存储的数据写至磁盘上,当redis实例宕机恢复时,从磁盘的rdb快照文件中恢复数据
2.AOF:是追加文件,当redis操作写命令时,都会存储在这个文件中,当redis实例宕机恢复时,会从这个文件中再次执行一遍命令来恢复数据
这两种方式哪个快?
RDB是二进制文件,体积较小,恢复的比较快,但是它可能会丢失数据,故也会用AOF来恢复数据,虽然AOF回复慢,但是丢数据的风险会小一些,AOF中设置不同的刷盘策略。
redis数据过期策略
有两种需要配合使用:
1.惰性删除:在设置key的过期时间后,就不去管它,当需要该key时,再检查是否过期,如果过期就删除
2.定期删除:每隔一段时间就对一些key进行检查,删除里面过期的key
两种模式:slow模式:10hz
fast模式:频率不固定
redis数据淘汰策略
当redis中的内存不够用时,此时在redis中添加新的key,那么redis会按照某种规则将内存中的数据删除,这种数据删除规则被称为内存的淘汰策略
默认的策略是noeviction即不删除任何数据,内存不足直接报错
LRU:最少最近使用,用当前时间捡起最后一次访问时间,值越大则淘汰优先级越高
LFU:最少频率使用,统计每个key的使用频率,值越小淘汰优先级越高
redis分布式锁
redis中提供了一个命令setnx,由于redis是单线程的,用了命令之后只能由一个客户端对一个key设置值,在没有过期或删除key的时候其他客户端是不允许设置这个key的。
如何控制redis实现分布式锁的有效时长:使用redis的框架redisson实现。 在redisson中需要手动加锁,并且可以控制锁的失效时间与等待时间,当锁住的业务还没有执行完成时,在redisson中引入看门狗机制,每隔一段时间就检查当前业务是否还持有锁,若持有锁就增加锁持续时间,没有就释放锁。
redisson实现的分布式锁可以重入吗:可以 这样做是为了避免死锁的产生。重入其实在内部就是判断是否是当前线程持有的锁,若是就会计数加1,如果释放就会减1,在存储数据时用的是hash结构
redisson实现的分布式锁不能解决主从一致的问题。
redis的集群方案
有三种:主从复制、哨兵模式与redis分片集群
1.主从复制:单节点的redis的并发能力是有上限的,要进一步提高redis的并发能力可以搭建主从集群,实现读写分离。一般是一主多从,主节点负责写操作,从节点负责读操作。主节点写入数据后需要把数据同步到从节点中。
主从同步的流程:分为两个阶段,全量同步与增量同步。
全量同步是指从节点第一次与主节点建立连接时使用全量同步
a.从节点请求主节点同步数据,其中从节点会携带自己的replication与offset
b.主节点判断是否是第一次请求,主要判断的依据是主节点与从节点是否是同一个replication,如果不是就说明是第一次同步,主节点会把自己的replication id 与offset发送给从节点,让从节点与主节点的信息保持一致
c.同时主节点会执行bgsave,生成rdb文件后,发送给从节点去执行,从节点先把自己的数据清空,然后执行主节点发送过来的rdb文件,这样就保持了一致.
注意:在rdb文件生成执行期间,依然有请求到了主节点,而主节点会以命令的方式记录到缓冲区,缓冲区是一个日志文件,最后把这个日志文件发送给从节点,这样就能保证主从节点完全一致,后期再同步数据时都是以来这个日志文件
增量同步是指从节点服务重启之后数据就不一致了,所以这时,从节点会请求主节点同步数据,主节点还是判断是否为第一次请求,不是第一次就获取从节点的offset值,然后主节点从命令日志中获取offset值之后的数据发送给从节点进行数据同步
redis的底层数据结构
有五种:String、List、Hash、Sorted Set与Set
redis多路IO服用底层(NIO)即相关IO
同步、异步、阻塞与非阻塞IO的区别
1.同步 IO 是用户线程发起 IO 请求后,需要等待或轮询内核 IO 操作完成后才能继续执行。
2.异步IO是用户发起IO请求后可以继续执行,当内核IO操作完成后会通知用户线程或调用用户线程注册的回调函数
3.阻塞IO指当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务。因此,在完成网络通信进行IO操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量客户端时,性能急剧下降。
4.非阻塞IO指当线程从某通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程可以管理多个输入和输出通道。
redis使用I/O多路复用结合事件的处理器来应对多个Socket请求
(1)连接应答处理器
(2)命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程来处理回复事件
(3)命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程
经典例子
有一个经典的举例:烧开水,假设有这么一个场景,有一排水壶(客户)在烧水。
AIO的做法是,每个水壶上装一个开关,当水开了以后会提醒对应的线程去处理。 NIO的做法是,叫一个线程不停的循环观察每一个水壶,根据每个水壶当前的状态去处理。
BIO的做法是,叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。
可以看出AIO是最聪明省力,NIO相对省力,叫一个人就能看所有的壶,BIO最愚蠢,劳动力低下。简单的描述一下BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理 处理完成后,通过输出流返回应答给客户端,线程销毁。即典型的一请求一应答通信模型。
分布式锁SetNX和Redission的区别
1.setNx是redis中的一个命令,用于将键值对设置到Redis数据库中。setnx表示Set if Not Exists,即当键不存在时才执行设置操作。它是一个原子性的操作,可以保证在多个客户端同时请求设置同一个键时,只有一个客户端能够成功设置该键,其他客户端将失败。这使得setnx在分布式环境中可以用于简单的分布式锁实现
优点:
- 实现简单,易于理解和使用。
- 原子性操作,避免并发设置同一键时的竞态条件问题。
缺点:
- 需要开发者自行处理锁的过期时间和续约机制,增加了额外的开发复杂性。
- 对于获取锁失败的客户端,需要不断重试,可能会导致性能损失和增加系统负载。
2.redisson是基于redis的分布式Java对象和服务框架,提供了丰富的分布式服务和工具如分布式锁、分布式集合、分布式对象等。Redisson封装了redis的底层API,简化了在Java应用中使用Redis的复杂性,提供了易于使用的高级API。Redisson的分布式锁实现不仅支持setnx命令,还提供了更多的锁实现方式,如可重入锁、公平锁、读写锁等,满足不同场景下需求。
优点:
- 封装了Redis的底层API,提供易于使用的高级API,简化了分布式锁的使用。
- 提供了丰富的分布式数据结构和服务,方便实现多种分布式功能。
缺点:
- 作为第三方库,引入项目中可能增加一定的依赖复杂性,需要仔细考虑项目整体架构和依赖管理。
- 在某些高并发场景下,Redisson可能产生较高的网络通信开销,需要合理优化配置。
setnx是Redis中的原子性命令,用于在键不存在时设置键值对,适用于简单的分布式锁实现;Redisson是基于Redis的分布式Java框架,封装了Redis的API,提供易用的高级API和丰富的分布式服务和工具,包括多种锁类型的实现,方便开发者使用和管理分布式功能。