高并发场景从来都是绕不开的 “硬骨头”,而缓存作为提升系统吞吐量、降低数据库压力的核心手段,早已成为每个开发者的必备技能。但很多时候,我们随手写的 “基础缓存逻辑”—— 比如简单的 “查缓存 - 无则查库 - 回写缓存”—— 在高并发流量冲击下,往往会暴露出缓存穿透、缓存击穿、缓存雪崩等一系列问题:可能是大量请求直击数据库导致服务雪崩,可能是缓存过期瞬间的并发击穿,也可能是分布式环境下的缓存一致性难题。
缓存穿透是指用户请求查询一个根本不存在的数据,导致请求绕过缓存直接打到数据库,当这类请求量巨大时,会给数据库带来极大压力甚至使其宕机。
高并发场景下的解决方案及代码实现
针对缓存穿透,主流且高效的解决方案有两种:空值缓存 + 布隆过滤器(空值缓存解决少量无效请求,布隆过滤器拦截大量无效请求,二者结合效果最佳)。
以下以Java + Spring Boot + Redis为例,模拟「电商商品查询」的高并发场景(比如秒杀活动中,大量用户查询不存在的商品 ID)
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
@Configuration
public class RedisConfig {
@Resource
private RedisTemplate<String, Object> redisTemplate;
// 布隆过滤器:假设系统中有100万件商品,误判率0.01
private BloomFilter<Long> productBloomFilter;
/**
* 初始化RedisTemplate序列化方式
*/
@PostConstruct
public void initRedisTemplate() {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
}
/**
* 初始化布隆过滤器(实际项目中应从数据库加载所有商品ID初始化)
*/
@Bean
public BloomFilter<Long> productBloomFilter() {
// 预期数据量100万,误判率0.01
productBloomFilter = BloomFilter.create(
Funnels.longFunnel(),
1000000,
0.01
);
// 模拟初始化:将系统中存在的商品ID加入布隆过滤器
for (long i = 1; i <= 1000000; i++) {
productBloomFilter.put(i);
}
return productBloomFilter;
}
}
import com.google.common.hash.BloomFilter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class ProductService {
@Resource
private RedisTemplate redisTemplate;
@Resource
private BloomFilter<Long> productBloomFilter;
// 缓存key前缀
private static final String PRODUCT_KEY_PREFIX = "product:info:";
// 空值缓存过期时间(5分钟,避免缓存过多无效数据)
private static final long NULL_CACHE_EXPIRE = 5;
// 正常商品缓存过期时间(1小时)
private static final long NORMAL_CACHE_EXPIRE = 60;
/**
* 查询商品信息(高并发场景防缓存穿透)
* @param productId 商品ID
* @return 商品信息(null表示不存在)
*/
public String getProductInfo(Long productId) {
// 步骤1:布隆过滤器快速拦截无效请求(核心:不存在的ID直接返回)
if (!productBloomFilter.mightContain(productId)) {
System.out.println("布隆过滤器拦截:商品ID " + productId + " 不存在,直接返回");
return null;
}
// 步骤2:查询Redis缓存
String cacheKey = PRODUCT_KEY_PREFIX + productId;
String productInfo = (String) redisTemplate.opsForValue().get(cacheKey);
// 缓存命中:直接返回
if (productInfo != null) {
// 空值缓存(""):表示数据库中确实不存在该商品,返回null
if ("".equals(productInfo)) {
System.out.println("缓存命中空值:商品ID " + productId + " 不存在");
return null;
}
System.out.println("缓存命中:返回商品ID " + productId + " 信息");
return productInfo;
}
// 步骤3:缓存未命中,查询数据库(加锁避免缓存击穿,此处简化,仅防穿透)
String dbProductInfo = queryProductFromDb(productId);
// 步骤4:处理数据库查询结果,写入缓存(核心:空值也缓存)
if (dbProductInfo == null) {
// 数据库中不存在:缓存空值(用""代替null,避免Redis缓存null值)
redisTemplate.opsForValue().set(
cacheKey,
"",
NULL_CACHE_EXPIRE,
TimeUnit.MINUTES
);
System.out.println("数据库查询为空:商品ID " + productId + ",写入空值缓存");
return null;
} else {
// 数据库中存在:缓存正常数据
redisTemplate.opsForValue().set(
cacheKey,
dbProductInfo,
NORMAL_CACHE_EXPIRE,
TimeUnit.MINUTES
);
System.out.println("数据库查询命中:商品ID " + productId + ",写入正常缓存");
return dbProductInfo;
}
}
/**
* 模拟数据库查询商品信息
*/
private String queryProductFromDb(Long productId) {
// 模拟逻辑:仅ID 1-1000000存在,其他不存在
if (productId >= 1 && productId <= 1000000) {
return "商品ID:" + productId + ",名称:小米手机,价格:1999";
} else {
// 模拟数据库查询耗时(高并发下无防护会导致数据库压力剧增)
try {
Thread.sleep(10); // 模拟数据库IO耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return null;
}
}
}
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Controller
public class ProductController {
@Resource
private ProductService productService;
// 固定线程池:模拟100个并发线程
private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(100);
/**
* 模拟高并发查询不存在的商品(缓存穿透测试)
*/
@GetMapping("/test/cache/penetration")
@ResponseBody
public String testCachePenetration() {
long startTime = System.currentTimeMillis();
// 模拟10万次请求,查询不存在的商品ID(1000001)
int requestCount = 100000;
for (int i = 0; i < requestCount; i++) {
EXECUTOR.submit(() -> productService.getProductInfo(1000001L));
}
// 等待所有请求完成
EXECUTOR.shutdown();
try {
EXECUTOR.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long endTime = System.currentTimeMillis();
return "10万次无效请求处理完成,耗时:" + (endTime - startTime) + "ms";
}
}
无防护时:10 万次请求会全部打到数据库,每次数据库查询耗时 10ms,总耗时约 1000 秒(16 分钟),且数据库会被高并发压垮。
有防护时:
Redis和数据库;Redis空值,不会再打数据库。缓存雪崩是指两种核心场景导致的灾难性后果:
针对这两种场景,核心解决方案是:打散缓存过期时间 + 缓存预热 + 分布式锁防缓存重建风暴 + Redis 高可用集群 + 服务降级 / 限流(多策略组合才能应对高并发下的雪崩风险)。
以下以Java + Spring Boot + Redis + Redisson(分布式锁)为例,模拟「电商首页商品分类缓存」的高并发场景(比如 100 个商品分类缓存原本同时过期,引发缓存雪崩)
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Service
public class CategoryService {
@Resource
private RedisTemplate redisTemplate;
@Resource
private RedissonClient redissonClient;
// 缓存key前缀
private static final String CATEGORY_KEY_PREFIX = "category:info:";
// 基础过期时间(2小时)
private static final long BASE_EXPIRE_MINUTES = 2 * 60;
// 过期时间随机范围(0-30分钟)
private static final long RANDOM_EXPIRE_RANGE = 30;
// 分布式锁前缀
private static final String LOCK_KEY_PREFIX = "lock:category:";
// 锁超时时间(避免死锁)
private static final long LOCK_EXPIRE_SECONDS = 30;
// 降级开关(雪崩时触发,返回兜底数据)
private volatile boolean isDegrade = false;
/**
* 查询商品分类(高并发防缓存雪崩)
* @param categoryId 分类ID
* @return 分类信息
*/
public String getCategoryInfo(Integer categoryId) {
// 降级策略:Redis宕机/压力过大时,直接返回兜底数据
if (isDegrade) {
return "【降级兜底】分类ID:" + categoryId + ",基础数码分类(缓存服务暂不可用)";
}
String cacheKey = CATEGORY_KEY_PREFIX + categoryId;
// 步骤1:查询Redis缓存
String categoryInfo = (String) redisTemplate.opsForValue().get(cacheKey);
if (categoryInfo != null) {
System.out.println("缓存命中:分类ID " + categoryId);
return categoryInfo;
}
// 步骤2:缓存未命中,加分布式锁(避免高并发下大量线程同时查数据库,引发缓存重建风暴)
String lockKey = LOCK_KEY_PREFIX + categoryId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试获取锁(最多等5秒,锁自动过期30秒)
boolean lockAcquired = lock.tryLock(5, LOCK_EXPIRE_SECONDS, TimeUnit.SECONDS);
if (!lockAcquired) {
// 获取锁失败:返回兜底数据(避免线程阻塞)
return "【临时兜底】分类ID:" + categoryId + ",数据加载中...";
}
// 步骤3:双重检查缓存(防止其他线程已重建缓存)
categoryInfo = (String) redisTemplate.opsForValue().get(cacheKey);
if (categoryInfo != null) {
return categoryInfo;
}
// 步骤4:查询数据库(模拟高耗时操作)
categoryInfo = queryCategoryFromDb(categoryId);
// 步骤5:写入缓存(核心:过期时间随机化,打散过期点)
long randomExpire = (long) (Math.random() * RANDOM_EXPIRE_RANGE);
redisTemplate.opsForValue().set(
cacheKey,
categoryInfo,
BASE_EXPIRE_MINUTES + randomExpire,
TimeUnit.MINUTES
);
System.out.println("缓存重建完成:分类ID " + categoryId + ",过期时间:" + (BASE_EXPIRE_MINUTES + randomExpire) + "分钟");
return categoryInfo;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return "【异常兜底】分类ID:" + categoryId + ",数据查询失败";
} finally {
// 释放锁(仅持有锁的线程释放)
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 模拟数据库查询分类信息(高耗时操作,模拟高并发下数据库压力)
*/
private String queryCategoryFromDb(Integer categoryId) {
// 模拟数据库IO耗时(10ms)
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "分类ID:" + categoryId + ",名称:数码分类-" + categoryId + "(数据库查询)";
}
/**
* 手动触发降级(模拟Redis宕机场景)
*/
public void triggerDegrade(boolean degrade) {
this.isDegrade = degrade;
}
}
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@RestController
public class CategoryController {
@Resource
private CategoryService categoryService;
// 固定线程池:模拟200个并发线程(高并发场景)
private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(200);
/**
* 模拟高并发查询分类(缓存雪崩测试)
* @param batchCount 总请求数
* @return 处理结果
*/
@GetMapping("/test/cache/avalanche")
@ResponseBody
public String testCacheAvalanche(@RequestParam(defaultValue = "200000") int batchCount) {
long startTime = System.currentTimeMillis();
// 模拟场景:100个分类缓存同时过期,20万次请求并发查询
for (int i = 0; i < batchCount; i++) {
int finalI = i;
EXECUTOR.submit(() -> {
// 循环查询1-100个分类(模拟全量分类请求)
int categoryId = (finalI % 100) + 1;
categoryService.getCategoryInfo(categoryId);
});
}
// 等待所有请求完成
EXECUTOR.shutdown();
try {
EXECUTOR.awaitTermination(2, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long endTime = System.currentTimeMillis();
return "20万次分类查询请求处理完成,耗时:" + (endTime - startTime) + "ms";
}
/**
* 触发/关闭降级策略
*/
@GetMapping("/test/degrade")
@ResponseBody
public String triggerDegrade(@RequestParam boolean degrade) {
categoryService.triggerDegrade(degrade);
return "降级策略已" + (degrade ? "开启" : "关闭");
}
}
Redis宕机时直接返回兜底数据,保障服务不挂;Redis集群 / 哨兵模式,避免单节点宕机导致缓存层不可用;Sentinel/Nginx做接口限流(比如每秒最多 1000 次请求),进一步保护数据库;Redis命中率、数据库QPS,异常时及时告警。Key同时过期,控制缓存重建的并发量,保障缓存层高可用,做好降级兜底;缓存击穿是指单个热点 Key(比如秒杀商品、热门榜单)在缓存中突然过期,此时大量并发请求会瞬间绕过缓存直接冲击数据库,导致数据库因单点压力过大而性能骤降甚至宕机。
针对缓存击穿,主流且高效的解决方案有两种:
Key永不过期,给Value增加「逻辑过期时间」,请求时返回旧数据 + 异步刷新缓存(性能优先,高并发下更友好)。以下以Java + Spring Boot + Redis + Redisson(分布式锁)为例,模拟「电商秒杀商品查询」的高并发场景(比如爆款手机的热点 Key 过期,10 万 + 并发请求涌入)。
import com.alibaba.fastjson2.JSON;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Service
public class SeckillProductService {
@Resource
private RedisTemplate redisTemplate;
@Resource
private RedissonClient redissonClient;
// 缓存Key前缀
private static final String PRODUCT_KEY_PREFIX = "seckill:product:";
// 分布式锁前缀
private static final String LOCK_KEY_PREFIX = "lock:seckill:product:";
// 锁超时时间(避免死锁)
private static final long LOCK_EXPIRE_SECONDS = 30;
// 热点商品逻辑过期时间(30分钟)
private static final long LOGIC_EXPIRE_SECONDS = 30 * 60;
// 异步刷新缓存的线程池(核心数=CPU核心数*2)
private static final ExecutorService CACHE_REFRESH_POOL = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
// ====================== 方案1:互斥锁(分布式锁)方案(一致性优先) ======================
/**
* 查询秒杀商品(互斥锁防击穿)
* 适用场景:对数据一致性要求高,允许请求短暂阻塞
*/
public Product getProductByMutexLock(Long productId) {
String cacheKey = PRODUCT_KEY_PREFIX + productId;
// 步骤1:查询Redis缓存
String productJson = (String) redisTemplate.opsForValue().get(cacheKey);
if (productJson != null && !productJson.isEmpty()) {
System.out.println("互斥锁方案-缓存命中:商品ID " + productId);
return JSON.parseObject(productJson, Product.class);
}
// 步骤2:缓存未命中,加分布式锁(仅1个线程重建缓存)
String lockKey = LOCK_KEY_PREFIX + productId;
RLock lock = redissonClient.getLock(lockKey);
Product product = null;
try {
// 尝试获取锁(最多等5秒,锁自动过期30秒)
boolean lockAcquired = lock.tryLock(5, LOCK_EXPIRE_SECONDS, TimeUnit.SECONDS);
if (!lockAcquired) {
// 获取锁失败:重试(或返回兜底数据)
System.out.println("互斥锁方案-获取锁失败,重试:商品ID " + productId);
return getProductByMutexLock(productId); // 简单重试,生产可限制重试次数
}
// 步骤3:双重检查缓存(防止其他线程已重建缓存)
productJson = (String) redisTemplate.opsForValue().get(cacheKey);
if (productJson != null && !productJson.isEmpty()) {
product = JSON.parseObject(productJson, Product.class);
return product;
}
// 步骤4:查询数据库(模拟高耗时操作)
product = queryProductFromDb(productId);
if (product == null) {
// 空值缓存(防穿透,此处复用)
redisTemplate.opsForValue().set(cacheKey, "", 5, TimeUnit.MINUTES);
return null;
}
// 步骤5:写入缓存(设置物理过期时间,比如10分钟)
redisTemplate.opsForValue().set(
cacheKey,
JSON.toJSONString(product),
10,
TimeUnit.MINUTES
);
System.out.println("互斥锁方案-缓存重建完成:商品ID " + productId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("获取分布式锁失败", e);
} finally {
// 释放锁(仅持有锁的线程释放)
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return product;
}
// ====================== 方案2:逻辑过期方案(性能优先,高并发友好) ======================
/**
* 查询秒杀商品(逻辑过期防击穿)
* 适用场景:高并发、允许数据短暂不一致(比如秒杀商品信息不要求实时更新)
*/
public Product getProductByLogicExpire(Long productId) {
String cacheKey = PRODUCT_KEY_PREFIX + productId;
// 步骤1:查询Redis缓存(逻辑过期的封装对象)
CacheDataWithLogicExpire cacheData = (CacheDataWithLogicExpire) redisTemplate.opsForValue().get(cacheKey);
if (cacheData == null) {
// 缓存未初始化:走数据库查询(首次加载)
return queryProductFromDb(productId);
}
// 步骤2:判断是否逻辑过期
if (!cacheData.isExpired()) {
// 未过期:直接返回旧数据
System.out.println("逻辑过期方案-缓存未过期:商品ID " + productId);
return cacheData.getData();
}
// 步骤3:逻辑过期:异步刷新缓存 + 返回旧数据(核心:不阻塞请求)
System.out.println("逻辑过期方案-数据过期,异步刷新:商品ID " + productId);
CACHE_REFRESH_POOL.submit(() -> refreshProductCache(productId));
// 直接返回旧数据,不阻塞请求
return cacheData.getData();
}
/**
* 异步刷新缓存(逻辑过期专用)
*/
private void refreshProductCache(Long productId) {
String lockKey = LOCK_KEY_PREFIX + productId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 加锁:避免多个异步线程同时刷新
if (lock.tryLock(0, LOCK_EXPIRE_SECONDS, TimeUnit.SECONDS)) {
// 查询数据库
Product product = queryProductFromDb(productId);
// 写入缓存(逻辑过期,物理永不过期)
String cacheKey = PRODUCT_KEY_PREFIX + productId;
redisTemplate.opsForValue().set(
cacheKey,
CacheDataWithLogicExpire.of(product, LOGIC_EXPIRE_SECONDS),
TimeUnit.DAYS.toSeconds(365), // 物理永不过期
TimeUnit.SECONDS
);
System.out.println("逻辑过期方案-缓存刷新完成:商品ID " + productId);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 初始化热点商品缓存(项目启动/秒杀开始前调用,设置逻辑过期)
*/
public void initHotProductCache(Long productId) {
Product product = queryProductFromDb(productId);
String cacheKey = PRODUCT_KEY_PREFIX + productId;
redisTemplate.opsForValue().set(
cacheKey,
CacheDataWithLogicExpire.of(product, LOGIC_EXPIRE_SECONDS),
TimeUnit.DAYS.toSeconds(365),
TimeUnit.SECONDS
);
System.out.println("热点商品缓存初始化完成:商品ID " + productId);
}
/**
* 模拟数据库查询(高耗时,模拟秒杀商品库查询)
*/
private Product queryProductFromDb(Long productId) {
// 模拟数据库IO耗时(10ms,高并发下无防护会压垮数据库)
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 模拟秒杀商品数据(ID=10086为热点商品)
if (productId.equals(10086L)) {
return new Product(10086L, "小米14秒杀款", 2999.0, 10000);
}
return new Product(productId, "普通商品", 999.0, 0);
}
}
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@RestController
public class SeckillProductController {
@Resource
private SeckillProductService seckillProductService;
// 高并发线程池:模拟1000个并发线程(秒杀场景)
private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(1000);
/**
* 初始化热点商品缓存(测试前调用)
*/
@GetMapping("/init/hot/product/{productId}")
@ResponseBody
public String initHotProduct(@PathVariable Long productId) {
seckillProductService.initHotProductCache(productId);
return "热点商品缓存初始化完成:" + productId;
}
/**
* 测试互斥锁方案(高并发查询热点商品)
*/
@GetMapping("/test/mutex/{productId}")
@ResponseBody
public String testMutexLock(@PathVariable Long productId) {
long startTime = System.currentTimeMillis();
int requestCount = 100000; // 10万次并发请求
// 模拟10万次并发请求查询同一个热点商品
for (int i = 0; i < requestCount; i++) {
EXECUTOR.submit(() -> seckillProductService.getProductByMutexLock(productId));
}
// 等待所有请求完成
EXECUTOR.shutdown();
try {
EXECUTOR.awaitTermination(2, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long endTime = System.currentTimeMillis();
return "互斥锁方案:10万次请求处理完成,耗时:" + (endTime - startTime) + "ms";
}
/**
* 测试逻辑过期方案(高并发查询热点商品)
*/
@GetMapping("/test/logic/expire/{productId}")
@ResponseBody
public String testLogicExpire(@PathVariable Long productId) {
long startTime = System.currentTimeMillis();
int requestCount = 100000; // 10万次并发请求
// 模拟10万次并发请求查询同一个热点商品
for (int i = 0; i < requestCount; i++) {
EXECUTOR.submit(() -> seckillProductService.getProductByLogicExpire(productId));
}
// 等待所有请求完成
EXECUTOR.shutdown();
try {
EXECUTOR.awaitTermination(2, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long endTime = System.currentTimeMillis();
return "逻辑过期方案:10万次请求处理完成,耗时:" + (endTime - startTime) + "ms";
}
}
热点Key过期后,10 万次并发请求全部打到数据库:
| 方案 | 数据库查询次数 | 总耗时 | 核心优势 | 适用场景 |
|---|---|---|---|---|
| 互斥锁方案 | 1 次 | ~800ms | 数据一致性高 | 金融、订单等强一致性场景 |
| 逻辑过期方案 | 1 次(异步) | ~300ms | 无请求阻塞,性能最优 | 秒杀、资讯等高并发场景 |
Redis访问日志,识别访问频率Top N的Key,提前标记为HotKey;Key加载到Redis并设置逻辑过期;Key的缓存命中率、数据库QPS,异常时及时告警;Key要精准(比如按商品 ID),避免粗粒度锁导致性能瓶颈。核心方案:防缓存击穿的核心是「控制热点Key过期后的数据库请求量」,互斥锁(一致性优先)和逻辑过期(性能优先)是两种主流方案;
关键要点:
| 问题类型 | 核心特征 | 解决方案核心 |
|---|---|---|
| 缓存穿透 | 请求不存在的数据 | 布隆过滤器 + 空值缓存 |
| 缓存雪崩 | 大量 Key 过期 / Redis 宕机 | 随机过期 + 分布式锁 + 高可用 |
| 缓存击穿 | 单个热点 Key 过期 | 互斥锁 / 逻辑过期 |
高并发下的缓存设计,从来不是 “一招鲜吃遍天” 的简单技巧,而是平衡性能、一致性、可用性的工程艺术。本文分享的进阶写法,核心是围绕 “规避并发风险、提升缓存有效性、降低异常影响” 三个核心目标展开 —— 从解决缓存击穿的双重检查锁,到应对缓存雪崩的过期时间随机化,再到兼顾一致性的缓存更新策略,每一种写法的背后,都是对高并发场景下 “极端情况” 的预判与应对。
需要强调的是,没有任何一种缓存方案是 “万能” 的:单机场景下的本地缓存优化,和分布式集群下的 Redis 缓存设计,其核心考量完全不同;读多写少的业务,与读写频繁的业务,缓存策略也需要差异化调整。真正优秀的缓存实现,是基于业务场景的 “量身定制”—— 理解每种进阶写法的适用场景、优缺点,结合压测数据持续调优,才是让缓存真正成为高并发系统 “性能底座” 的关键。
好了,今天的分享就到此结束了,如果文章对你有所帮助,欢迎:点赞+评论+收藏,我是:IT_sunshine ,我们下期见!
上一篇: 妖精漫画免费下载最新版本安装包-妖精漫画免费浏览下载入口大全
下一篇: 石墨文档-官网在线使用入口