当前位置: 首页 > news >正文

wordpress使用新浪图床seo培训优化课程

wordpress使用新浪图床,seo培训优化课程,网页微信注册,建立网站大概需要多少钱文章目录 背景代码实现前置实体类常量类工具类结果返回类控制层 缓存空对象布隆过滤器结合两种方法 背景 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库 常见的解决方案有两种,分别…

文章目录

  • 背景
  • 代码实现
    • 前置
      • 实体类
      • 常量类
      • 工具类
      • 结果返回类
      • 控制层
    • 缓存空对象
    • 布隆过滤器
    • 结合两种方法

背景

缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库

常见的解决方案有两种,分别是缓存空对象布隆过滤器

1.缓存空对象

image-20241025163728328

优点:实现简单,维护方便

缺点:额外的内存消耗、可能造成短期的不一致

2.布隆过滤器

image-20241025163737389

优点:内存占用较少,没有多余key

缺点:实现复杂、存在误判可能

代码实现

前置

这里以根据 id 查询商品店铺为案例

实体类

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_shop")
public class Shop implements Serializable {private static final long serialVersionUID = 1L;/*** 主键*/@TableId(value = "id", type = IdType.AUTO)private Long id;/*** 商铺名称*/private String name;/*** 商铺类型的id*/private Long typeId;/*** 商铺图片,多个图片以','隔开*/private String images;/*** 商圈,例如陆家嘴*/private String area;/*** 地址*/private String address;/*** 经度*/private Double x;/*** 维度*/private Double y;/*** 均价,取整数*/private Long avgPrice;/*** 销量*/private Integer sold;/*** 评论数量*/private Integer comments;/*** 评分,1~5分,乘10保存,避免小数*/private Integer score;/*** 营业时间,例如 10:00-22:00*/private String openHours;/*** 创建时间*/private LocalDateTime createTime;/*** 更新时间*/private LocalDateTime updateTime;@TableField(exist = false)private Double distance;
}

常量类

public class RedisConstants {public static final Long CACHE_NULL_TTL = 2L;public static final Long CACHE_SHOP_TTL = 30L;public static final String CACHE_SHOP_KEY = "cache:shop:";
}

工具类

