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

向google提交网站微信抽奖小程序怎么做

向google提交网站,微信抽奖小程序怎么做,wordpress修改登录图标,wordpress怎么改缓存是Java应用性能优化的"利器",能将数据库访问压力降低80%以上。但不合理的缓存策略会导致缓存雪崩、数据不一致等问题,反而影响系统稳定性。本文将聚焦缓存策略优化的三个核心方向,通过电商、秒杀等真实场景案例,详解…

缓存是Java应用性能优化的"利器",能将数据库访问压力降低80%以上。但不合理的缓存策略会导致缓存雪崩、数据不一致等问题,反而影响系统稳定性。本文将聚焦缓存策略优化的三个核心方向,通过电商、秒杀等真实场景案例,详解如何设计高效、可靠的缓存架构。

一、多级缓存设计:本地缓存 + 分布式缓存的"黄金组合"

单一缓存方案难以兼顾性能与一致性,多级缓存通过"本地缓存抗高频访问,分布式缓存保一致性"的组合,实现性能与可靠性的平衡。

为什么需要多级缓存?

不同缓存方案的特性对比:

缓存类型访问速度内存成本分布式一致性适用场景
本地缓存(Caffeine)微秒级(最快)较高(每个实例单独占用)差(实例间不共享)高频访问、变化少的热点数据
分布式缓存(Redis)毫秒级较低(集中存储)好(集群共享)跨实例共享数据、一致性要求高的场景

结论:本地缓存适合抗住高频访问的"流量尖峰",分布式缓存适合保证跨服务的数据一致性。

实战:Caffeine + Redis 多级缓存实现

以电商商品详情查询为例,实现"本地缓存→Redis→数据库"的三级查询链路。

1. 依赖引入
<!-- pom.xml -->
<!-- 本地缓存 Caffeine -->
<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.1.8</version>
</dependency><!-- 分布式缓存 Redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 缓存配置
@Configuration
public class CacheConfig {// 1. 本地缓存 Caffeine 配置@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(10_000) // 最大缓存条目(根据内存调整).expireAfterWrite(5, TimeUnit.MINUTES) // 写入后5分钟过期.recordStats(); // 开启统计(用于监控命中率)}@Beanpublic Cache<String, Object> localCache(Caffeine<Object, Object> caffeine) {return caffeine.build();}// 2. Redis 缓存配置@Beanpublic RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {// 序列化配置(解决对象存储问题)RedisSerializer<String> keySerializer = new StringRedisSerializer();GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)) // 默认30分钟过期.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer)).disableCachingNullValues(); // 不缓存null值(避免缓存穿透)// 针对不同业务设置不同过期时间Map<String, RedisCacheConfiguration> cacheConfigs = new HashMap<>();cacheConfigs.put("product", config.entryTtl(Duration.ofHours(1))); // 商品缓存1小时cacheConfigs.put("user", config.entryTtl(Duration.ofMinutes(10))); // 用户缓存10分钟return RedisCacheManager.builder(factory).cacheDefaults(config).withInitialCacheConfigurations(cacheConfigs).build();}
}
3. 多级缓存查询实现
@Service
public class ProductService {@Autowiredprivate ProductMapper productMapper;@Autowiredprivate Cache<String, Object> localCache; // Caffeine本地缓存@Autowiredprivate RedisCacheManager redisCacheManager;/*** 商品详情查询:先查本地缓存,再查Redis,最后查数据库*/public ProductDTO getProductDetail(Long productId) {String cacheKey = "product:" + productId;ProductDTO result;// 1. 查本地缓存(Caffeine)result = (ProductDTO) localCache.getIfPresent(cacheKey);if (result != null) {log.info("从本地缓存获取商品:{}", productId);return result;}// 2. 查分布式缓存(Redis)RedisCache productCache = (RedisCache) redisCacheManager.getCache("product");result = productCache.get(cacheKey, ProductDTO.class);if (result != null) {log.info("从Redis获取商品:{}", productId);// 同步到本地缓存,减少下次查询开销localCache.put(cacheKey, result);return result;}// 3. 查数据库log.info("从数据库查询商品:{}", productId);Product product = productMapper.selectById(productId);if (product == null) {return null;}result = convertToDTO(product);// 4. 写入缓存(先写Redis,再写本地)productCache.put(cacheKey, result);localCache.put(cacheKey, result);return result;}// 数据库更新时,同步删除缓存(避免数据不一致)@Transactionalpublic void updateProduct(ProductDTO dto) {String cacheKey = "product:" + dto.getId();// 1. 更新数据库Product product = convertToEntity(dto);productMapper.updateById(product);// 2. 删除缓存(避免脏数据)localCache.invalidate(cacheKey); // 清除本地缓存RedisCache productCache = (RedisCache) redisCacheManager.getCache("product");productCache.evict(cacheKey); // 清除Redis缓存}
}

