手机端公司网站怎么做ip查询网站备案查询系统

news/2025/10/8 2:57:58/文章来源:
手机端公司网站怎么做,ip查询网站备案查询系统,wordpress没有备案,做a 免费网站↑↑↑请在文章头部下载测试项目原代码↑↑↑ 文章目录 前言4.2 商户查询缓存4.2.1 缓存介绍4.2.2 查询商户信息的传统做法4.2.2.1 接口文档4.2.2.2 代码实现4.2.2.3 功能测试 4.2.3 查询商户信息添加Redis缓存4.2.3.1 逻辑分析4.2.3.2 代码实现4.2.3.3 功能测试 4.2.3 数据一致…↑↑↑请在文章头部下载测试项目原代码↑↑↑ 文章目录 前言4.2 商户查询缓存4.2.1 缓存介绍4.2.2 查询商户信息的传统做法4.2.2.1 接口文档4.2.2.2 代码实现4.2.2.3 功能测试 4.2.3 查询商户信息添加Redis缓存4.2.3.1 逻辑分析4.2.3.2 代码实现4.2.3.3 功能测试 4.2.3 数据一致性问题及其解决方案4.2.3.1 问题分析4.2.3.2 实现商户信息的双写一致 4.2.4 缓存穿透问题及其解决方案4.2.4.1 什么是缓存穿透问题4.2.4.2 缓存穿透问题的解决方案4.2.4.3 基于缓存空对象解决缓存穿透问题 4.2.5 缓存击穿问题及其解决4.2.5.1 什么是缓存击穿问题4.2.5.2 缓存击穿问题的解决方案4.2.5.3 利用互斥锁解决缓存击穿问题4.2.5.4 利用逻辑过期解决缓存击穿问题 前言 上一节实现了Redis实战项目的第一个功能短信登录。项目最初采用session方式实现短信登录但该方式存在session共享问题因此改为基于Redis来实现。 Redis从入门到精通(四)Redis实战(一)短信登录 4.2 商户查询缓存 4.2.1 缓存介绍 缓存Cache即数据交换的缓存区俗称的缓存就是指缓存区中的数据。缓存最大的特点就是它运行在内存中速度快可以大大降低用户访问并发量带来的服务器读写压力。 但缓存也有不足之处就是会增加代码复杂度和运营成本 在实际开发中会构筑多级缓存来使系统运行速度进一步提升包括 浏览器缓存浏览器在用户磁盘上对最近请求过的文档进行存储当访问者再次请求这个页面时浏览器就可以从本地磁盘显示文档这样就可以加速页面的阅览。。应用层缓存例如Tomcat本地缓存或Redis缓存。数据库缓存例如MySQL缓存是指MySQL数据库服务器中的内存区域用于存储经常访问的数据和查询结果以提高查询性能和响应时间。。CPU缓存CPU的L1、L2、L3级缓存。 4.2.2 查询商户信息的传统做法 根据ID查询商户信息传统做法是直接从数据库查询并将查询结果返回。 4.2.2.1 接口文档 项目说明请求方式GET请求路径/shop/{id}请求参数id返回值Shop 4.2.2.2 代码实现 在ShopController类中实现一个queryById()方法调用IShopService接口的queryShopById()方法 // com.star.redis.dzdp.controller.ShopControllerSlf4j RestController RequestMapping(/shop) public class ShopController {Resourceprivate IShopService shopService;/*** 根据ID查询商户信息* author hsgx* since 2024/4/3 11:23* param id* return com.star.redis.dzdp.pojo.BaseResultcom.star.redis.dzdp.pojo.Shop*/GetMapping(/{id})public BaseResultShop queryById(PathVariable Long id) {return shopService.queryShopById(id);} }然后在IShopService接口的实现类ShopServiceImpl类中实现queryShopById()方法 // com.star.redis.dzdp.service.impl.ShopServiceImplSlf4j Service Transactional(rollbackFor Exception.class) public class ShopServiceImpl extends ServiceImplShopMapper, Shop implements IShopService {Overridepublic BaseResultShop queryShopById(Long id) {log.info(query Shop by id {}, id);// 查询数据库Shop shop getById(id);if(shop null) {return BaseResult.setFail(商户不存在);}return BaseResult.setOkWithData(shop);} }4.2.2.3 功能测试 当id100时返回“商户不存在” 当id1时成功获取到商户信息耗时62ms 4.2.3 查询商户信息添加Redis缓存 4.2.3.1 逻辑分析 通常情况下该功能的逻辑是在查询数据库之前先查询Redis如果Redis中存在数据则直接从Redis中返回数据如果Redis中没有数据再查询数据库并将查询结果保存到Redis中。 4.2.3.2 代码实现 修改ShopServiceImpl类中实现queryShopById()方法添加查询Redis的逻辑 // com.star.redis.dzdp.service.impl.ShopServiceImplResource private StringRedisTemplate stringRedisTemplate;Override public BaseResultShop queryShopById(Long id) {log.info(query Shop by id {}, id);// 1.构建Key并从Redis中查询商户信息String key cache:shop: id;String shopJson stringRedisTemplate.opsForValue().get(key);log.info(get from Redis: Key {}, Value {}, key, shopJson);// 2.判断商户信息是否存在if(StrUtil.isNotBlank(shopJson)) {// 3.存在直接返回Shop shop JSONUtil.toBean(shopJson, Shop.class);return BaseResult.setOkWithData(shop);}// 4.不存在根据ID查询数据库Shop shop getById(id);if(shop null) {return BaseResult.setFail(商户不存在);}// 5.将商户信息写入RedisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));log.info(set to Redis: Key {}, Value {}, key, JSONUtil.toJsonStr(shop));// 6. 返回信息return BaseResult.setOkWithData(shop); }4.2.3.3 功能测试 当id1时由于此时Redis中还没有数据所以会从数据库查询数据并保存到Redis。控制台打印信息如下省略了一些不重要的信息 query Shop by id 1 get from Redis: Key cache:shop:1, Value nullPreparing: SELECT id,name,type_id,images,area,address,x,y,avg_price,sold,comments,score,open_hours,create_time,update_time FROM tb_shop WHERE id?Parameters: 1(Long)Total: 1 set to Redis: Key cache:shop:1, Value {area:大关,openHours:10:00-22:00,...省略...,id:1}此时可以查询Redis中已经保存了商户的数据 再次查询id1的商户信息由于此时Redis中已经存在数据所以会直接从Redis中返回。控制台打印信息如下省略了一些不重要的信息 query Shop by id 1 get from Redis: Key cache:shop:1, Value {area:大关,openHours:10:00-22:00,...省略...,id:1}再比较一下两次查询的性能第二次查询耗时15ms比前面直接查询数据库的方式的62ms要快。 4.2.3 数据一致性问题及其解决方案 4.2.3.1 问题分析 如果我们向Redis添加的大量数据就会导致Redis缓存中的数据过多为了节约宝贵的内存资源Redis会对部分数据进行动态更新。 主要有三种方式 1内存淘汰 在Redis的配置文件redis.conf中有两个关于内存淘汰的配置 # 最大内存限制 maxmemory 512mb # 当达到最大内存限制时的缓存更新策略 maxmemory-policy noevictionmaxmemory用于配置最大内存限制当Redis中保存的数据超过该限制时就会根据maxmemory-policy配置的策略自动淘汰一部分数据。 2超时剔除 Redis支持单独为每个Key设置有效期超过有效期后Redis会自动删除。 3主动更新 我们还可以手动调用Redis的DEL命令删除数据通常用于解决缓存和数据库不一致的问题。 Redis缓存中的数据来源于数据库因此当数据库的数据发生变化时如果Redis缓存没有同步就会产生数据一致性问题。 解决数据一致性问题也有三种方案 1Cache Aside Pattern人工编码方式由开发者在更新完数据库后再去更新缓存也称之为双写方案。2Read/Write Through Pattern缓存与数据库整合为一个服务由该服务来维护缓存与数据库的数据一致性。3Write Behind Caching Pattern调用者只操作缓存由其他线程去异步处理数据库实现数据的最终一致。 通常情况下双写方案是最易实现且可靠性最好的方案。因此本案例采用的就是双写方案。 下面继续来考虑双写方案的两个问题 1无效写操作过多问题 假设需要对数据库的记录进行N次修改。如果每次修改记录后都更新缓存则需要更新N次缓存但如果在这N次修改期间并没有另一个用户查询数据那对缓存来说只有最后一次更新是有效的中间的几次更新都是无效的。这就导致了无效写操作较多的问题。 要避免这个问题可以放弃每次修改记录都更新缓存的方案而是在修改记录后删除缓存等待再次查询时再更新缓存。这样就能确保缓存中的数据始终是最新的且避免多次写操作。 2操作顺序问题 先修改数据库再更新缓存还是先更新缓存再修改数据库 答案是先修改数据库再更新缓存。 原因如下 4.2.3.2 实现商户信息的双写一致 1修改ShopServiceImpl类的queryShopById()方法 根据id查询商户时如果缓存中没有则查询数据库再将数据库结果写入缓存并返回。此处将写入缓存这一步增设超时时间。到时间后商户缓存自动失效再次查询时自动更新为最新信息。 // com.star.redis.dzdp.service.impl.ShopServiceImpl#queryShopById()// 5.将商户信息写入Redis // 旧逻辑 // stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop)); // 新逻辑增设超时时间30分钟 stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), 30, TimeUnit.MINUTES);2实现根据ID修改商户信息功能 接口文档如下 项目说明请求方式POST请求路径/shop/edit请求参数Shop返回值无 在ShopController类中白那些一个editById()方法调用IShopService接口的editShopById()方法 // com.star.redis.dzdp.controller.ShopController/*** 根据ID修改商户信息* author hsgx* since 2024/4/3 15:17* param shop * return com.star.redis.dzdp.pojo.BaseResult*/ PostMapping(/edit) public BaseResult edit(RequestBody Shop shop) {return shopService.editShopById(shop); }然后在IShopService接口的实现类ShopServiceImpl类中编写editShopById()方法 // com.star.redis.dzdp.service.impl.ShopServiceImplOverride public BaseResult editShopById(Shop shop) {log.info(edit Shop by id {}, shop.toString());if(shop.getId() null) {return BaseResult.setFail(商户ID不能为空);}// 1.更新数据库记录updateById(shop);// 2.删除缓存Boolean delete stringRedisTemplate.delete(cache:shop: shop.getId());log.info(delete from Redis: Key {}, result {}, cache:shop: shop.getId(), delete);return BaseResult.setOk(); }最后进行功能测试 调用/shop/1接口获取id1的商户信息日志显示从Redis直接返回 query Shop by id 1 get from Redis: Key cache:shop:1, Value {area:大关,openHours:10:00-22:00,...省略...,id:1}调用/shop/edit接口修改id1的商户信息日志显示先修改数据库记录后删除Redis缓存 edit Shop by id Shop(id1, namenull, typeIdnull, imagesnull, area修改后的area, addressnull, xnull, ynull, avgPricenull, soldnull, commentsnull, scorenull, openHoursnull, createTimenull, updateTimenull, distancenull)Preparing: UPDATE tb_shop SET area? WHERE id?Parameters: 修改后的area(String), 1(Long)Updates: 1 delete from Redis: Key cache:shop:1, result true再次调用/shop/1接口获取id1的商户信息日志显示从数据库查询后再存入Redis且数据是最新的 query Shop by id 1 get from Redis: Key cache:shop:1, Value nullPreparing: SELECT id,name,type_id,images,area,address,x,y,avg_price,sold,comments,score,open_hours,create_time,update_time FROM tb_shop WHERE id?Parameters: 1(Long)Total: 1 set to Redis: Key cache:shop:1, Value {area:修改后的area,openHours:10:00-22:00,...省略...,id:1}4.2.4 缓存穿透问题及其解决方案 4.2.4.1 什么是缓存穿透问题 查询商户信息时目前的做法仍然存在漏洞即当id100时Redis和数据库都没有数据程序最终会操作数据库。那如果有大量id100的查询请求每次都会去查询数据库Redis缓存就相当于摆设了。 这就是缓存穿透问题即客户端请求的数据在缓存中和数据库中都不存在这些请求会穿透缓存直击数据库给数据库造成压力。 4.2.4.2 缓存穿透问题的解决方案 常见的解决缓存穿透问题的方案有两种缓存空对象、布隆过滤。 1缓存空对象 服务端接收到查询请求后如果Redis和数据库都不存在也将这个数据存入Redis只是其Value值设置为空字符串。那么下次查询这个不存在的数据时在Redis这里就可以知道数据不存在了而不用继续访问数据库。 缓存空对象的优点在于实现简单、维护方便缺点在于有额外的内存消耗。 2布隆过滤 布隆过滤是指采用哈希思想来解决这个问题通过一个庞大的二进制数组利用哈希思想判断当前要查询的数据是否存在。如果布隆过滤器判断存在则放行这个请求会去访问Redis或数据库如果布隆过滤器判断这个数据不存在则直接返回不存在。 布隆过滤的优点在于节约内存空间没有多余的Key缺点是布隆过滤器走的是哈希思想可能存在误判。 综合考虑本案例采用缓存空对象的方案。 4.2.4.3 基于缓存空对象解决缓存穿透问题 修改ShopServiceImpl类的queryShopById方法 // com.star.redis.dzdp.service.impl.ShopServiceImpl#queryShopById()// 2.判断商户信息是否存在 if(StrUtil.isNotBlank(shopJson)) {// 3.存在直接返回Shop shop JSONUtil.toBean(shopJson, Shop.class);return BaseResult.setOkWithData(shop); } else if(.equals(shopJson)) {// 新增逻辑如果保存了空字符串则说明是商户信息不存在直接返回不存在return BaseResult.setFail(商户不存在); }// 4.不存在根据ID查询数据库 Shop shop getById(id); // 旧逻辑shop为空时直接返回不存在 // 新逻辑要将字符串写入Redis因此这里不直接返回 // if(shop null) { // return BaseResult.setFail(商户不存在); // }// 5.将商户信息写入Redis // 新逻辑如果商户信息为空则将空字符串写入Redis stringRedisTemplate.opsForValue().set(key,shop ! null ? JSONUtil.toJsonStr(shop) : ,30, TimeUnit.MINUTES);接下来进行功能测试调用/shop/100接口获取id100的商户信息日志显示将空字符串写入了Redis query Shop by id 100 get from Redis: Key cache:shop:100, Value nullPreparing: SELECT id,name,type_id,images,area,address,x,y,avg_price,sold,comments,score,open_hours,create_time,update_time FROM tb_shop WHERE id?Parameters: 100(Long)Total: 0 set to Redis: Key cache:shop:100, Value null查看Redis中保存的数据 再次调用/shop/100接口获取id100的商户信息日志显示从Redis中获取到了空值注解返回商户不存在 query Shop by id 100 get from Redis: Key cache:shop:100, Value 4.2.5 缓存击穿问题及其解决 4.2.5.1 什么是缓存击穿问题 缓存击穿问题也叫热点Key问题就是一个被高并发访问并且缓存重建业务较复杂的Key突然失效了大量的请求击穿缓存到达数据库给数据库造成压力。 例如 如上图所示线程1查询缓存未命中然后继续查询数据库并重建缓存数据但由于重建缓存数据业务复杂没等重建完成线程2、3、4就来查询缓存了线程2、3、4都不能从缓存中查到数据转而去查数据库。最终结果是4个线程都访问了数据库给数据库造成压力。 4.2.5.2 缓存击穿问题的解决方案 解决方案一互斥锁 如上图所示线程1查询缓存未命中然后会获取互斥锁获取到了锁再继续查询数据库并重建缓存数据最后释放锁。而此时线程2查询缓存未命中也去获取互斥锁但锁只能一个线程使用所以会获取失败转而进入休眠状态不断地尝试查询缓存和获取锁这两步等线程1重建缓存完成并释放锁后线程2获取到互斥锁就能跳出休眠状态从缓冲中获取到数据了。 解决方案二逻辑过期 如上图所示逻辑过期方案的要点是不对key设置过期时间而是将过期时间设置在Value值中。 假设线程1查询缓存发现逻辑时间已经过期则开启一个新的线程2并直接返回过期的数据。线程2获取互斥锁去查询数据库和重构缓存数据完成后释放锁。假设线程3过来访问由于线程2持有着锁所以线程3无法获得锁线程3也直接返回过期数据只有等到线程2重建缓存数据完成后才能返回正确的数据。 两种解决方案各有优缺点 互斥锁由于保证了互斥性所以数据一致且实现简单仅仅只需要加一把锁没有额外的内存消耗缺点在于有锁就有死锁问题的发生且只能串行执行性能会受到影响。 逻辑过期线程读取过程中不需要等待性能好有一个额外的线程持有锁去进行重构数据但是在重构数据完成前其他的线程只能返回之前的脏数据且实现起来麻烦。 4.2.5.3 利用互斥锁解决缓存击穿问题 如上图所示要利用互斥锁解决缓存击穿问题可以在缓存未命中时尝试获取互斥锁只有获取到了互斥锁才能查询数据库并将查询结果写入Redis而没有获取到互斥锁时则进行休眠并重试查询缓存。 在持有互斥锁的线程完成查询数据库并将查询结果写入Redis的动作之前其他没有获得互斥锁的线程不断重复地从Redis拿数据直到互斥锁被释放后才能从Redis拿到数据。 下面正式进行代码编写。首先要对互斥锁进行设计 Redis的String类型有一个SETNX方法该方法会尝试添加一个String类型的键值对前提是这个key不存在否则不执行。我们可以利用这个方法来获取互斥锁如果调用该方法成功说明Key不存在获取互斥锁成功调用该方法失败说明Key已存在获取互斥锁失败。 在ShopServiceImpl类中编写获取互斥锁的tryLock()方法以及释放锁的unLock()方法 // com.star.redis.dzdp.service.impl.ShopServiceImpl/*** 获取互斥锁* author hsgx* since 2024/4/3 17:19* param key* return boolean*/ private boolean tryLock(String key) {Boolean flag stringRedisTemplate.opsForValue().setIfAbsent(key, 1, 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag); }/*** 释放锁* author hsgx* since 2024/4/3 17:20* param key* return void*/ private void unLock(String key) {stringRedisTemplate.delete(key); }接着在IShopService接口中定义一个queryShopByIdWithLock()方法并在其实现类ShopServiceImpl中具体实现功能仍然是根据ID查询商户信息只是添加了互斥锁 // com.star.redis.dzdp.service.impl.ShopServiceImplpublic BaseResultShop queryShopByIdWithLock(Long id) {log.info(query Shop by id {}, id);// 1.构建Key并从Redis中查询商户信息String key cache:shop: id;String shopJson stringRedisTemplate.opsForValue().get(key);log.info(get from Redis: Key {}, Value {}, key, shopJson);// 2.判断商户信息是否存在if(StrUtil.isNotBlank(shopJson)) {// 存在直接返回Shop shop JSONUtil.toBean(shopJson, Shop.class);return BaseResult.setOkWithData(shop);} else if(.equals(shopJson)) {// 如果保存了空字符串则说明是商户信息不存在直接返回不存在return BaseResult.setFail(商户不存在);}// 3.使用互斥锁方案解决缓存击穿问题String lockKey lock:shop: id;Shop shop null;try {// 尝试获取互斥锁boolean isLock tryLock(lockKey);log.info(isLock {}, isLock);if(!isLock) {// 没有获取到互斥锁则休眠重试log.info(Retry queryShopByIdWithLock()...);Thread.sleep(50);return queryShopByIdWithLock(id);}// 获取到了互斥锁则根据ID查询数据并重构缓存数据shop getById(id);if(shop null) {// 商户不存在保存空字符串stringRedisTemplate.opsForValue().set(key, , 30, TimeUnit.MINUTES);log.info(set to Redis: Key {}, Value \\, key);return BaseResult.setFail(商户不存在);} else {stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), 30, TimeUnit.MINUTES);log.info(set to Redis: Key {}, Value {}, key, JSONUtil.toJsonStr(shop));}} catch (Exception e) {e.printStackTrace();} finally {// 释放锁unLock(lockKey);}return BaseResult.setOkWithData(shop); }然后修改ShopController类中的调用方法 // com.star.redis.dzdp.controller.ShopControllerGetMapping(/{id}) public BaseResultShop queryById(PathVariable Long id) {//return shopService.queryShopById(id);// 使用互斥锁解决缓存击穿问题return shopService.queryShopByIdWithLock(id); }修改完毕后进行功能测试首先在shop getById(id);这一行代码上打一个断点用于模拟重建缓存数据的时间很久。 然后调用/shop/102接口获取id102的商户信息日志显示线程5从Redis中没有获取到数据并获取互斥锁成功进入查询数据库和重建缓存的逻辑停在了断点处 [http-nio-8081-exec-5] query Shop by id 102 [http-nio-8081-exec-5] get from Redis: Key cache:shop:102, Value null [http-nio-8081-exec-5] tryLock key lock:shop:102 [http-nio-8081-exec-5] isLock true接着再次调用/shop/102接口获取id102的商户信息日志显示线程6从Redis中没有获取到数据并获取互斥锁失败进入重试逻辑再次调用queryShopByIdWithLock()方法 [http-nio-8081-exec-6] query Shop by id 102 [http-nio-8081-exec-6] get from Redis: Key cache:shop:102, Value null [http-nio-8081-exec-6] tryLock key lock:shop:102 [http-nio-8081-exec-6] isLock false [http-nio-8081-exec-6] Retry queryShopByIdWithLock()...此时放开断点线程5继续执行查询数据库信息并将查询结果写入Redis最后释放锁 [http-nio-8081-exec-5] Preparing: SELECT id,name,type_id,images,area,address,x,y,avg_price,sold,comments,score,open_hours,create_time,update_time FROM tb_shop WHERE id? [http-nio-8081-exec-5] Parameters: 102(Long) [http-nio-8081-exec-5] Total: 0 [http-nio-8081-exec-5] set to Redis: Key cache:shop:102, Value 锁释放后线程6已可以从Redis中拿到数据并返回结果 [http-nio-8081-exec-6] query Shop by id 102 [http-nio-8081-exec-6] get from Redis: Key cache:shop:102, Value 至此基于互斥锁的方案测试完成。 4.2.5.4 利用逻辑过期解决缓存击穿问题 由上图可知当查询缓存未命中时则直接返回空如果命中了则将缓存数据中的Value值取出判断Value值中保存的时间是否过期如果没有过期则直接返回缓存数据如果过期了则获取互斥锁并开启一个独立的线程后直接返回过期数据。 独立线程负责查询数据库并更新缓存数据。而其他线程在获得过期数据后由于无法获取互斥锁也直接返回过期数据。 下面开始编写代码。由于要在Value值中保存过期时间我们首先要重新定义一个保存到Redis的实体类。 // com.star.redis.dzdp.pojo.RedisDataData public class RedisData {private Date expireTime;private Object data; }接着在IShopService接口中定义一个queryShopByIdWithExpire()方法并在其实现类ShopServiceImpl中具体实现功能仍然是根据ID查询商户信息只是添加了逻辑过期 // com.star.redis.dzdp.service.impl.ShopServiceImplprivate static final ExecutorService CACHE_REBUILD_EXECUTOR Executors.newFixedThreadPool(10);Override public BaseResultShop queryShopByIdWithExpire(Long id) {log.info(query Shop by id {}, id);// 1.构建Key并从Redis中查询商户信息String key cache:shop: id;String shopJson stringRedisTemplate.opsForValue().get(key);log.info(get from Redis: Key {}, Value {}, key, shopJson);// 2.商户信息不存在直接返回if(StrUtil.isBlank(shopJson)) {return BaseResult.setFail(商户不存在);}// 3.商户信息存在先反序列化为Java对象RedisData redisData JSONUtil.toBean(shopJson, RedisData.class);Date expireTime redisData.getExpireTime();Shop shop JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);// 4.判断是否过期if(expireTime.after(new Date())) {// 没过期直接返回数据return BaseResult.setOkWithData(shop);}// 5.已过期则尝试获取互斥锁String lockKey lock:shop: id;try {boolean isLock tryLock(lockKey);log.info(isLock {}, isLock);if(!isLock) {// 没有获取到互斥锁则直接返回过期数据return BaseResult.setOkWithData(shop);}// 获取到了互斥锁则开启一个新的线程log.info(create a new thread ...);CACHE_REBUILD_EXECUTOR.submit(() - {log.info(a new thread begin...);// 查询数据库Shop newShop getById(id);// 写入RedisRedisData newRedisDate new RedisData();newRedisDate.setData(newShop);long expire System.currentTimeMillis() 30 * 60 * 1000;newRedisDate.setExpireTime(new Date(expire));stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(newRedisDate));log.info(set to Redis: Key {}, Value {}, key, JSONUtil.toJsonStr(newRedisDate));// 释放锁unLock(lockKey);log.info(a new thread end...);});} catch (Exception e) {e.printStackTrace();}// 当前线程直接返回过期数据return BaseResult.setOkWithData(shop); }然后修改ShopController类中的调用方法 // com.star.redis.dzdp.controller.ShopControllerGetMapping(/{id}) public BaseResultShop queryById(PathVariable Long id) {// return shopService.queryShopById(id);// 使用互斥锁解决缓存击穿问题// return shopService.queryShopByIdWithLock(id);// 使用逻辑过期解决缓存击穿问题return shopService.queryShopByIdWithExpire(id); }由于在queryShopByIdWithExpire()方法中只要Redis未命中就直接返回不存在因此这种方式需要进行初始化也就是将数据库的商户信息同步一次到Redis中。 假设现在初始化过了在Redis中有这样一条数据 然后我们在线程内部添加一行代码Thread.sleep(30 * 1000);用于模拟新线程重建缓存数据的时间很久。 接着调用/shop/2接口获取id2的商户信息日志显示线程1从Redis中没有获取到了过期数据并获取互斥锁成功然后创建新线程 [http-nio-8081-exec-1] query Shop by id 2 [http-nio-8081-exec-1] get from Redis: Key cache:shop:2, Value {data:{area:拱宸桥/上塘,openHours:11:30-03:00,...省略...,id:2},expireTime:1640170813000} [http-nio-8081-exec-1] tryLock key lock:shop:2 [http-nio-8081-exec-1] isLock true [http-nio-8081-exec-1] create a new thread ...然后新线程[pool-1-thread-1]进入30s睡眠模拟耗时很长。 此时一个新的请求过来线程2开始执行日志显示线程2从Redis拿到了过期数据但获取互斥锁失败直接返回过期数据 [pool-1-thread-1] a new thread begin... [http-nio-8081-exec-2] query Shop by id 2 get from Redis: Key cache:shop:2, Value {data:{area:拱宸桥/上塘,openHours:11:30-03:00,...省略...,id:2},expireTime:1640170813000} [http-nio-8081-exec-2] tryLock key lock:shop:2 [http-nio-8081-exec-2] isLock false新线程[pool-1-thread-1]休眠结束查询数据库并将查询结果更新到Redis最后释放互斥锁 [pool-1-thread-1] Preparing: SELECT id,name,type_id,images,area,address,x,y,avg_price,sold,comments,score,open_hours,create_time,update_time FROM tb_shop WHERE id? [pool-1-thread-1] Parameters: 2(Long) [pool-1-thread-1] Total: 1 [pool-1-thread-1] set to Redis: Key cache:shop:2, Value {data:{area:拱宸桥/上塘,openHours:11:30-03:00,...省略...,id:2},expireTime:1712234901457} [pool-1-thread-1] unLock key lock:shop:2 [pool-1-thread-1] a new thread end...至此基于逻辑过期的方案也测试完成。 … 本节完更多内容请查阅分类专栏Redis从入门到精通 感兴趣的读者还可以查阅我的另外几个专栏 SpringBoot源码解读与原理分析(已完结)MyBatis3源码深度解析(已完结)再探Java为面试赋能(持续更新中…)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/931067.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自己做网站和外包做pc端网站效果

