一、缓存的使用
- 即时性、数据一致性要求不高的
- 访问量大且更新频率不高的数据(读多,写少)
举例:电商应用,商品分类,商品列表等适合缓存并加一个失效时间(根据数据更新频率来定),后台如果发布一个商品,买家需要5分钟才能看到新的商品一般还是可以接受的
二、高并发下缓存失效问题
- 缓存穿透
查询一个不存在的数据,由于缓存是不命中的,将去查询数据库,但是数据库也无此记录,我们没有将这个此查询的null写入缓存,这将导致这个不存在的数据每次请求都到存储层查询,失去了缓存的意义。
风险:利用不存在的数据进行攻击,数据库瞬时压力增大,最终导致崩溃
解决:null结果缓存,并加入短暂过期时间 - 缓存雪崩
指在我们设置缓存时key采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
解决:原有失效时间的基础上增加一个随机值,比如1-5分钟随机,这样没有一个缓存的过期时间的重复率就会降低,就很难引发集体失效事件。 - 缓存击穿
对于设置过期时间的key,如果这些key可能会在某些时间点被高并发地访问,是一种非常"热点"的数据。
如果这个key在大量请求同时进来前正好失效,那么所有对这个key的数据查询都落到DB,我们称为缓存击穿。
解决:加锁,大量并发只让一个去查,其他人等待,查到后释放锁,其他人获取到锁,先查缓存,就会有数据,不用去DB
三、本地锁
只要是同一把锁,就能锁住需要这把锁的所有线程,
1. synchronized(this),JUC(lock)
SpringBoot 所有的组件在容器中都是单例的,一个服务一个容器,一个容器一个实例,
每个this是不同的锁,
四、分布式锁
基本原理: 可以去一个地方占坑,占到就执行逻辑,否则等待,直到释放锁,"占坑"可以去redis,可以去数据库,可以去任何大家都能访问到的地方,等待可以自旋的方式。
问题
- setnx 占好了位,业务异常或者程序在页面过程中宕机,没有执行删除锁逻辑,就造成了死锁,可以通过设置锁的自动过期,即使没有删除,会自动删除,设置过期和加锁要原子操作
如果业务时间长,锁自己过期了,我们直接删除,有可能把别人正在持有的锁删除了,占锁的时候指定uuid,每个人匹配自己的锁才删除。删除之前判断是否是自己的
String lockValue = stringRedisTemplate.opsForValue().get("lock"); if (uuid.equals(lockValue)) { stringRedisTemplate.delete("lock"); }
但这样还是有问题, 从redis取出值,返回的路上,redis过期了,别的线程加锁成功,这时判断为true,删除就删除了别的线程的锁,所以获取,判断,删除要是原子操作,通过lua脚本来实现。
Redisson
- 阻塞式等待,默认加锁都是30s时间
- 锁的自动续期,如果业务超长,运行期间自动给锁续上新的30s,不用担心业务时间长,锁自动过期被删掉
- 加锁业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30秒以后自动删除
读写锁
保证一定能读到最新数据,修改期间,写锁是一个排他锁(互斥锁、独享锁),读锁是一个共享锁
写锁没释放读就必须等待
读 + 读:相当于无锁
写 + 读:等待写锁释放
写 + 写:阻塞方式
读 + 写:有读锁,写也需要等待
只要有写的存在,都必须等待五、缓存一致性
缓存中的数据如何与数据库保持一致(缓存数据一致性)
- 双写模式
修改了数据库再查下,保存至缓存。两个线程修改同一条数据库,由于卡顿原因,导致写缓存2在最前,写缓存1在后面就出现不一致。
脏数据问题:这时暂时性脏数据问题,但是在数据稳定,缓存过期后,又能得到最新的正确数据(最终一致性) - 失效模式
修改数据库,删除缓存
- 双写模式
无论双写还是失效模式,都会导致缓存不一致问题,即多个实例同时更新会出事,怎么办?
- 如果是用户维度数据(订单数据、用户数据),这种并发几率小,不用考虑这个问题,缓存数据加上过期时间,每隔一段时间触发读主动更新即可
- 如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式。
- 缓存数据+过期时间也足够解决大部分业务对于缓存的要求
- 通过加锁保证并发读写,写写的时候按顺序排好队,读读无所谓,所以适合读写锁(业务不关心脏数据,允许临时脏数据可忽略)
总结
- 我们能放入缓存的数据本就不应该是实时性的,一致性要求超高的,所以缓存数据加上过期时间,保证每天拿到当前的最新数据即可
- 我们不应该过度设计,增加系统的复杂性
- 遇到实时性、一致性要求高到的数据,就应该查数据库,即使慢点
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。