Redis 在电商系统中的应用:高并发场景下的架构艺术
🛒 Redis 在电商系统中的应用:高并发场景下的架构艺术
文章目录
- 🛒 Redis 在电商系统中的应用:高并发场景下的架构艺术
- 🚀 一、电商系统架构挑战
- 💡 电商系统核心痛点
- 📊 电商场景性能要求
- 🛍️ 二、购物车系统实战
- 💡 购物车业务特点
- 🛠️ Redis Hash 购物车实现
- 📊 购物车性能优化
- 📦 三、库存扣减方案
- ⚡ 原子库存扣减
- 🛡️ 超卖防护方案
- 📊 库存同步策略
- ⚡ 四、秒杀系统设计
- 🎯 秒杀系统架构
- ⚡ 秒杀核心实现
- 🛡️ 多层防护策略
- 💡 五、总结与最佳实践
- 📋 电商Redis使用Checklist
- 🚀 架构设计原则
- 📊 监控与告警
- 🎯 不同场景优化策略
🚀 一、电商系统架构挑战
💡 电商系统核心痛点
电商系统面临的高并发挑战极其复杂,Redis 在其中扮演着关键角色:
📊 电商场景性能要求
场景 | QPS要求 | 响应时间 | 数据一致性 | 可用性 |
---|---|---|---|---|
商品浏览 | 10,000+ | <50ms | 最终一致 | 99.9% |
购物车 | 5,000+ | <100ms | 强一致 | 99.99% |
库存查询 | 20,000+ | <30ms | 最终一致 | 99.9% |
下单支付 | 2,000+ | <200ms | 强一致 | 99.99% |
秒杀活动 | 100,000+ | <10ms | 强一致 | 99.999% |
🛍️ 二、购物车系统实战
💡 购物车业务特点
购物车是电商系统中最复杂的模块之一,需要平衡并发性能和数据结构合理性:
🛠️ Redis Hash 购物车实现
数据结构设计:
// 购物车Redis Key设计
String cartKey = "cart:user:" + userId;// Hash结构:
// Field: productId_skuId
// Value: JSON字符串包含商品信息
Map<String, String> cartData = new HashMap<>();
cartData.put("product_1001_2001", "{\"quantity\":2,\"price\":19900,\"name\":\"iPhone 13\",\"image\":\"iphone13.jpg\"}");
cartData.put("product_1002_2002", "{\"quantity\":1,\"price\":9900,\"name\":\"AirPods\",\"image\":\"airpods.jpg\"}");// 存储到Redis
jedis.hset(cartKey, cartData);
jedis.expire(cartKey, 30 * 24 * 3600); // 30天过期
购物车操作示例:
public class CartService {private Jedis jedis;private static final int CART_EXPIRE = 2592000; // 30天// 添加商品到购物车public void addToCart(Long userId, CartItem item) {String cartKey = "cart:user:" + userId;String field = "product_" + item.getProductId() + "_" + item.getSkuId();String value = serializeCartItem(item);// 使用Pipeline提高性能Pipeline pipeline = jedis.pipelined();pipeline.hset(cartKey, field, value);pipeline.expire(cartKey, CART_EXPIRE);pipeline.sync();}// 获取购物车所有商品public List<CartItem> getCart(Long userId) {String cartKey = "cart:user:" + userId;Map<String, String> cartData = jedis.hgetAll(cartKey);List<CartItem> items = new ArrayList<>();for (String value : cartData.values()) {items.add(deserializeCartItem(value));}return items;}// 更新商品数量public void updateQuantity(Long userId, Long productId, Long skuId, int quantity) {String cartKey = "cart:user:" + userId;String field = "product_" + productId + "_" + skuId;String value = jedis.hget(cartKey, field);if (value != null) {CartItem item = deserializeCartItem(value);item.setQuantity(quantity);jedis.hset(cartKey, field, serializeCartItem(item));}}// 删除购物车商品public void removeFromCart(Long userId, Long productId, Long skuId) {String cartKey = "cart:user:" + userId;String field = "product_" + productId + "_" + skuId;jedis.hdel(cartKey, field);}
}
📊 购物车性能优化
内存优化策略:
// 购物车项压缩存储
public class CompactCartItem {// 使用数字ID代替字符串private int p; // productIdprivate int s; // skuIdprivate int q; // quantityprivate int p; // price (以分为单位)// 使用位图存储状态private byte flags;public static byte[] serialize(CompactCartItem item) {ByteBuffer buffer = ByteBuffer.allocate(16); // 固定16字节buffer.putInt(item.p);buffer.putInt(item.s);buffer.putInt(item.q);buffer.putInt(item.p);buffer.put(item.flags);return buffer.array();}
}
分区策略:
// 大购物车分片存储
public class ShardedCartService {private static final int SHARD_COUNT = 10;public String getCartShardKey(Long userId, int shardIndex) {return "cart:user:" + userId + ":shard:" + shardIndex;}public void addToLargeCart(Long userId, CartItem item) {// 根据商品ID分片int shardIndex = (item.getProductId().hashCode() & 0x7FFFFFFF) % SHARD_COUNT;String shardKey = getCartShardKey(userId, shardIndex);// 存储到分片jedis.hset(shardKey, "item_" + item.getProductId(), serialize(item));}
}
📦 三、库存扣减方案
⚡ 原子库存扣减
库存扣减是电商系统中最关键也是最容易出问题的环节:
🛡️ 超卖防护方案
方案1:Redis 原子操作
public class StockService {// 初始化库存public void initStock(Long productId, Integer stock) {String key = "stock:product:" + productId;jedis.set(key, stock.toString());}// 原子扣减库存public boolean deductStock(Long productId, Integer quantity) {String key = "stock:product:" + productId;// Lua脚本保证原子性String script = """local key = KEYS[1]local deduct = tonumber(ARGV[1])local stock = tonumber(redis.call('GET', key))if stock >= deduct thenredis.call('DECRBY', key, deduct)return stock - deductelsereturn -1end""";Long result = (Long) jedis.eval(script, 1, key, quantity.toString());return result >= 0;}
}
方案2:分布式锁保护
public class SafeStockService {private DistributedLock lock;public boolean safeDeductStock(Long productId, Integer quantity) {String lockKey = "lock:stock:" + productId;String stockKey = "stock:product:" + productId;// 获取分布式锁if (lock.tryLock(lockKey, 1000, 3000)) {try {int stock = Integer.parseInt(jedis.get(stockKey));if (stock >= quantity) {jedis.decrBy(stockKey, quantity);return true;}return false;} finally {lock.unlock(lockKey);}}throw new RuntimeException("获取锁失败");}
}
📊 库存同步策略
数据库同步方案:
public class StockSyncService {// 异步同步库存到数据库@Asyncpublic void asyncSyncStock(Long productId, Integer change) {String lockKey = "sync:lock:" + productId;if (lock.tryLock(lockKey, 500, 1000)) {try {// 1. 获取Redis当前库存String redisStock = jedis.get("stock:product:" + productId);// 2. 更新数据库stockDao.updateStock(productId, Integer.parseInt(redisStock));// 3. 记录同步日志logSyncOperation(productId, change);} finally {lock.unlock(lockKey);}}}// 库存核对任务@Scheduled(fixedRate = 300000) // 每5分钟执行一次public void stockReconciliation() {List<Product> products = productDao.findAll();for (Product product : products) {String redisStock = jedis.get("stock:product:" + product.getId());if (redisStock != null) {int redisValue = Integer.parseInt(redisStock);if (redisValue != product.getStock()) {log.warn("库存不一致: product={}, db={}, redis={}", product.getId(), product.getStock(), redisValue);// 自动修复策略repairStock(product.getId(), product.getStock(), redisValue);}}}}
}
⚡ 四、秒杀系统设计
🎯 秒杀系统架构
秒杀是电商系统中最极端的并发场景,需要多层防护:
⚡ 秒杀核心实现
1. 预减库存方案:
public class SeckillService {// 预热秒杀库存public void预热秒杀库存(Long seckillId, Integer stock) {String stockKey = "seckill:stock:" + seckillId;String soldKey = "seckill:sold:" + seckillId;jedis.set(stockKey, stock.toString());jedis.set(soldKey, "0");jedis.expire(stockKey, 3600); // 1小时过期jedis.expire(soldKey, 3600);}// 秒杀扣减public SeckillResult seckill(Long userId, Long seckillId) {// 1. 频率限制if (!rateLimiter.tryAcquire()) {return SeckillResult.error("请求过于频繁");}// 2. 资格检查if (jedis.sismember("seckill:blacklist", userId.toString())) {return SeckillResult.error("无参与资格");}// 3. Lua原子扣减String script = """local stockKey = KEYS[1]local soldKey = KEYS[2]local stock = tonumber(redis.call('GET', stockKey))if stock <= 0 thenreturn 0endredis.call('DECR', stockKey)redis.call('INCR', soldKey)return 1""";Long result = (Long) jedis.eval(script, 2, "seckill:stock:" + seckillId, "seckill:sold:" + seckillId);if (result == 1) {// 4. 生成订单号String orderId = generateOrderId();// 5. 订单进入队列sendToQueue(orderId, userId, seckillId);return SeckillResult.success(orderId);}return SeckillResult.error("库存不足");}
}
2. 排队削峰方案:
public class SeckillQueueService {// 秒杀请求排队public SeckillResult seckillWithQueue(Long userId, Long seckillId) {// 1. 生成排队号Long queueNumber = jedis.incr("seckill:queue:" + seckillId);// 2. 检查队列位置if (queueNumber > 10000) { // 只允许1万人排队return SeckillResult.error("排队人数已满");}// 3. 加入排队队列String queueKey = "seckill:queue:list:" + seckillId;jedis.lpush(queueKey, userId + ":" + System.currentTimeMillis());// 4. 返回排队信息return SeckillResult.queue(queueNumber);}// 队列处理 workerpublic void processSeckillQueue(Long seckillId) {String queueKey = "seckill:queue:list:" + seckillId;while (true) {// 从队列获取请求String request = jedis.rpop(queueKey);if (request == null) {sleep(100);continue;}String[] parts = request.split(":");Long userId = Long.parseLong(parts[0]);try {// 实际处理秒杀doSeckill(userId, seckillId);} catch (Exception e) {log.error("秒杀处理失败: {}", request, e);}}}
}
🛡️ 多层防护策略
限流与降级:
public class SeckillProtection {// 多层限流保护private RateLimiter userLimiter = RateLimiter.create(10); // 用户级:10次/秒private RateLimiter ipLimiter = RateLimiter.create(1000); // IP级:1000次/秒private RateLimiter globalLimiter = RateLimiter.create(10000); // 全局:10000次/秒public boolean allowRequest(String userId, String ip) {// 1. 全局限流if (!globalLimiter.tryAcquire()) {return false;}// 2. IP限流if (!ipLimiter.tryAcquire()) {return false;}// 3. 用户限流if (!userLimiter.tryAcquire()) {return false;}// 4. 黑名单检查if (jedis.sismember("seckill:blacklist", userId)) {return false;}return true;}// 动态降级public void checkSystemLoad() {// 监控系统负载double load = getSystemLoadAverage();long memory = getUsedMemory();if (load > 80.0 || memory > 0.9 * getTotalMemory()) {// 自动降级enableDegradeMode();}}private void enableDegradeMode() {// 1. 减少排队人数jedis.set("seckill:queue:max", "1000");// 2. 限制每秒处理数globalLimiter.setRate(1000);// 3. 关闭非核心功能jedis.set("seckill:features:detail", "false");}
}
💡 五、总结与最佳实践
📋 电商Redis使用Checklist
数据结构选择:
-
✅ String: 简单缓存、计数器
-
✅ Hash: 购物车、商品属性
-
✅ List: 消息队列、最新列表
-
✅ Set: 黑名单、标签系统
-
✅ ZSet: 排行榜、优先级队列
-
✅ HyperLogLog: UV统计
-
✅ Bitmap: 用户签到、特征标记
性能优化建议:
# redis.conf 优化配置
maxmemory 16gb
maxmemory-policy allkeys-lru
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
activerehashing yes
🚀 架构设计原则
缓存策略:
graph TBA[请求] --> B[本地缓存]B --> C[分布式缓存]C --> D[数据库]B -->|缓存命中| AC -->|缓存命中| BD -->|缓存未命中| Cstyle B fill:#9f9,stroke:#333style C fill:#99f,stroke:#333
分级缓存实现:
public class MultiLevelCache {private LoadingCache<String, Object> localCache; // Guava Cacheprivate JedisPool redisPool;public Object get(String key) {try {// 1. 检查本地缓存return localCache.get(key, () -> {// 2. 检查Redistry (Jedis jedis = redisPool.getResource()) {Object value = jedis.get(key);if (value == null) {// 3. 检查数据库value = database.get(key);if (value != null) {jedis.setex(key, 3600, serialize(value));}}return value;}});} catch (Exception e) {// 降级到直接查询Redistry (Jedis jedis = redisPool.getResource()) {return jedis.get(key);}}}
}
📊 监控与告警
关键监控指标:
# 内存使用
redis-cli info memory# 命中率
redis-cli info stats | grep hitrate# 持久化状态
redis-cli info persistence# 复制状态
redis-cli info replication# 慢查询
redis-cli slowlog get
告警规则配置:
# alert-rules.yml
groups:
- name: redis-alertsrules:- alert: RedisDownexpr: up{instance="redis"} == 0for: 1m- alert: HighMemoryUsageexpr: redis_memory_used_bytes / redis_memory_max_bytes > 0.8for: 5m- alert: CacheHitRateLowexpr: rate(redis_keyspace_hits_total[5m]) / rate(redis_keyspace_misses_total[5m]) < 0.9for: 10m- alert: ManyConnectionsexpr: redis_connected_clients > 1000for: 5m
🎯 不同场景优化策略
场景 | 主要优化策略 | 数据结构 | 持久化策略 | 监控重点 |
---|---|---|---|---|
购物车 | Hash压缩、分片 | Hash | AOF每秒 | 内存使用、响应时间 |
库存 | 原子操作、Lua | String | RDB快照 | 原子性、一致性 |
秒杀 | 队列削峰、限流 | List+String | 禁用持久化 | QPS、排队长度 |
商品缓存 | 多级缓存、过期 | Hash+String | AOF追加 | 命中率、缓存穿透 |
订单 | 异步队列、分库 | List | AOF每秒 | 队列堆积、处理延迟 |