中医认为,自测疾病有很多方法,如通过身体部位可自测疾病,还可通过疼痛自测疾病等等。在这里,我们主要讲如何通过脚自测疾病。也许,许多人不太在意自己的脚部,是最不受“照顾”的一个身体部位。然而&#xf…

网站创建免费用户网站是做排行榜

《乡土中国》重新审视自己的故乡,再出发 费孝通(1910—2005),中国社会学家、人类学家。被誉为中国社会学和人类学的奠基人之一。江苏吴江人。1980年获国际应用人类学会马林诺斯基荣誉奖。1981年获英国皇家人类学会赫胥黎奖章。代表作有《乡土中国》《江村…

四川住房和城乡建设厅网站首页网站开发与应用论文

【Python】进阶学习:pandas–info()用法详解 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程👈 希望得到您的订…

vm Locky9.6 安装docker

1、设置vi行号vi ~/.vimrcset number 2、启动可远程登录vi /etc/ssh/sshd_configPermitRootLogin yes // 约 40 行PasswordAuthentication yes // 约 65行sudo systemctl restart sshd 3、设置dnssudo nmcli con…

南京网站改版企业展厅设计网

前言 在很多场景下,需要一种通知的交互方式来提醒用户,传统方式下可以在页面实现一个 Dialog,或通过修改网页的 title 来实现消息的通知。然而传统的实现存在着一定的不足,在网页最小化的情况下,无法查看任何通知&…