多级缓存的性能提升

某电商商品详情接口优化前后对比(日均1000万访问):

指标仅用Redis多级缓存(Caffeine+Redis)提升幅度
平均响应时间35ms8ms77%
Redis访问量1000万次/天300万次/天70%
数据库访问量100万次/天30万次/天70%
缓存命中率90%97%7%

二、缓存失效策略:避免雪崩、击穿、穿透的"三板斧"

缓存失效是导致系统故障的常见原因,三种典型问题需针对性解决:

1. 缓存雪崩:大量缓存同时失效导致数据库压垮

问题场景:若所有商品缓存都设置在凌晨2点过期,凌晨2点后大量请求会直接冲击数据库,导致数据库宕机。

解决方案:过期时间加随机偏移量,避免"集体失效"。

// 错误:所有缓存使用固定过期时间
redisTemplate.opsForValue().set(cacheKey, value, 30, TimeUnit.MINUTES);// 正确:添加随机偏移量(±5分钟)
int baseExpire = 30; // 基础过期时间30分钟
int random = new Random().nextInt(10) - 5; // -5到5的随机数
redisTemplate.opsForValue().set(cacheKey, value, baseExpire + random, TimeUnit.MINUTES);

进阶方案:使用Redis集群+持久化,避免缓存服务整体宕机;数据库添加限流保护(如Sentinel)。

2. 缓存击穿:热点数据过期瞬间被高并发穿透

问题场景:某秒杀商品缓存过期瞬间,10万并发请求直接查询数据库,导致数据库过载。

解决方案:互斥锁+双重检查,只让一个请求去数据库加载数据。

/*** 带互斥锁的缓存查询(解决缓存击穿)*/
public ProductDTO getSeckillProduct(Long productId) {String cacheKey = "seckill:product:" + productId;ProductDTO result;// 1. 先查缓存result = (ProductDTO) redisTemplate.opsForValue().get(cacheKey);if (result != null) {return result;}// 2. 缓存不存在,获取互斥锁String lockKey = "lock:" + cacheKey;boolean locked = false;try {// 尝试获取锁(3秒过期,避免死锁)locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 3, TimeUnit.SECONDS);if (locked) {// 3. 双重检查:再次查缓存(防止其他线程已加载)result = (ProductDTO) redisTemplate.opsForValue().get(cacheKey);if (result != null) {return result;}// 4. 只有一个线程能走到这里,查数据库result = loadFromDb(productId);if (result != null) {// 热点数据设置较长过期时间(如1小时)redisTemplate.opsForValue().set(cacheKey, result, 60, TimeUnit.MINUTES);}return result;} else {// 5. 未获取到锁,等待100ms后重试Thread.sleep(100);return getSeckillProduct(productId); // 递归重试}} finally {// 6. 释放锁if (locked) {redisTemplate.delete(lockKey);}}
}

更优方案:使用Redisson的分布式锁(自带自动续期,避免锁提前释放):

