引言
在高并发电商系统中,Redis 缓存是提升系统性能的关键组件。本文将分享我在实习期间处理日均 10 万+ PV 电商平台的 Redis 优化经验。
缓存架构设计
整体架构
1 2 3
| 用户请求 → CDN → Nginx → 应用层 → Redis缓存 → MySQL ↓ 本地缓存(Caffeine)
|
多级缓存策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Component public class MultiLevelCache { private final Cache<String, Object> localCache = Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(5, TimeUnit.MINUTES) .build(); @Autowired private StringRedisTemplate redisTemplate; public Object get(String key) { Object value = localCache.getIfPresent(key); if (value != null) { return value; } value = redisTemplate.opsForValue().get(key); if (value != null) { localCache.put(key, value); return value; } return null; } }
|
商品详情缓存优化
缓存预热实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Component public class ProductCacheWarmer { @Autowired private StringRedisTemplate redisTemplate; @Autowired private ProductService productService; @Scheduled(cron = "0 0 2 * * ?") public void warmUpHotProducts() { List<Long> hotProductIds = productService.getHotProductIds(7, 100); for (Long productId : hotProductIds) { ProductDetail detail = productService.getProductDetail(productId); String cacheKey = "product:detail:" + productId; redisTemplate.opsForValue().set( cacheKey, JSON.toJSONString(detail), Duration.ofHours(24) ); } log.info("缓存预热完成,共预热 {} 个商品", hotProductIds.size()); } }
|
缓存更新策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| @Service public class ProductCacheService { @Autowired private StringRedisTemplate redisTemplate; private static final String PRODUCT_KEY_PREFIX = "product:detail:"; private static final long CACHE_TTL = 3600;
public ProductDetail getProductDetail(Long productId) { String cacheKey = PRODUCT_KEY_PREFIX + productId; String cached = redisTemplate.opsForValue().get(cacheKey); if (StringUtils.hasText(cached)) { return JSON.parseObject(cached, ProductDetail.class); } ProductDetail detail = productService.queryFromDB(productId); if (detail != null) { redisTemplate.opsForValue().set( cacheKey, JSON.toJSONString(detail), CACHE_TTL, TimeUnit.SECONDS ); } return detail; }
public void updateProduct(ProductDetail product) { productService.update(product); String cacheKey = PRODUCT_KEY_PREFIX + product.getId(); redisTemplate.delete(cacheKey); } }
|
分布式锁实现
Redisson 分布式锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Component public class DistributedLockManager { @Autowired private RedissonClient redissonClient;
public boolean tryLockStock(Long productId, long waitTime, long leaseTime) { String lockKey = "lock:stock:" + productId; RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } }
public void unlockStock(Long productId) { String lockKey = "lock:stock:" + productId; RLock lock = redissonClient.getLock(lockKey); if (lock.isHeldByCurrentThread()) { lock.unlock(); } } }
|
库存扣减实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| @Service public class StockService { @Autowired private StringRedisTemplate redisTemplate; @Autowired private DistributedLockManager lockManager;
public boolean deductStock(Long productId, Integer quantity) { String stockKey = "stock:" + productId; String lockKey = "lock:stock:" + productId; Long remainStock = redisTemplate.opsForValue().decrement(stockKey, quantity); if (remainStock == null || remainStock < 0) { redisTemplate.opsForValue().increment(stockKey, quantity); return false; } try { asyncSyncToDB(productId, quantity); return true; } catch (Exception e) { redisTemplate.opsForValue().increment(stockKey, quantity); throw new StockException("库存扣减失败", e); } } @Async protected void asyncSyncToDB(Long productId, Integer quantity) { productMapper.decreaseStock(productId, quantity); } }
|
缓存问题解决方案
1. 缓存穿透
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| @Component public class CachePenetrationSolution { @Autowired private StringRedisTemplate redisTemplate;
public ProductDetail getWithBloomFilter(Long productId) { String bloomKey = "bloom:product"; Boolean mightExist = redisTemplate.opsForValue() .getBit(bloomKey, productId.hashCode()); if (Boolean.FALSE.equals(mightExist)) { return null; } return getProductDetail(productId); }
public ProductDetail getWithNullCache(Long productId) { String cacheKey = "product:detail:" + productId; String cached = redisTemplate.opsForValue().get(cacheKey); if ("NULL".equals(cached)) { return null; } ProductDetail detail = queryFromDB(productId); if (detail == null) { redisTemplate.opsForValue().set( cacheKey, "NULL", 5, TimeUnit.MINUTES ); } return detail; } }
|
2. 缓存雪崩
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Component public class CacheAvalancheSolution { @Autowired private StringRedisTemplate redisTemplate;
public void setWithRandomExpire(String key, Object value, long baseExpire) { long randomOffset = ThreadLocalRandom.current().nextInt(300); long finalExpire = baseExpire + randomOffset; redisTemplate.opsForValue().set( key, JSON.toJSONString(value), finalExpire, TimeUnit.SECONDS ); }
public void setHotData(String key, Object value) { redisTemplate.opsForValue().set(key, JSON.toJSONString(value)); String logicExpireKey = "expire:" + key; redisTemplate.opsForValue().set( logicExpireKey, String.valueOf(System.currentTimeMillis() + 3600000), 1, TimeUnit.HOURS ); } }
|
性能优化成果
在实际项目中,通过上述优化措施:
| 指标 |
优化前 |
优化后 |
提升 |
| 商品查询 QPS |
~200 |
~800 |
300% |
| 平均响应时间 |
120ms |
80ms |
33% |
| 数据库查询量 |
100% |
20% |
80% |
| 库存超卖 |
偶有发生 |
零超卖 |
100% |
总结
Redis 缓存优化需要综合考虑:
- 多级缓存:本地缓存 + Redis 缓存
- 预热策略:提前加载热点数据
- 更新策略:Cache-Aside 模式
- 并发控制:分布式锁 + 原子操作
- 异常处理:防穿透、防雪崩、防击穿
合理的缓存设计能够将系统性能提升数倍,是高并发系统的必备技能。
本文基于电商平台实习经验总结,日均 10 万+ PV 场景实践