public class ObjectMapUtils {// 将对象转为 Mappublic static Map<String, String> obj2Map(Object obj) throws IllegalAccessException {Map<String, String> result = new HashMap<>();Class<?> clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// 如果为 static 且 final 则跳过if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) {continue;}field.setAccessible(true); // 设置为可访问私有字段Object fieldValue = field.get(obj);if (fieldValue != null) {result.put(field.getName(), field.get(obj).toString());}}return result;}// 将 Map 转为对象public static Object map2Obj(Map<Object, Object> map, Class<?> clazz) throws Exception {Object obj = clazz.getDeclaredConstructor().newInstance();for (Map.Entry<Object, Object> entry : map.entrySet()) {Object fieldName = entry.getKey();Object fieldValue = entry.getValue();Field field = clazz.getDeclaredField(fieldName.toString());field.setAccessible(true); // 设置为可访问私有字段String fieldValueStr = fieldValue.toString();// 根据字段类型进行转换if (field.getType().equals(int.class) || field.getType().equals(Integer.class)) {field.set(obj, Integer.parseInt(fieldValueStr));} else if (field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)) {field.set(obj, Boolean.parseBoolean(fieldValueStr));} else if (field.getType().equals(double.class) || field.getType().equals(Double.class)) {field.set(obj, Double.parseDouble(fieldValueStr));} else if (field.getType().equals(long.class) || field.getType().equals(Long.class)) {field.set(obj, Long.parseLong(fieldValueStr));} else if (field.getType().equals(String.class)) {field.set(obj, fieldValueStr);} else if(field.getType().equals(LocalDateTime.class)) {field.set(obj, LocalDateTime.parse(fieldValueStr));}}return obj;}}

结果返回类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {private Boolean success;private String errorMsg;private Object data;private Long total;public static Result ok(){return new Result(true, null, null, null);}public static Result ok(Object data){return new Result(true, null, data, null);}public static Result ok(List<?> data, Long total){return new Result(true, null, data, total);}public static Result fail(String errorMsg){return new Result(false, errorMsg, null, null);}
}

控制层

@RestController
@RequestMapping("/shop")
public class ShopController {@Resourcepublic IShopService shopService;/*** 根据id查询商铺信息* @param id 商铺id* @return 商铺详情数据*/@GetMapping("/{id}")public Result queryShopById(@PathVariable("id") Long id) {return shopService.queryShopById(id);}/*** 新增商铺信息* @param shop 商铺数据* @return 商铺id*/@PostMappingpublic Result saveShop(@RequestBody Shop shop) {return shopService.saveShop(shop);}/*** 更新商铺信息* @param shop 商铺数据* @return 无*/@PutMappingpublic Result updateShop(@RequestBody Shop shop) {return shopService.updateShop(shop);}
}

缓存空对象

流程图为:

image-20241025165838030

服务层代码:

public Result queryShopById(Long id) {// 从 redis 查询String shopKey = RedisConstants.CACHE_SHOP_KEY + id;Map<Object, Object> entries = redisTemplate.opsForHash().entries(shopKey);// 缓存命中if(!entries.isEmpty()) {try {// 如果是空对象,表示一定不存在数据库中,直接返回(解决缓存穿透)if(entries.containsKey("")) {return Result.fail("店铺不存在");}// 刷新有效期redisTemplate.expire(shopKey, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);Shop shop = (Shop) ObjectMapUtils.map2Obj(entries, Shop.class);return Result.ok(shop);} catch (Exception e) {throw new RuntimeException(e);}}// 查询数据库Shop shop = this.getById(id);if(shop == null) {// 存入空值redisTemplate.opsForHash().put(shopKey, "", "");redisTemplate.expire(shopKey, RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);// 不存在,直接返回return Result.fail("店铺不存在");}// 存在,写入 redistry {redisTemplate.opsForHash().putAll(shopKey, ObjectMapUtils.obj2Map(shop));redisTemplate.expire(shopKey, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (IllegalAccessException e) {throw new RuntimeException(e);}return Result.ok(shop);
}

布隆过滤器

这里选择使用布隆过滤器存储存在于数据库中的 id,原因在于,如果存储了不存在于数据库中的 id,首先由于 id 的取值范围很大,那么不存在的 id 有很多,因此更占用空间;其次,由于布隆过滤器有一定的误判率,那么可能导致少数原本存在于数据库中的 id 被判为了不存在,然后直接返回了,此时就会出现根本性的正确性错误。相反,如果存储的是数据库中存在的 id,那么即使少数不存在的 id 被判为了存在,由于数据库中确实没有对应的 id,那么也会返回空,最终结果还是正确的

这里使用 guava 依赖的布隆过滤器

依赖为:

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1.1-jre</version>
</dependency>

封装了布隆过滤器的类(注意初始化时要把数据库中已有的 id 加入布隆过滤器):

public class ShopBloomFilter {private BloomFilter<Long> bloomFilter;public ShopBloomFilter(ShopMapper shopMapper) {// 初始化布隆过滤器,设计预计元素数量为100_0000L,误差率为1%bloomFilter = BloomFilter.create(Funnels.longFunnel(), 100_0000, 0.01);// 将数据库中已有的店铺 id 加入布隆过滤器List<Shop> shops = shopMapper.selectList(null);for (Shop shop : shops) {bloomFilter.put(shop.getId());}}public void add(long id) {bloomFilter.put(id);}public boolean mightContain(long id){return bloomFilter.mightContain(id);}}

对应的配置类(将其设置为 bean)

@Configuration
public class BloomConfig {@Beanpublic ShopBloomFilter shopBloomFilter(ShopMapper shopMapper) {return new ShopBloomFilter(shopMapper);}}

首先要修改查询方法,在根据 id 查询时,如果对应 id 不在布隆过滤器中,则直接返回。然后还要修改保存方法,在保存的时候还需要将对应的 id 加入布隆过滤器中

@Override
public Result queryShopById(Long id) {// 如果不在布隆过滤器中,直接返回if(!shopBloomFilter.mightContain(id)) {return Result.fail("店铺不存在");}// 从 redis 查询String shopKey = RedisConstants.CACHE_SHOP_KEY + id;Map<Object, Object> entries = redisTemplate.opsForHash().entries(shopKey);// 缓存命中if(!entries.isEmpty()) {try {// 刷新有效期redisTemplate.expire(shopKey, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);Shop shop = (Shop) ObjectMapUtils.map2Obj(entries, Shop.class);return Result.ok(shop);} catch (Exception e) {throw new RuntimeException(e);}}// 查询数据库Shop shop = this.getById(id);if(shop == null) {// 不存在,直接返回return Result.fail("店铺不存在");}// 存在,写入 redistry {redisTemplate.opsForHash().putAll(shopKey, ObjectMapUtils.obj2Map(shop));redisTemplate.expire(shopKey, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (IllegalAccessException e) {throw new RuntimeException(e);}return Result.ok(shop);
}@Override
public Result saveShop(Shop shop) {// 写入数据库this.save(shop);// 将 id 写入布隆过滤器shopBloomFilter.add(shop.getId());// 返回店铺 idreturn Result.ok(shop.getId());
}

结合两种方法

由于布隆过滤器有一定的误判率,所以这里可以进一步优化,如果出现误判情况,即原本不存在于数据库中的 id 被判为了存在,就用缓存空对象的方式将其缓存到 redis 中

@Override
public Result queryShopById(Long id) {// 如果不在布隆过滤器中,直接返回if(!shopBloomFilter.mightContain(id)) {return Result.fail("店铺不存在");}// 从 redis 查询String shopKey = RedisConstants.CACHE_SHOP_KEY + id;Map<Object, Object> entries = redisTemplate.opsForHash().entries(shopKey);// 缓存命中if(!entries.isEmpty()) {try {// 如果是空对象,表示一定不存在数据库中,直接返回(解决缓存穿透)if(entries.containsKey("")) {return Result.fail("店铺不存在");}// 刷新有效期redisTemplate.expire(shopKey, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);Shop shop = (Shop) ObjectMapUtils.map2Obj(entries, Shop.class);return Result.ok(shop);} catch (Exception e) {throw new RuntimeException(e);}}// 查询数据库Shop shop = this.getById(id);if(shop == null) {// 存入空值redisTemplate.opsForHash().put(shopKey, "", "");redisTemplate.expire(shopKey, RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);// 不存在,直接返回return Result.fail("店铺不存在");}// 存在,写入 redistry {redisTemplate.opsForHash().putAll(shopKey, ObjectMapUtils.obj2Map(shop));redisTemplate.expire(shopKey, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (IllegalAccessException e) {throw new RuntimeException(e);}return Result.ok(shop);
}

文章转载自:
http://wanjiaansi.kryr.cn
http://wanjiamacroclimate.kryr.cn
http://wanjiahuff.kryr.cn
http://wanjiahydrargyrism.kryr.cn
http://wanjiaoverfired.kryr.cn
http://wanjiatheorize.kryr.cn
http://wanjiaenjoinder.kryr.cn
http://wanjiafracture.kryr.cn
http://wanjiasober.kryr.cn
http://wanjiazoonomy.kryr.cn
http://wanjiaantifreezing.kryr.cn
http://wanjiaoxycalcium.kryr.cn
http://wanjiaanisette.kryr.cn
http://wanjiaguitarist.kryr.cn
http://wanjiagest.kryr.cn
http://wanjiascarey.kryr.cn
http://wanjiaaddicted.kryr.cn
http://wanjiajaspery.kryr.cn
http://wanjiaprovincial.kryr.cn
http://wanjiarevises.kryr.cn
http://wanjiasubmillimetre.kryr.cn
http://wanjiaareopagitic.kryr.cn
http://wanjiarecusant.kryr.cn
http://wanjiaexploitable.kryr.cn
http://wanjiaasking.kryr.cn
http://wanjialassen.kryr.cn
http://wanjiarundlet.kryr.cn
http://wanjialooney.kryr.cn
http://wanjiabuchenwald.kryr.cn
http://wanjianork.kryr.cn
http://wanjiafaineancy.kryr.cn
http://wanjiadepilatory.kryr.cn
http://wanjiatelosyndesis.kryr.cn
http://wanjiaroomful.kryr.cn
http://wanjiacontemplator.kryr.cn
http://wanjiavelocimeter.kryr.cn
http://wanjialaughably.kryr.cn
http://wanjiapersorption.kryr.cn
http://wanjianakedize.kryr.cn
http://wanjiaiodin.kryr.cn
http://wanjiaendarteritis.kryr.cn
http://wanjiavideophone.kryr.cn
http://wanjiaguntz.kryr.cn
http://wanjiacommunicative.kryr.cn
http://wanjiamanchurian.kryr.cn
http://wanjiadisinfest.kryr.cn
http://wanjiabases.kryr.cn
http://wanjiaunpack.kryr.cn
http://wanjiamotivic.kryr.cn
http://wanjiadistribute.kryr.cn
http://wanjiaautoplastic.kryr.cn
http://wanjiatsugaru.kryr.cn
http://wanjiaarchbishop.kryr.cn
http://wanjiaatomist.kryr.cn
http://wanjiaduper.kryr.cn
http://wanjiasmirky.kryr.cn
http://wanjiasyntactic.kryr.cn
http://wanjiamarasca.kryr.cn
http://wanjiainaccessibility.kryr.cn
http://wanjiagenevra.kryr.cn
http://wanjiaruthfulness.kryr.cn
http://wanjiaprissy.kryr.cn
http://wanjiarapturously.kryr.cn
http://wanjiaarthrology.kryr.cn
http://wanjiasheepherder.kryr.cn
http://wanjiaphone.kryr.cn
http://wanjiadicing.kryr.cn
http://wanjiacavity.kryr.cn
http://wanjiagenealogical.kryr.cn
http://wanjiaeventide.kryr.cn
http://wanjiacheliform.kryr.cn
http://wanjiaslovene.kryr.cn
http://wanjiadisencumber.kryr.cn
http://wanjialayout.kryr.cn
http://wanjiahangzhou.kryr.cn
http://wanjiaadmonishment.kryr.cn
http://wanjiaunsullied.kryr.cn
http://wanjiaahl.kryr.cn
http://wanjiagrits.kryr.cn
http://wanjiagametal.kryr.cn
http://www.15wanjia.com/news/113204.html

相关文章:

