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

asp网站安装个人网站注册

asp网站安装,个人网站注册,南沙网站开发,大型网站开发教程目录 一、什么是缓存 二、缓存更新策略 2.1. 缓存主动更新策略 2.1.1. Cache Aside模式#xff08;主流#xff09;‌ 2.1.2. Read/Write Through模式‌ 2.1‌.3. Write Behind模式‌ 2.1.4. 总结 三、缓存穿透 四、缓存雪崩 五、缓存击穿 5.1. 互斥锁实现 5.1.1…目录 一、什么是缓存 二、缓存更新策略 2.1. 缓存主动更新策略 2.1.1. Cache Aside模式主流‌ 2.1.2. Read/Write Through模式‌ 2.1‌.3. Write Behind模式‌ 2.1.4. 总结 三、缓存穿透 四、缓存雪崩 五、缓存击穿 5.1. 互斥锁实现 5.1.1. 实现流程图 ​编辑5.1.2. 主要实现代码 5.2. 逻辑过期实现 六、缓存穿透/击穿封装工具类 本文中的图片内容部分来源于黑马程序员教程案例 一、什么是缓存 ‌‌缓存是一种用于临时存储数据的技术或机制旨在提高数据访问速度和系统性能。‌ 缓存通常位于计算机系统内部或附近可以是硬件、软件或两者的结合体。例如Web浏览器可以将最常访问的网页内容缓存在本地以便下次访问时无需从远程服务器重新下载。‌ 缓存的作用和原理在于利用程序局部性原理将频繁访问的数据存储在高速存储器中如‌SRAM或‌DRAM以便快速访问。当硬件需要读取数据时首先在缓存中查找如果找到则直接执行否则从内存中查找。由于缓存的运行速度比内存快得多因此缓存的作用是帮助硬件更快地运行。 缓存可以根据不同的分类标准进行分类。例如根据存储位置和用途的不同可以分为‌CPU缓存、‌硬盘缓存、‌内存缓存等。CPU缓存包括‌L1、L2和L3缓存硬盘缓存用于提高数据传输速度。此外还有‌HTTP缓存、‌浏览器缓存等。 虽然缓存可以提高系统性能但也会引入一定的数据一致性问题。因为缓存中的数据可能会与后端数据源如数据库存在不一致的情况所以在使用缓存时需要考虑数据的更新和缓存的过期策略以保证数据的一致性。 二、缓存更新策略 2.1. 缓存主动更新策略 指在更新数据库中的数据时如何同步更新缓存中的数据以保证数据的一致性。常见的缓存更新策略包括‌Cache Aside模式、‌Read/Write Through模式和‌Write Behind模式。 2.1.1. Cache Aside模式主流‌ Cache Aside模式是最常用的缓存更新策略其中又分为两种 1. 先删除缓存再操作数据库 2. 先操作数据库再删除缓存主流其操作步骤如下 先更新数据库中的数据。然后删除缓存中的数据或者让缓存失效。当下次查询时如果缓存失效则重新从数据库中读取数据并更新缓存。 注由于操作缓存和数据库的话存在线程安全性问题相较于先删除缓存再操作数据库而言先操作数据库再删除缓存对于产生线程安全性的概率较低因为写缓存的速度比更新数据库要快很多线程1在查询缓存未命中后继续查询数据库时线程2进来更新数据库绝大部分情况下线程2更新数据库期间线程1已经完成了缓存的写入。 2.1.2. Read/Write Through模式‌ Read/Write Through模式将缓存和数据库整合为一个服务由服务来维护一致性。操作步骤如下 查询操作直接从缓存中读取数据。如果缓存中不存在数据则直接从数据库中读取并返回给用户同时将数据写入缓存。 2.1‌.3. Write Behind模式‌ Write Behind模式由其他线程异步地将缓存数据持久化到数据库保证最终一致性。操作步骤如下 更新操作只更新缓存。由其他线程异步地将缓存数据写入数据库。 2.1.4. 总结 ‌不同策略的优缺点‌ Cache Aside模式的优点是简单易实现但存在数据不一致的风险。Read/Write Through模式的优点是数据一致性高但性能较低。Write Behind模式的优点是最终一致性但需要额外的线程管理。 ‌不同场景下的适用性‌ Cache Aside模式适用于读多写少的场景简单高效。Read/Write Through模式适用于对数据一致性要求高的场景。Write Behind模式适用于写操作较少可以容忍最终一致性的场景。 缓存更新策略的最佳实践方案 1. 低一致性需求使用Redis自带的内存淘汰机制 2. 高一致性需求主动更新并以超时剔除作为兜底方案 读操作 缓存命中则直接返回缓存未命中则查询数据库并写入缓存设定超时时间 写操作 先写数据库然后再删除缓存要确保数据库与缓存操作的原子性 整个写操作的逻辑我们要确保事务性如在单体的Spring的JavaWeb项目业务代码的Service实现层添加Transactional注解分布式的项目中可以通过TTC模式来控制当比如删除Redis缓存出现异常直接回滚数据库的写操作。 三、缓存穿透 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在这样缓存永远不会生效这些请求都会到数据库。 常见的解决方案有两种 3.1. 缓存空对象项目中主流用法 优点实现简单维护方便缺点额外的内存消耗以及可能造成短期的不一致 2. 布隆过滤器 优点内存占用较少没有多余key缺点实现复杂、存在误判可能 3. 参数校验过滤不合法请求 通过对用户输入的参数进行校验例如检查ID的格式是否合法如果不合法则直接拒绝请求。这种方法可以过滤掉一部分恶意伪造的用户请求减少对数据库的压力。 4. 使用分布式锁 在缓存未命中时通过分布式锁控制只有一个请求去查询数据库其他请求等待锁释放。这样可以防止多个请求同时查询数据库减轻数据库的压力。 5. 服务降级和限流 在面对大量的恶意请求时可以通过服务降级或限流的方式来保护后端服务。限流可以限制到达数据库的请求数量避免数据库压力过大。 四、缓存雪崩 ‌缓存雪崩是指由于缓存中大量数据同时失效或缓存服务器故障导致大量请求直接打到数据库上引发数据库压力激增可能导致整个系统崩溃的现象。‌ 缓存雪崩通常由于缓存的过期策略不当或缓存服务器故障导致。例如如果大量的缓存数据设置为在同一时间点过期或者缓存服务器出现问题无法提供服务所有的请求将直接访问后端存储系统导致后端系统瞬时承受巨大的负载压力‌ 解决方案 给不同的key的TTL失效时间设置随机值利用Redis集群提高服务的可用性给缓存业务添加降级限流策略给业务添加多级缓存 五、缓存击穿 ‌缓存击穿是指当一个缓存中的热点数据过期或被删除后大量并发请求同时到达导致这些请求直接穿透缓存访问数据库从而增加数据库的负载。‌ 这种情况通常发生在热点数据过期或删除的瞬间导致短时间内大量请求无法通过缓存获取数据直接访问数据库造成数据库负载急剧增加‌。 缓存击穿的具体场景包括 ‌热点数据过期‌当缓存中的热点数据过期时大量请求会同时查询后端数据库导致数据库负载增加‌。‌第一次请求‌对于一个之前从未被请求过的数据当它第一次被请求时缓存中没有该数据导致请求穿透到后端存储‌。 解决缓存击穿的方法包括 ‌设置热点数据永不过期‌将热点数据的缓存过期时间设置为较长的时间甚至是永不过期。这种方法可以避免缓存击穿但可能导致数据不够及时和准确‌。‌使用互斥锁‌在数据失效时通过设置互斥锁来保护数据库访问过程。如果某个请求已经获取到了锁其他请求需要等待直到获取到锁为止。这种方法可以避免大量并发请求同时访问数据库但可能导致并发性能下降和请求等待时间增加‌ 注意这两种方案没有孰优孰劣在实际项目中我们要针对业务场景和需求权衡自身是更注重可用性还是一致性来做选择。 5.1. 互斥锁实现 5.1.1. 实现流程图 互斥锁的实现主要基于Redis的命令setnx再附加一个失效时间key不存在时可以往Redis中写入返回值1否则写入失败返回值0同一时期如果有多人写入同一个key只有一人能成功以此达到互斥锁的效果 5.1.2. 主要实现代码 package com.hmdp.service;import com.hmdp.dto.Result; import com.hmdp.entity.Shop; import com.baomidou.mybatisplus.extension.service.IService;public interface IShopService extends IServiceShop {Result queryById(Long id);Result update(Shop shop); }package com.hmdp.service.impl;import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.hmdp.dto.Result; import com.hmdp.entity.Shop; import com.hmdp.mapper.ShopMapper; import com.hmdp.service.IShopService; import com.hmdp.utils.CacheClient; import com.hmdp.utils.SystemConstants; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResult; import org.springframework.data.geo.GeoResults; import org.springframework.data.redis.connection.RedisGeoCommands; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.domain.geo.GeoReference; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource; import java.util.*; import java.util.concurrent.TimeUnit;import static com.hmdp.utils.RedisConstants.*;Service public class ShopServiceImpl extends ServiceImplShopMapper, Shop implements IShopService {Resourceprivate StringRedisTemplate stringRedisTemplate;Overridepublic Result queryById(Long id) {// 互斥锁解决缓存击穿Shop shop queryWithMutex(id);if (shop null) {return Result.fail(店铺不存在);}return Result.ok(shop);}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 id);return Result.ok();}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. 存在直接返回return JSONUtil.toBean(shopJson, Shop.class);}// 判断命中的是否为null空值如果是除了null以外的和 \t\n这类空串则直接返回错误信息if (shopJson ! null) {// 返回一个错误信息return null;}// 4. 实现缓存重建// 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);// 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();} finally {// 释放互斥锁unlock(lockKey);}// 7. 返回return shop;}private boolean trylock(String key) {// 不要直接返回flag因这个值可能会为空直接拆箱会可能报空指针Boolean flag stringRedisTemplate.opsForValue().setIfAbsent(key, 1, 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unlock(String key) {stringRedisTemplate.delete(key);} }5.2. 逻辑过期实现 缓存击穿往往发生在高并发访问的热点数据上因此实际项目中在我们的预期里缓存击穿的热点数据是能够命中即存在的。对于未命中的key我们认定为非热点数据因此直接返回空不做逻辑过期处理。 package com.hmdp.utils;import lombok.Data; import java.time.LocalDateTime;/*** 作为封装的Redis缓存对象*/ Data public class RedisData {/*** 逻辑过期时间*/private LocalDateTime expireTime;/*** 缓存的业务实体Bean如用户、商家等*/private Object data; }package com.hmdp.service;import com.hmdp.dto.Result; import com.hmdp.entity.Shop; import com.baomidou.mybatisplus.extension.service.IService;public interface IShopService extends IServiceShop {Result queryById(Long id);Result update(Shop shop); }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.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.hmdp.dto.Result; import com.hmdp.entity.Shop; import com.hmdp.mapper.ShopMapper; import com.hmdp.service.IShopService; 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.CACHE_SHOP_KEY; import static com.hmdp.utils.RedisConstants.LOCK_SHOP_KEY;Service public class ShopServiceImpl extends ServiceImplShopMapper, Shop implements IShopService {private static final ExecutorService CACHE_REBUILD_EXECUTOR Executors.newFixedThreadPool(10);Resourceprivate StringRedisTemplate stringRedisTemplate;Overridepublic Result queryById(Long id) {// 逻辑过期Shop shop queryWithLogicalExpire(id);if (shop null) {return Result.fail(店铺不存在);}// 7.返回return Result.ok(shop);}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 id);return Result.ok();}/*** 逻辑过期代码实现* param id* return*/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);Shop shop JSONUtil.toBean((JSONObject) redisData.getData(), 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;}private void saveShop2Redis(Long id, Long expireSeconds) {// 1. 查询店铺数据Shop shop getById(id);// 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));}private boolean trylock(String key) {// 不要直接返回flag因这个值可能会为空直接拆箱会可能报空指针Boolean flag stringRedisTemplate.opsForValue().setIfAbsent(key, 1, 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unlock(String key) {stringRedisTemplate.delete(key);} }六、缓存穿透/击穿封装工具类 package com.hmdp.utils;import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component;import java.time.LocalDateTime; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Function;import static com.hmdp.utils.RedisConstants.CACHE_NULL_TTL; import static com.hmdp.utils.RedisConstants.LOCK_SHOP_KEY;Slf4j Component public class CacheClient {private final StringRedisTemplate stringRedisTemplate;private static final ExecutorService CACHE_REBUILD_EXECUTOR Executors.newFixedThreadPool(10);public CacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}public void set(String key, Object value, Long time, TimeUnit unit) {stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);}public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {// 设置逻辑过期RedisData redisData new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));// 写入RedisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));}/*** 缓存穿透* param keyPrefix* param id* param type* param dbFallback* param time* param unit* param R* param ID* return*/public R,ID R queryWithPassThrough(String keyPrefix, ID id, ClassR type, FunctionID, R dbFallback, Long time, TimeUnit unit){String key keyPrefix id;// 1.从redis查询商铺缓存String json stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isNotBlank(json)) {// 3.存在直接返回return JSONUtil.toBean(json, type);}// 判断命中的是否是空值if (json ! null) {// 返回一个错误信息return null;}// 4.不存在根据id查询数据库R r dbFallback.apply(id);// 5.不存在返回错误if (r null) {// 将空值写入redisstringRedisTemplate.opsForValue().set(key, , CACHE_NULL_TTL, TimeUnit.MINUTES);// 返回错误信息return null;}// 6.存在写入redisthis.set(key, r, time, unit);return r;}/*** 逻辑过期解决缓存击穿* param keyPrefix* param id* param type* param dbFallback* param time* param unit* param R* param ID* return*/public R, ID R queryWithLogicalExpire(String keyPrefix, ID id, ClassR type, FunctionID, R dbFallback, Long time, TimeUnit unit) {String key keyPrefix id;// 1.从redis查询商铺缓存String json stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isBlank(json)) {// 3.存在直接返回return null;}// 4.命中需要先把json反序列化为对象RedisData redisData JSONUtil.toBean(json, RedisData.class);R r JSONUtil.toBean((JSONObject) redisData.getData(), type);LocalDateTime expireTime redisData.getExpireTime();// 5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())) {// 5.1.未过期直接返回店铺信息return r;}// 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 {// 查询数据库R newR dbFallback.apply(id);// 重建缓存this.setWithLogicalExpire(key, newR, time, unit);} catch (Exception e) {throw new RuntimeException(e);}finally {// 释放锁unlock(lockKey);}});}// 6.4.返回过期的商铺信息return r;}/*** 互斥锁解决缓存击穿* param keyPrefix* param id* param type* param dbFallback* param time* param unit* param R* param ID* return*/public R, ID R queryWithMutex(String keyPrefix, ID id, ClassR type, FunctionID, R dbFallback, Long time, TimeUnit unit) {String key keyPrefix id;// 1.从redis查询商铺缓存String shopJson stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {// 3.存在直接返回return JSONUtil.toBean(shopJson, type);}// 判断命中的是否是空值if (shopJson ! null) {// 返回一个错误信息return null;}// 4.实现缓存重建// 4.1.获取互斥锁String lockKey LOCK_SHOP_KEY id;R r null;try {boolean isLock tryLock(lockKey);// 4.2.判断是否获取成功if (!isLock) {// 4.3.获取锁失败休眠并重试Thread.sleep(50);return queryWithMutex(keyPrefix, id, type, dbFallback, time, unit);}// 4.4.获取锁成功根据id查询数据库r dbFallback.apply(id);// 5.不存在返回错误if (r null) {// 将空值写入redisstringRedisTemplate.opsForValue().set(key, , CACHE_NULL_TTL, TimeUnit.MINUTES);// 返回错误信息return null;}// 6.存在写入redisthis.set(key, r, time, unit);} catch (InterruptedException e) {throw new RuntimeException(e);}finally {// 7.释放锁unlock(lockKey);}// 8.返回return r;}/*** 加锁* param key* return*/private boolean tryLock(String key) {Boolean flag stringRedisTemplate.opsForValue().setIfAbsent(key, 1, 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}/*** 解锁* param key*/private void unlock(String key) {stringRedisTemplate.delete(key);} }
http://www.hkea.cn/news/14424863/