@Autowired
private RedissonClient redissonClient;public ProductDTO getSeckillProduct(Long productId) {String cacheKey = "seckill:product:" + productId;ProductDTO result = (ProductDTO) redisTemplate.opsForValue().get(cacheKey);if (result != null) return result;RLock lock = redissonClient.getLock("lock:" + cacheKey);try {// 尝试获取锁(等待1秒,10秒自动释放)if (lock.tryLock(1, 10, TimeUnit.SECONDS)) {// 双重检查+加载数据(同上)// ...} else {// 重试或返回默认值Thread.sleep(100);return getSeckillProduct(productId);}} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}
}

3. 缓存穿透:查询不存在的数据导致缓存失效

问题场景:黑客恶意查询不存在的商品ID(如-1、999999),由于缓存不存这些数据,所有请求都会穿透到数据库,导致数据库压力过大。

解决方案:布隆过滤器拦截无效请求 + 空值缓存。

步骤1:初始化布隆过滤器(启动时加载所有有效ID)
@Component
public class ProductBloomFilterInitializer implements CommandLineRunner {@Autowiredprivate ProductMapper productMapper;@Autowiredprivate RedissonClient redissonClient;// 布隆过滤器预期插入量(根据实际商品数调整)private static final long EXPECTED_INSERTIONS = 10_000_000;// 误判率(0.01 = 1%)private static final double FALSE_POSITIVE_RATE = 0.01;@Overridepublic void run(String... args) throws Exception {// 获取Redis布隆过滤器RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter("product:id:bloom");// 初始化布隆过滤器bloomFilter.tryInit(EXPECTED_INSERTIONS, FALSE_POSITIVE_RATE);// 批量加载所有商品ID(分批查询,避免OOM)int batchSize = 1000;long total = productMapper.count();for (long i = 0; i < total; i += batchSize) {List<Long> ids = productMapper.selectIds(i, i + batchSize);ids.forEach(bloomFilter::add);}log.info("布隆过滤器初始化完成,加载商品ID总数:{}", total);}
}
步骤2:查询时先通过布隆过滤器拦截
public ProductDTO getProductDetail(Long productId) {// 1. 布隆过滤器快速判断:不存在的ID直接返回RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter("product:id:bloom");if (!bloomFilter.contains(productId)) {log.warn("无效商品ID:{},布隆过滤器拦截", productId);return null;}String cacheKey = "product:" + productId;ProductDTO result;// 2. 查缓存(本地+Redis)// ...(同上)// 3. 数据库查询结果为空时,缓存空值(设置短期过期)if (result == null) {log.info("商品{}不存在,缓存空值", productId);redisTemplate.opsForValue().set(cacheKey, null, 5, TimeUnit.MINUTES); // 空值缓存5分钟localCache.put(cacheKey, null);return null;}// 4. 写入缓存(正常流程)// ...
}

效果:无效请求被布隆过滤器拦截,数据库访问量减少99%。

三、热点数据预热:把"流量尖峰"扼杀在缓存里

缓存预热是指在流量到来前,主动将热点数据加载到缓存中,避免缓存未命中时的数据库压力。

预热时机与场景

预热时机适用场景实现方式
系统启动时基础数据(如商品分类、地区信息)CommandLineRunner/ApplicationRunner
定时任务周期性热点数据(如每日热销商品)@Scheduled
流量增长前促销活动、秒杀商品接口触发/消息通知

实战1:系统启动预热基础数据

@Component
public class BasicDataPreloader implements CommandLineRunner {@Autowiredprivate CategoryService categoryService;@Autowiredprivate Cache<String, Object> localCache;@Autowiredprivate RedisCacheManager redisCacheManager;@Overridepublic void run(String... args) throws Exception {log.info("开始预热基础数据...");long startTime = System.currentTimeMillis();// 1. 加载所有商品分类List<CategoryDTO> categories = categoryService.getAllCategories();RedisCache categoryCache = (RedisCache) redisCacheManager.getCache("category");// 2. 写入缓存for (CategoryDTO category : categories) {String cacheKey = "category:" + category.getId();categoryCache.put(cacheKey, category);localCache.put(cacheKey, category);}long cost = System.currentTimeMillis() - startTime;log.info("基础数据预热完成,加载分类{}个,耗时{}ms", categories.size(), cost);}
}

实战2:秒杀活动前预热商品数据

@Service
public class SeckillPreheatService {@Autowiredprivate ProductMapper productMapper;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 预热秒杀商品数据(活动开始前10分钟调用)*/public void preheatSeckillProducts(List<Long> productIds) {log.info("开始预热秒杀商品:{}", productIds);// 1. 批量查询商品数据List<Product> products = productMapper.selectBatchIds(productIds);if (CollectionUtils.isEmpty(products)) {log.warn("无秒杀商品需要预热");return;}// 2. 批量写入Redis(使用pipeline减少网络开销)redisTemplate.executePipelined((RedisCallback<Object>) connection -> {for (Product product : products) {String cacheKey = "seckill:product:" + product.getId();ProductDTO dto = convertToDTO(product);// 序列化对象byte[] key = redisTemplate.getStringSerializer().serialize(cacheKey);byte[] value = redisTemplate.getValueSerializer().serialize(dto);// 设置过期时间为2小时(覆盖活动时间)connection.setEx(key, 7200, value);}return null;});log.info("秒杀商品预热完成,共{}个", products.size());}
}// 定时任务:活动前10分钟触发预热
@Scheduled(cron = "0 50 23 * * ?") // 每天23:50执行(假设活动0点开始)
public void schedulePreheat() {List<Long> tomorrowSeckillProductIds = seckillService.getTomorrowProductIds();seckillPreheatService.preheatSeckillProducts(tomorrowSeckillProductIds);
}

预热效果对比(秒杀场景)

某秒杀活动(10万并发)优化前后对比:

指标未预热预热后提升幅度
首屏响应时间800ms50ms94%
数据库峰值QPS5万50099%
缓存命中率60%99.5%39.5%
系统错误率15%0.1%99.3%

缓存策略优化的核心原则

缓存优化的本质是"用空间换时间",但需平衡一致性、可用性和性能,核心原则包括:

  1. 缓存更新策略

    • 读多写少:更新数据库后删除缓存(Cache Aside)
    • 写多读少:更新数据库后更新缓存(Write Through)
  2. 缓存粒度控制

    • 避免缓存过大对象(如整个表),按业务需求拆分粒度
    • 示例:缓存商品基本信息和库存分开,库存更新不影响基本信息
  3. 监控与调优

    • 监控缓存命中率(目标≥90%)、平均响应时间、内存占用
    • 根据监控调整缓存大小、过期时间、预热策略
  4. 降级与容错

    • 缓存服务故障时,降级为直接查数据库(配合限流)
    • 使用Sentinel等工具实现缓存熔断

记住:没有万能的缓存策略,需结合业务场景(如电商、金融、社交)选择合适的方案。通过多级缓存抗流量、失效策略保稳定、预热机制提效率,才能构建高性能、高可用的缓存架构,让Java应用在高并发场景下从容应对。

http://www.dtcms.com/a/529809.html

相关文章:

  • 西安网站挂标广西金利建设有限公司网站
  • windows10安装WSL2 ubuntu24.04中安装vLLM vLLM中部署Qwen2.5-VL
  • 如何在Linux(Ubuntu)操作系统上查看文件的MD5,SHA256等校验码
  • 网站付的保证金怎么做会计凭证山东公司网站建设
  • 商城网站建设企业lamp 搭建wordpress
  • 做网站对企业有什么好处2022年网站能用的
  • 邯郸推广网站建设哪个好桐乡市城乡规划建设局网站
  • 布吉网站的建设中国菲律宾最新局势
  • 青海电商网站建设公司中国十大最著名品牌
  • ML 与 DL 常用数据集介绍
  • 佛山网站建设公司怎么样石家庄网站建设远策科技
  • 厦门u 网站建设如何选择昆明网站建设
  • 做网站生意多吗a5站长平台
  • 网站开发企业一级a做爰片免费网站孕交视频
  • 旅游网站设计规划书做视频网站视频用什么插件吗
  • 继承Thread类
  • 福建城建设厅官方网站昆山高端网站设计公司
  • 呼市网站seo优化工资提成怎么算邙山郑州网站建设
  • 【C++】开发环境配置
  • 百度下载软件太慢了怎么办
  • 【Windows】关于搜狗浏览器安装11、12版本-区别
  • 深圳响应式设计企业网站网站建设 部署与发布视频教程
  • pyttsx3使用Windows 10的Kangkang语音
  • 3.权限(一)
  • Gurobi vs CPLEX:大规模优化求解器的选择逻辑与国产替代新选项
  • 优秀的手机网站案例走着瞧网站 设计
  • 济南网站建设外包公司福田欧曼行星
  • 建设网站的基础知识企业宣传片制作软件app
  • 【07】C语言中的浮点类型详解
  • 营销型网站代理网站上面的水印怎么做