经典网站首页,乐清网络公司哪家好,微信平台的微网站怎么做,如何解压缩wordpress系列博客目录 文章目录 系列博客目录1.缓存穿透总结 2.缓存雪崩3.缓存击穿代码总结 1.缓存穿透
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在#xff0c;这样缓存永远不会生效#xff0c;这些请求都会打到数据库。
常见的解决方案有两种#xff1a;
缓存空对…系列博客目录 文章目录 系列博客目录1.缓存穿透总结 2.缓存雪崩3.缓存击穿代码总结 1.缓存穿透
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在这样缓存永远不会生效这些请求都会打到数据库。
常见的解决方案有两种
缓存空对象 优点实现简单维护方便缺点额外的内存消耗、可能造成短期的不一致 布隆过滤 优点内存占用较少没有多余key缺点实现复杂、存在误判可能
这里选择缓存空对象。 总结 缓存穿透产生的原因是什么 用户请求的数据在缓存中和数据库中都不存在不断发起这样的请求给数据库带来巨大压力 缓存穿透的解决方案有哪些 缓存null值布隆过滤增强id的复杂度避免被猜测id规律做好数据的基础格式校验加强用户权限校验做好热点参数的限流
2.缓存雪崩
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机导致大量请求到达数据库带来巨大压力。
解决方案
给不同的Key的TTL添加随机值利用Redis集群提高服务的可用性给缓存业务添加降级限流策略给业务添加多级缓存
这部分后面代码总结中没有因为就是简单的为TTL设置随机值。
3.缓存击穿
缓存击穿问题也叫热点Key问题就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了无数的请求访问会在瞬间给数据库带来巨大的冲击。 常见的解决方案有两种互斥锁、逻辑过期 案例基于逻辑过期方式解决缓存击穿问题
需求修改根据id查询商铺的业务基于逻辑过期方式来解决缓存击穿问题 这里最左边缓存未命中返回空而不进行其他操作比如查数据库回设缓存是为了主要掌握逻辑过期的逻辑而不注重于其他。
代码总结
package com.hmdp.service.impl;import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hmdp.dto.Result;
import com.hmdp.entity.Shop;
import com.hmdp.mapper.ShopMapper;
import com.hmdp.service.IShopService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisData;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;import static com.hmdp.utils.RedisConstants.*;/*** p* 服务实现类* /p** author 虎哥* since 2021-12-22*/
Service
public class ShopServiceImpl extends ServiceImplShopMapper, Shop implements IShopService {ResourceStringRedisTemplate stringRedisTemplate;Overridepublic Result queryById(Long id) {// 实现缓存穿透// Shop shop queryWithPassThrough(id);// 利用互斥锁解决缓存击穿// Shop shop queryWithMutex(id);// 利用逻辑过期解决缓存击穿Shop shop queryWithLogicalExpire(id);if (shop null) {return Result.fail(店铺不存在);}// 7.返回return Result.ok(shop);}private static final ExecutorService CACHE_REBUILD_EXECUTOR Executors.newFixedThreadPool(10);public Shop queryWithLogicalExpire(Long id){String key CACHE_SHOP_KEY id;// 1.从Redis查询商户缓存String shopJson stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if(StrUtil.isBlank(shopJson)){// 3.存在直接返回return null;}// 4.命中需要先把json反序列化为对象RedisData redisData JSONUtil.toBean(shopJson, RedisData.class);JSONObject data (JSONObject) redisData.getData();Shop shop JSONUtil.toBean(data, Shop.class);LocalDateTime expireTime redisData.getExpireTime();// 5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())){// 5.1 未过期直接返回店铺信息return shop;}// 5.2 已过期需要缓存重建// 6. 缓存重建// 6.1 获取互斥锁String lockKey LOCK_SHOP_KEY id;boolean isLock tryLock(lockKey);// 6.2 判断是否获取锁成功if(isLock){// 6.3 成功开启独立线程实现缓存重建CACHE_REBUILD_EXECUTOR.submit(() -{try {//重建缓存this.saveShop2Redis(id, 20L);} catch (Exception e) {throw new RuntimeException(e);} finally {// 释放锁unLock(lockKey);}});}// 6.4 不成功返回过期商铺信息return shop;}public Shop queryWithMutex(Long id){String key CACHE_SHOP_KEY id;// 1.从Redis查询商户缓存String shopJson stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if(StrUtil.isNotBlank(shopJson)){// 3.存在直接返回Shop shop JSONUtil.toBean(shopJson, Shop.class);return shop;}// 命中的是否是空值if(shopJson ! null){// 返回一个错误信息 , 现在是空值return null;}// 实现缓存重建// 4.1 获取互斥锁String lockKey lock:shop: id;Shop shop null;try {boolean isLock tryLock(lockKey);// 4.2 判断获取互斥锁是否成功if(!isLock){// 4.3 失败休眠并重试Thread.sleep(50);return queryWithMutex(id);}// 4.4 成功 根据id查询数据库shop getById(id);// 模拟重建延迟Thread.sleep(200);// 5.不存在返回错误if(shop null){// 将空值写入RedisstringRedisTemplate.opsForValue().set(key, , CACHE_NULL_TTL, TimeUnit.MINUTES);// 返回错误信息return null;}// 6.存在写入redisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);}finally {// 7.释放互斥锁unLock(lockKey);}// 8.返回return shop;}public Shop queryWithPassThrough(Long id){String key CACHE_SHOP_KEY id;// 1.从Redis查询商户缓存String shopJson stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if(StrUtil.isNotBlank(shopJson)){// 3.存在直接返回Shop shop JSONUtil.toBean(shopJson, Shop.class);return shop;}// 命中的是否是空值if(shopJson ! null){// 返回一个错误信息 , 现在是空值return null;}// 4.不存在根据id查询数据库Shop shop getById(id);// 5.不存在返回错误if(shop null){// 将空值写入RedisstringRedisTemplate.opsForValue().set(key, , CACHE_NULL_TTL, TimeUnit.MINUTES);// 返回错误信息return null;}// 6.存在写入redisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);// 7.返回return shop;}private boolean tryLock(String key){Boolean flag stringRedisTemplate.opsForValue().setIfAbsent(key, 1, 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unLock(String key){stringRedisTemplate.delete(key);}public void saveShop2Redis(Long id, Long expireSeconds) throws InterruptedException {// 1.查询店铺数据Shop shop getById(id);// 为了更好观察逻辑过期设置睡眠时间Thread.sleep(200);// 2.封装逻辑过期时间RedisData redisData new RedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));// 3.写入RedisstringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY id, JSONUtil.toJsonStr(redisData));}OverrideTransactionalpublic Result update(Shop shop) {Long id shop.getId();if(id null){return Result.fail(店铺id不能为空);}// 1.更新数据库updateById(shop);// 2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY shop.getId());return Result.ok();}}