把黑马的redis实战看了将近一半,自己也做了挺多思考,现在对于Redis的使用,以及业务方面的思考,有了更深刻的理解。
- 使用缓存能够加快数据的查询速度,提高用户的使用感受,对于经常需要访问的数据,都可以放到缓存中,这样也能给数据库减少压力。
但是,使用缓存之后,就有许多问题需要解决,包括业务场景的考虑。
1. 缓存和数据库一致性的问题
- 这个问题是确保用户能够从缓存中访问到最新的数据。
-- 一般需要考虑的场景就是:更新了数据库,那么我们的缓存也要更新。否则用户去查数据走缓存,那么拿到的数据就可能是假的。 - 如何去更新也是有技巧,能够确保不做过多的无效操作,也要确保用户能拿到最新数据。
- 根据学习,我理解的就是采用:先更新数据库,再删除缓存这么一个策略
-- 相较于每次更新完数据库都去更新缓存。这个策略就能避免很多无效的写操作。也能确保缓存是最新的(这其中就涉及到一些并发的业务逻辑)。
2. 缓存击穿问题
- 这个问题就是热点key失效了,而重构这个key又需要比较久的时间。
-- 而在这个重构的时间段里面,如果并发访问数据,那么就又会给数据库造成很大压力。 - 如何去解决这个问题。我理解的就是加锁。
-- 如果用户进来查询数据,后台发现未命中缓存,那么就去尝试获取锁,而这个锁,是redis中的一个分布式锁,setnx这个命令。拿到了锁,那么就去执行缓存重建逻辑。
-- 而如果这时候,其他用户也进来查数据,此时肯定拿不到锁,那么就会休眠一会,然后尝试重新去查询数据。 - 这就是1一个解决方案,这个方案能避免给数据库造成很大压力,但是也可能会影响用户体验,因为需要进行等待。
- 还有一个解决方案就是逻辑超时,也就是不给key设置过期时间,但是给他设置一个逻辑超时时间,这个方案的话就是给k-v的v中存储一个key的有效期。这个方式的话,在缓存重建之前,用户会拿到脏数据。
- 还得根据具体业务去选择
3. 缓存穿透问题
- 这个问题就是客户端发来了很多请求,但是这些请求需要的数据在缓存、数据库都没有,给数据库造成了很大的压力。
- 解决方案:1.缓存空数据 2.布隆过滤器(对这个不是很了解,没用过)
4. 缓存雪崩问题
- 这个问题是redis宕机,或者说大量的key失效
- 解决方案:搭建redis集群
分析一个具体的业务场景
用户抢优惠券,要满足条件:一人一单
要考虑并发时出现的问题。
1.首先要查该用户是否有券,如果没有就能让它去抢,否则就不允许
2.如果该用户没券,就去执行抢券逻辑
在查券的时候,如果同一个用户发来了很多请求,这是一个并发问题。
如果说线程1判断无券,那么就去执行抢券逻辑。如果这时候线程2进来了,也会判断无券,又会执行它的抢券逻辑。
所以需要加一个锁,即使同一个用户发来多次请求,此时也是串行的,只有等他执行完抢券逻辑,才会释放锁。那么如果该用户的其他的抢券请求进来,也不会去执行抢券。
同时,抢券逻辑也在这个锁的锁定范围内,也避免了超卖的问题。
这里的锁需要使用分布式锁,比如redis的setnx()。
分析:我们采用的锁对象是,user.getId()。在单体项目之下,对于一个用户的多个请求进来,该锁对象确实是唯一的。
但是如果将该项目做成集群的形式,由于每个Tomcat都有自己的JVM,那么此时的锁就不是同一吧了,而是这台服务器认为他的这把锁是唯一的,那台服务器认为他的这把锁是唯一的。这样子就做不到唯一锁。所以我们这里需要使用分布式锁。
但是分布式锁也会遇到一些问题,比如说误删锁问题,以及删锁时的原子性问题。