1、缓存穿透、击穿、雪崩
1.1、缓存穿透
是指缓存和数据库中都没有的数据,而用户不断发起请求
如发起为id为“-1”的数据或id为特别大(不存在的数据)。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决:
接口层增加校验,如用户鉴权校验,id做基础校验,比如 id<=0的直接拦截;
最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。
1.2、缓存击穿
是指缓存中没有但数据库中有的数据
当一个key非常热点(类似于爆款),在不停的扛着大并发,大并发集中对这一个点进行访问;
当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
解决:
- 设置热点数据永远不过期。
- 加互斥锁。
- 读到某key时重置(延长)它的过期时间(缓存续命)
1.3、缓存雪崩
是指缓存中数据大批量到过期时间,大批量数据同一时间过期,导致请求量全部请求到数据库,造成数据库宕机。
解决:
- 给缓存失效时间,加上一个随机值,避免大量缓存集体失效。
- 双缓存:缓存A和B,比如A的失效时间是20分钟,B不失效。比如从A中没读到,就去B中读,然后异步起一个线程同步到A。
2、Memcache与Redis的区别
1、memecache不支持持久化 宕机断电无法恢复
2、memecache只有简单的字符串类型,虽然也可以缓存图片;Redis有很多种类型。
3、memcache可以利用多核优势,单实例吞吐量极高,可以达到几十万QPS,适用于最大程度扛量,100k以上的文件memcache较快。
4、memcached一个value最大只支持1MB,Redis最大支持512MB
redis6.0引入了多线程
开启多线程后,是否会存在线程并发安全问题?
不会:
1)Redis多线程部分仅用于处理网络IO和协议解析
2)Redis执行命令仍然是单线程的
3、Redis 常见性能问题和解决方案
(1) Master主节点最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件。
(因为RDB生成快照是很耗性能的,这样会阻塞master,会造成master假死)(2) 如果数据比较重要,某个 Slave从节点开启 AOF 备份数据,策略设置为每秒同步一次
(这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度,所以要合理设置AOF重写机制)(3) Master 和 Slave 最好在同一个局域网内(主从复制的速度快和连接的稳定性强 )
(4) 读写分离,把请求压力分散到各个服务。
4、Redis分布式锁
关键点:SETNX命令。
Redis中提供SETNX命令可以实现分布式锁。
redisson也可以实现(redisson是在Redis是封装的一个jar,简单灵活),配合lua进行加锁和解锁。
将 key 的值设为 value ,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作。(轮询)
1 | RLock rLock = redissonClient.getLock(lockKey); |
5、Redis的并发竞争问题如何解决?
出现这个问题的原因:多个子系统去set一个key-value,并发情况下,可能会导致数据一致性问题。
上面第10点说的分布式锁是一种方案,利用SETNX命令。确保同一个时间,只有一个线程拿到了锁,其他则在等待。
还有一种方案就是时间戳。假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。
利用队列,将set方法变成串行访问 等..
6、redis事务
Redis也自带事务,但是和MySQL的不同,又有点类似。
Redis事务和MySQL一样,通过 begin;commit;rollback去开启和提交/回滚事务,只不过命令不一样。
具体参考:https://www.cnblogs.com/DeepInThought/p/10720132.html
7、双一致性问题
简单的说就是先写Redis还是先写数据库的问题。
举例:A先改数据库 但B改缓存的操作比A先响应
A改数据库 → B改数据库 → B改缓存 → A改缓存 那就会有数据脏读问题。
详细说明、解决方案见:https://www.cnblogs.com/rjzheng/p/9041659.html
简略说明:
先改库 再改缓存 普遍反对 上述例子说明脏读问题
先删缓存 再改库 也有问题 (用延迟双删策略)
先改库 再删缓存 也有问题