应用问题

缓存穿透

1、key 对应 value 不存在于数据源

(1)每次从缓存中无法获取此 key,获取请求都会压到数据源,从而可能压垮数据源

(2)一个一定不存在缓存及查询不到的数据,出于容错考虑,如果从存储层查不到数据,则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义

2、解决

(1)缓存 null 值:如果一个查询返回的数据为 null(不管是数据是否存在),仍把空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟

(2)设置可访问的名单(白名单):使用 bitmaps 类型定义一个可以访问的名单,名单 id 作为 bitmaps 偏移量,每次访问时,与 bitmap 的 id 比较,如果访问 id 不在 bitmaps 中,进行拦截,不允许访问

(3)采用布隆过滤器(Bloom Filter):实际上是一个很长的二进制向量(位图)、一系列随机映射函数(哈希函数),检索一个元素是否在一个集合中;优点是空间效率、查询时间远超一般算法,缺点是有一定的误识别率、删除困难;将所有可能存在的数据哈希到一个足够大的 bitmaps 中,一个一定不存在的数据会被该 bitmaps 拦截,从而避免对底层存储系统的查询压力

(4)进行实时监控:当发现 Redis 命中率开始急速降低,需要排查访问对象、访问数据,和运维人员配合,可以设置黑名单限制服务

(5)增强 id 复杂度,避免被猜测 id 规律、校验数据的基础格式、校验用户权限、限流热点参数

 

缓存击穿

1、一个 key 对应 value 存在于数据源,但在 Redis 中过期

(1)此时若有大量并发请求,这些请求发现缓存过期,一般都会从后端 DB 加载数据,并回设缓存,这时大并发的请求可能会瞬间把后端 DB 压垮

(2)key 可能会在某些时间点,被超高并发地访问,是一种热点数据

2、预先设置热门数据

(1)在 Redis 访问高峰之前,把一些热门数据提前存入到 Redis 中

(2)加大热门数据 key 过期时长

3、实时调整:现场监控哪些数据热门,实时调整 key 过期时长

4、使用锁

(1)在缓存失效时(判断 value 为空),不立即查询 DB

(2)先使用缓存工具的带成功操作返回值的操作(比如 Redis 的 SETNX),设置一个 mutex key

(3)当操作返回成功时,再进行加载 DB,并回设缓存,最后删除 mutex key

(4)当操作返回失败时,证明有线程在加载 DB,当前线程睡眠一段时间,再重试查询整个缓存

 

缓存雪崩

1、多个 key 对应 value 存在,但在 Redis 中过期

(1)此时若有大量并发请求,这些请求发现缓存过期,一般都会从后端 DB 加载数据,并回设缓存,这时大并发的请求可能会瞬间把后端 DB 压垮

(2)缓存雪崩:群体失效;缓存击穿:单体失效

(3)正常访问

(4)缓存雪崩

2、解决

(1)构建多级缓存架构:Nginx + Redis + 其他(Ehcache 等)

(2)使用锁或队列:保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上,不适用高并发情况

(3)设置过期标志更新缓存:记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程,在后台去更新 key 的缓存

(4)将缓存失效时间分散开:可以在原有的失效时间基础上增加一个随机值,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件

 

分布式锁

1、单体单机部署的系统 -> 分布式集群系统

(1)由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯 Java API 并不能提供分布式锁的能力

(2)需要一种跨 JVM 的互斥机制来控制共享资源的访问

2、主流实现

(1)基于数据库实现分布式锁

(2)基于缓存(Redis 等)

(3)基于 Zookeeper

3、方案对比

(1)性能:Redis 最高

(3)可靠性:Zookeeper 最高

4、设置锁的过期时间

(1)expire:缺乏原子性,如果在 setnx 和 expire 之间出现异常,无法释放锁

(2)在 set 同时,使用 ex 指定过期时间(建议)

5、避免误释放锁

(1)设置锁的 UUID,区分不同操作的锁

(2)LUA 脚本保证删除的原子性

 

LUA 脚本在 Redis 中的优势

1、将复杂、多步 Redis 操作,写为一个脚本,一次提交给 Redis 执行,减少反复连接 Redis 次数

(1)LUA 脚本类似 Redis 事务,有一定原子性,不会被其他命令插队,可以完成一些 Redis 事务性的操作

(2)本质上利用 Redis 单线程的特性,用任务队列的方式解决多任务并发问题

2、Redis 的 Lua 脚本功能,只有在 Redis 2.6 以上的版本才可以使用

posted @   半条咸鱼  阅读(42)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示