相关文章:

  • 学编程的好处重庆网站页面优化
  • 企业网站cms宣传海报怎么制作
  • 网站做cdn需要多少钱常州哪家公司做网站
  • 网站怎么做架构南京网站制作有限公司
  • 网站托管套餐深圳网站建设简介
  • 泰州手机网站制作asp网站模板免费下载
  • 建设网站技术标准nas可以做网站
  • 四川手机网站设计方案网站怎么做才能被百度收录
  • 婚纱摄影网站设计案例公司网站开发费用兴田德润官方网站
  • 网站登记表o2o商城
  • 网站开发基础课程论坛网站模板
  • 网站登录界面模板html域名代理商网站
  • 台州网站公司建站小程序制作难吗
  • 本溪做网站的公司好的网站布局
  • 网站怎么做直通车推客易可以做自己的网站吗
  • 站长之家素材做app推广上哪些网站吗
  • 用asp做的网站如何发布住房及城乡建设部网站
  • 荥阳网站制作如果做vr参观网站
  • 牛二网站建设wordpress吃内存
  • 网站内容计划wordpress 调用相册
  • 厦门市建设局网站做图片详情网站
  • 网站建设app手机下载织梦怎么做门户网站
  • html网站登录界面模板wordpress arduino
  • 广东网站建设公司网络服务海口seo关键词优化
  • 重庆品牌网站建设公司排名北京房产
  • 网站快速盈利做网站绑定 对应的域名
  • 有哪些网站制作公司网络安全设计方案
  • 大气的企业网站源码新手机发布
  • 网站建设标书微网站的搭建
  • 网站开发vsc网站开发公司公司做网站好吗