网站升级建设招标公告电子商务seo是指什么意思

创建业务对象 1、已有的业务对象可以放入向量数据库 2、如果没有新的业务对象需要创建,直接跳过 3、新的业务对象由用户手动创建 提示词 假设你是一名 Java 软件技术专家,根据如下 yaml 格式生成对应的实体对象,其中 1、最顶层为对象名 2、…

男女做暖暖暖网站微博短网址生成

文章目录 一、作用二、区别applycallbind小结 三、实现 一、作用 call、apply、bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向 那么什么情况下需要改变this的指向呢?下面举个例子 var name "lucy"; var obj {n…

腾讯云做的网站会被拦截么seo外包资讯

1.单调栈 单调栈是一种数据结构,其中存放的数据应该是有序的,所以单调栈也有单调递减栈和单调递增栈 单调递增栈:栈顶到栈底的元素大小是从小到大 单调递减栈:栈顶到栈底的元素大小是从大到小 单调栈主要就是用来求一个给定序列中…

设置网站人数wordpress的404页面如何做

随着公众对家庭用水安全意识的提高,如何确保自来水管和楼顶储水罐的安全性和卫生已成为家庭生活中的重要议题。近期,专家针对此问题提出了一系列实用的注意事项和建议。 注意事项: 定期检查:专家强调,家庭应每季度至…

网站开发专业怎么样自助模板网站建设做seo

在上篇文章中,我们完成了应用程序容器化,把webapi项目构建镜像并容器化运行。本文将会演示如何把自己构建的镜像上传到docker官网的仓库和自己私有仓库本地镜像推送到官网的registry1.创建仓库点击Docker Desktop图标->Repositories-》create 跳转到…

seo优化网站模板北京小程序外包

(一)MyBatis快速入门 通过一个案例快速入门Mybatis框架案例:查询user表中所有数据1) 创建user表,添加数据2) 创建模块,导入坐标3) 编写MyBatis核心配置文件-->替换连接信息,解决硬编码问题4) 编写SQL映…

