网站备案后怎么做实名认证,wordpress ip验证不当,陕麻圈辅助软件,高端的平面设计网站一、什么是缓存
#xff08;一#xff09;概念#xff1a;缓存就是数据交换的缓冲区#xff08;称为Cache#xff09;#xff0c;是存储数据的临时区域#xff0c;一般读写性能较高。
#xff08;二#xff09;常见缓存#xff1a; 浏览器缓存#xff0c;服务器缓…一、什么是缓存
一概念缓存就是数据交换的缓冲区称为Cache是存储数据的临时区域一般读写性能较高。
二常见缓存 浏览器缓存服务器缓存数据库缓存CPU缓存磁盘缓存。
三缓存的作用
降低后端负载提高读写效率降低响应时间
四缓存的成本
数据一致性成本代码维护成本运维成本 二、缓存更新策略
一三种更新策略的对比
内存淘汰超时剔除主动更新说明Redis提供的内存淘汰机制当内存不足时自动淘汰部分数据下次查询时更新缓存给缓存数据添加TTL到期后自动删除缓存下次查询时更新缓存编写业务逻辑在修改数据库的同时更新缓存一致性差一般好维护成本无低高
二业务场景
低一致性需求 使用内存淘汰机制主要是针对几乎不改变的数据。高一致性需求 主动更新并以超时剔除作为协助方案主要是针对频繁查询的缓存。
三主动更新策略
1Cache Aside Pattern 由缓存调用者同时更新数据库和缓存。可控性更高 删除缓存还是更新缓存 更新缓存 每次更新数据库都更新缓存无效写操作较多。删除缓存 更新数据库时让缓存失效查询时再更新缓存。 2如何保证缓存与数据库的操作同时成功或失败 单体系统 将缓存与数据库操作放在一个事务。分布式系统 利用TCC等分布式事务方案。 3先操作缓存还是先操作数据库 先删除缓存在操作数据库。先操作数据库在删除缓存。保证一致性的概率更高 Read/Write Through Pattern 缓存与数据库整合为一个服务由服务来维护一致性。调用者调用该服务无需关心缓存一致性问题。Write Behind Caching Pattern 调用者只操作缓存由其它线程异步的将缓存数据持久化到数据库保证最终一致。 三、缓存穿透、缓存雪崩、缓存击穿
一缓存穿透
1、缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在这样缓存永远不会生效这些请求都会打到数据库。
2、缓存穿透过程 客户端发送不存在的id 》 Redis按照 id 查询未查询到 》 数据库根据 id 查询未查询到 》 返回客户端 》 客户端发送不存在的id。这个过程若采用多线程循环攻击数据库那么就会造成数据库带宽爆满请求阻塞系统资源耗尽等等问题
3、缓存穿透常用解决方案
1缓存空对象 在用户第一次查询缓存和数据库时都没有对应的值则在缓存中存储一个对应的空值并设置一个短期的TTL当恶意用户短期内发起请求时都会在缓存中查找到对应的值避免了缓存穿透。 优点 实现简单维护方便缺点 额外的内存消耗、可能造成短期的不一致 2布隆过滤 通过布隆过滤器在客户端和Redis之间做一道防火墙当用户访问到布隆过滤器时布隆过滤器会查询自身是否存在请求值若不存在则拒绝若存在则放行查询。 优点 内存占用较少没有多余key缺点 实现复杂存在误判可能 3增强id复杂度避免被猜测id规律4做好数据的基础格式校验5加强用户权限校验6做好热点参数的限流
4、布隆过滤器 一种算法通过将数据库的数据进行某一种hash值计算并将这些hash值转化为二进制位存储到布隆过滤器中客户端发送的请求中的数据通过hash计算转化为二进制位在布隆过滤器中查找若能找到则说明存在该数据。
二缓存雪崩
1、缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机导致大量请求到达数据库。
2、缓存雪崩的过程 客户端请求数据 》 Redis查询Redis部分缓存缺失或Redis宕机 》 查询数据库大量请求到达把数据库也给宕机了
3、解决方案
1给不同的key的TTL添加随机值避免同时失效2利用Redis集群提高服务的可用性Redis哨兵机制监听选举主Redis主从Redis保证数据安全性3给缓存业务添加降级限流策略4给业务添加多级缓存
三缓存击穿
1、缓存击穿是指高并发且缓存重建业务复杂的key突然失效无数的请求访问在瞬间给数据库带来巨大的冲击。
2、常见的解决方案
1互斥锁 在一个线程进入缓存重建阶段时获取互斥锁其它线程无法进行获取互斥锁并自旋尝试获取锁直到互斥锁释放。 缺点 线程锁住了缓存时间过长会造成业务阻塞。 2逻辑过期 在存储一个数据时添加一个逻辑上的过期时间到期后直接移除即可。
3、两种解决方案的区别
解决方案优点缺点互斥锁 没有额外的内存消耗保证一致性实现简单 线程需要等待性能受影响可能发生死锁逻辑过期 现成无需等待性能较好 不保证一致性有额外内存消耗实现复杂 四、缓存工具封装
基于StringRedisTemplate封装一个缓存工具类。
方法1 将任意Java对象序列化为json并存储在string类型的key中并设置TTL过期时间。方法2 将任意Java对象序列化为json并存储在string类型的key中并可以设置逻辑过期时间用于处理缓存击穿问题。方法3 根据指定key查询缓存并反序列化为指定类型利用缓存空值的方式解决缓存穿透问题。方法4 根据指定key查询缓存并反序列化为指定类型需要利用逻辑过期解决缓存击穿问题。
一工具类
Slf4j
Component
public class CacheClient {private final StringRedisTemplate redisTemplate;public CacheClient(StringRedisTemplate redisTempalte) {this.redisTemplate redisTempalte;}public String toJsonStr(Object value) {return JSONObject.toJSONString(value)}public T String toJsonObj(String json, ClassT type) {return JSONObject.parseObject(json, type)}}二实现缓存工具方法
方法1序列化并设置TTL public void set(String key, Object data, Long time, TimeUnit unit) {redisTemplate.opsForValue().set(key, this.toJsonStr(data), time, unit);}方法2序列化并设置逻辑过期 public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {//1、设置逻辑过期RedisData - (R Data, LocalDateTime dateTime)RedisData redisData new RedisData(value, LocalDateTime.now().plusSeconds(unit.toSeconds(time)));//2、RedisData 写入RedisredisTemplate.opsForValue().set(key, this.toJsonStr(redisData));}方法3缓存空值解决缓存穿透 public R, ID R queryWithPassThrough(String keyPrefix, ID id, ClassR type, FunctionID, R dbFallback, Long time, TimeUnit unit) {//1、查看Cache是否命中String key keyPrefix id;String json stringRedisTemplate.opsForValue().get(key);if(!json.isEmpty()){//命中判断为有值直接返回return this.toJsonObj(json, type);}if(Objects.isNull(json)){//命中判断为空白返回错误信息return null;}//未命中查看数据库R r dbFallback.apply(id);//若不存在对应数据则打入Redis一个空值并返回错误信息if (r null) {redisTemplate.opsForValue().set(key, , 2, TimeUnit.MINUTES);return null;}//若存在则返回并设置TTLthis.set(key, r, time, unit);//返回该值return r;}方法4 逻辑过期解决缓存击穿
public R, ID R queryWithLogicalExpire(String keyPrefix, ID id, ClassR type, FunctionID, R dbFallback, Long time, TimeUnit unit){//1、查看Cache是否命中String key keyPrefix id;String json stringRedisTemplate.opsForValue().get(key);if(Objects.isEmpty(json)){//命中判断为空白返回错误信息return null;}RedisData redisData this.toJsonObject(json, Redis.Class);R r type.cast(redisData.getData());LocalDateTime time redisData.getDateTime();//若逻辑时间小于现实时间则返回当前对象if(time.isBefore(LocalDateTime.now())){redisData JSON.parseObject(json, RedisData.class);if(redisData.getDateTime LocalDateTime.now()){return type.cast(redisData.getData());}}//过期了获取互斥锁String lockKey lock:shop:id;boolean isLock tryLock(lockKey);//尝试获取互斥锁如果事变则睡眠重试if(!isLock){//失败则休眠后重试Thread.sleep(50);return queryWithLogicalExpire(keyPrefix, id, type, dbFallback, time, unit);}// 重建数据Executors.newFixedThreadPool(10).submit(() - {try{// 查询数据库R r1 dbFallback.apply(id);this.setWithLogicalExpire(id, r1, time, unit);}catch(Exception e){throw new RuntimeException(e);}finally{unlock(lockKey);}});return r;
}private boolean tryLock(String key) {Boolean flag redisTemplate.opsForValue().setIfAbsent(key, , 10, TimeUnit.SECONDS);return Boolean.getBoolean(flag);
}private void unlock(String key) {redisTemplate.delete(key);
}