Redis
Redis是高性能的以KV存储的NoSql类型数据库,并支持String/List/Ser/Hash/Zset等不同结构的数据存储。
常用应用场景:
1. 缓存服务器
2. 分布式锁
缓存服务器
Redis为了保证效率, 与memcached一样将数据存放到内存中,区别是redis可以周期性的将内存数据写入磁盘,以实现数据的持久性。
用作缓存服务器存在的问题
1.缓存穿透
2.缓存击穿
3.缓存雪崩
4.数据一致性
缓存穿透
通常情况下,我们使用缓存的逻辑如下:
步骤:
1.发送请求,查询redis
2.如果命中查询数据,直接返回
3.如果没有该缓存中没有命中,则查询数据库
4.如果不为空,则将数据添加到缓存,并返回
这种情况下看似没有问题,但如果出现大量的并发请求,都没有命中缓存,而查询数据库,这样就导致
数据库的压力暴增,从而增加数据库开销,导致数据库性能下降,甚至宕机
解决方案:
将步骤4 改为:无论数据库返回的数据是否为空,都将其缓存,同时为了节约资源,将其设置一个合理的有效期(过期失效)。
缓存击穿
问题出现场景:某一类热数据缓存突然失效,导致大量请求查询数据库,引发数据库的压力暴增,增加数据库开销,导致数据库性能下降,甚至宕机
解决方案:
方法 1 直接将热数据缓存设置为永不过期,这样即不存在缓存过期情况
方法 2 将热数据类型的数据进行缓存,根据设定的规则,分别设置不同的过期时间,这样即使部分热 数据出现缓存过期,也不会导致,所有对热数据的并发请求全部直达数据库,可有效缓解数据库压力
缓存雪崩
由于缓存层承载着大量请求,有效的保护了存储层,但是如果缓存层由于某些原因整体不能提供服务,于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
导致缓存雪崩的可能因数:
1.缓存服务宕机
2.大量的缓存同时过期
解决问题1:
实现reids高可用
解决问题2:
1)通过限流降级方式控制并发量,减轻数据库压力,有效保证服务器的稳定性
2) 通过锁机制控制
3)通过对数据设置不同的过期时间来控制请求到达数据库的并发量,降低数据库压力
4)通过队列控制
数据一致性
数据一致,指缓存中的数据与数据库中的数据是否一致。想要达到两个不同存储系统中的数据实时一致严格来说不可能完成的。因为redis中的数据操作与mysql中对数据的操作并不是一个原子操作,所以无法保证两个系统中的数据实时一直。但我们可以实现最终的数据一致性,并争取在最短的时间内,实现数据一致性。
解决问题 1: 双删机制
当我们更新数据库数据时,缓存数据也应该同时更新,操作方式如下几种:
第一种
1.删除缓存
2.更新数据库
第二种
1.更新数据库
2.删除缓存
第一种存在问题:一个更新操作开始执行,先删除缓存,在更新数据库,如果在删除缓存后,这时来一个了写操作,缓存中的数据已经删除,也就无法命中,所以它会去数据库中读取数据,然后在存入缓存,读操作刚将写操作存入缓存中,更新操作进行到第二个并完成数据库的更新,这时就会导致缓存数据不一种。出现一种脏读情况
第二种存在问题:一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。
这种情况理论上会出现,不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所以所有的这些条件都具备的概率基本并不大,但万事皆可能…
(两种情况皆时考虑操作可正常执行不包括,宕机等情况)
双删策略
方案一:
1.删除缓存
2.更新数据库操作
3.暂停当前线程
4.删除缓存
这样就可以有效的阻止了缓存脏数据的可能,前提时每一步骤都能成功执行。如果删除缓存失败怎么办?
如果出现删除缓存失败情况,既可使用mq将删除失败key放入其中,使用mq异步删除缓存。
方案二:
可以通过阿里的canal中间件,读取数据库的binlog,再配合Mq进行异步数据同步
分布式锁
使用redis实现分布式锁:
转发:https://blog.csdn.net/G0_hw/article/details/89851645