南昌网站建设案例wordpress插件 悬浮

众所周知,计算机考研408计算机学科基础综合难度与一些顶尖985自命题相比也是不落下风的,号称最难工科专业课(请忽略912这种殿堂级别的),难度大、知识点庞杂也是前些年众多高校纷纷脱离408统考的原因。19年的计算机类考研火到爆炸,…

二十条优化长沙哪里优化网站

创建Mnesia数据库 mnesia:create_schema([node()]).在shell里输入该行代码即可创建一个mnesia数据库于当前文件夹下 编译器文件路径下同样也有 数据库表定义创建 之后是数据库表定义,打开数据库创建完成后,启动数据库,添加一些表定义&…

电子商务网站的开发原则包括企业关键词推广

文章目录 WindTerm下载WindTerm远程连接Linux WindTerm下载 官网下载链接 WindTerm网盘下载链接 WindTerm不需要安装,将下载的压缩包解压后直接找到WindTerm.exe执行文件,双击即可运行 WindTerm远程连接Linux 1.先获取你的Linux的IP地址,…

建设手机网站费用dede做导航网站

前文分别介绍了滴滴自研的ES强一致性多活是如何实现的、以及如何提升ES的性能潜力。由于ES具有强大的搜索和分析功能,同时也因其开源和易于使用而成为黑客攻击的目标。近些年,业界ES数据泄露事件频发, 以下是一些比较严重的数据泄露案件: 202…