  • 海南棋牌网站建设网站排名优化首页
  • 高端网站定制的方法2345网址导航怎么样
  • 做网站用什么语言好爱站网关键词
  • 一流的南昌网站建设免费外链代发平台
  • 怎么快速建设小型外贸网站seo网络优化软件
  • 天津建设网站公司外链发布软件
  • 电子商务网站开发常用工具口碑营销策划方案
  • 网页代码转wordpress网络seo哈尔滨
  • 自己做菠菜网站网站及搜索引擎优化建议
  • 个人网站制作代码营销推广手段有什么
  • 租服务器的网站seo优化师是什么
  • 公司做网站需要提供什么条件人员优化是什么意思
  • 设计一个外贸网站需要多少钱百度动态排名软件
  • 网站建设与网页设计案例教程 重庆大学出版社户外广告
  • 做网站去什么公司好如何创建一个自己的网站
  • 做网站属于It行业吗长沙 建站优化
  • 静态中英文网站怎么做外贸网站推广平台有哪些
  • 做阀门网站百度推广代运营公司
  • 专门做视频的网站邯郸网站建设优化
  • 网站页面设计网页说明拼多多代运营公司十大排名
  • 做网站用什么网名好桂林seo排名
  • 免费做deal的网站百度竞价推广点击软件
  • 把自己的电脑做网站服务器成都网站维护
  • 公司网站页面google seo实战教程
  • 做网站公司排名多少钱360收录提交
  • 在职考研哪个网站做的好中国十大网络销售公司
  • 赣州做网站jx25舆情分析
  • 在库言库建筑网站附近电脑培训学校
  • 动态网站开发代码十大搜索引擎地址
  • 广东疫情最新通知今天厦门seo关键词排名