重装系统后 D 盘软件搜不到?3 步搞定快捷方式与搜索问题

重装系统后 D 盘软件搜不到?3 步搞定快捷方式与搜索问题 很多朋友重装 Windows 系统后,都会遇到一个棘手的问题:非系统盘(比如 D 盘)里的软件文件明明还在,双击.exe 文件也能正常运行,但无论是 Windows 搜索栏还…

做我男朋友好不好网站头像制作器在线制作

Fedora 35 中安装 nginx 的方法非常简单。 运行下面的命令: sudo dnf install nginx 在提示你需要确认的地方,输入 y 后回车即可。 开机自动启动 如果你希望在你的操作系统重启的时候自动启动 nginx,请输入下面的命令: syst…

Codeforces 2155D Batteries 题解 [ 绿 ] [ 图论 ] [ Ad-hoc ]

Batteries:很有趣的一个 Ad-hoc,之前见到过一个类似的构造。如果对上脑电波应该很快能秒掉。 看到这种比较奇怪的交互次数限制,可以想到拆限制的式子,\(\lfloor\dfrac{n^2}{a}\rfloor = \lfloor\dfrac{n}{a}\cdot …

platform驱动注册时不同态

platform驱动注册时不同态非常棒的问题,龙,这说明你已经走到驱动命名机制的深水区了 💡。 你现在发现的 "led_platform" 和 "led_demo" 混用现象——其实非常常见, 根本原因在于:Linux 驱动…

云建站模板公众号内容制作步骤

uni-app 网络API 在 uni-app 开发中,网络请求是获取数据与和服务器交互的重要手段。以下介绍 uni-app 中常见的网络 API,包括发起请求、上传和下载以及 WebSocket、UDP 通信等方面。 发起请求 在 uni-app 里,使用uni.request(OBJECT)来发起…