1. Redis全局唯一ID生成实战在分布式系统中生成全局唯一ID是一个常见需求。传统数据库自增ID在分布式环境下存在性能瓶颈而Redis的高性能特性使其成为理想的解决方案。1.1 ID组成结构设计我们设计的64位ID结构如下符号位1bit固定为0保证ID为正数时间戳31bit以秒为单位可使用约69年序列号32bit每秒可生成2^32个不同ID这种设计具有以下优势时间戳在前保证ID趋势递增每秒可生成42亿个ID满足高并发需求无中心节点依赖各服务可独立生成1.2 Redis实现方案Component public class RedisIdWorker { private static final long BEGIN_TIMESTAMP 1640995200L; // 2022-01-01 00:00:00 private static final int COUNT_BITS 32; private StringRedisTemplate stringRedisTemplate; public long nextId(String keyPrefix) { // 1.生成时间戳 LocalDateTime now LocalDateTime.now(); long nowSecond now.toEpochSecond(ZoneOffset.UTC); long timestamp nowSecond - BEGIN_TIMESTAMP; // 2.生成序列号按天分片 String date now.format(DateTimeFormatter.ofPattern(yyyy:MM:dd)); long count stringRedisTemplate.opsForValue() .increment(icr: keyPrefix : date); // 3.拼接并返回 return timestamp COUNT_BITS | count; } }关键设计点解析按天分片计数器避免单个key无限增长便于数据清理位运算拼接通过左移和或运算高效组合时间戳与序列号基准时间使用固定时间戳减少ID长度提示BEGIN_TIMESTAMP应根据业务预期使用年限调整建议设置为系统上线时间2. 秒杀系统核心实现2.1 基础秒杀流程public Result seckillVoucher(Long voucherId) { // 1.查询优惠券 SeckillVoucher voucher seckillVoucherService.getById(voucherId); // 2.校验秒杀时间 if (voucher.getBeginTime().isAfter(LocalDateTime.now())) { return Result.fail(秒杀尚未开始); } if (voucher.getEndTime().isBefore(LocalDateTime.now())) { return Result.fail(秒杀已经结束); } // 3.校验库存 if (voucher.getStock() 1) { return Result.fail(库存不足); } // 4.扣减库存 boolean success seckillVoucherService.update() .setSql(stock stock - 1) .eq(voucher_id, voucherId) .gt(stock, 0) .update(); if (!success) { return Result.fail(库存不足); } // 5.创建订单 VoucherOrder voucherOrder new VoucherOrder(); voucherOrder.setId(redisIdWorker.nextId(order)); voucherOrder.setUserId(UserHolder.getUser().getId()); voucherOrder.setVoucherId(voucherId); save(voucherOrder); return Result.ok(voucherOrder.getId()); }2.2 超卖问题解决方案乐观锁实现方案boolean success seckillVoucherService.update() .setSql(stock stock - 1) .eq(voucher_id, voucherId) .gt(stock, 0) // 核心防超卖逻辑 .update();原理分析在更新时校验库存是否大于0利用数据库行锁保证原子性更新失败时返回库存不足性能优化建议库存预扣减提前将库存加载到Redis异步创建订单使用消息队列削峰库存预热活动前预先分配库存到不同节点3. 一人一单实现方案3.1 同步锁实现演进版本1.0问题public synchronized Result createVoucherOrder(Long voucherId) { // 方法级别锁导致所有用户串行 }版本2.0改进public Result createVoucherOrder(Long voucherId) { synchronized(userId.toString().intern()) { // 用户级别锁 } }但存在事务提交与锁释放时序问题锁在事务内释放事务提交前其他线程可能获取锁最终方案synchronized (userId.toString().intern()) { IVoucherOrderService proxy (IVoucherOrderService) AopContext.currentProxy(); return proxy.createVoucherOrder(voucherId); }3.2 集群环境问题单机锁在集群环境下失效需要使用分布式锁不同JVM的锁互不感知需要跨JVM的互斥机制4. Redis分布式锁实现4.1 基础实现public boolean tryLock(long timeoutSec) { String threadId ID_PREFIX Thread.currentThread().getId(); return Boolean.TRUE.equals( stringRedisTemplate.opsForValue() .setIfAbsent(KEY_PREFIX name, threadId, timeoutSec, TimeUnit.SECONDS) ); } public void unlock() { stringRedisTemplate.delete(KEY_PREFIX name); }4.2 误删问题优化增加锁标识校验public void unlock() { String threadId ID_PREFIX Thread.currentThread().getId(); String id stringRedisTemplate.opsForValue().get(KEY_PREFIX name); if(threadId.equals(id)) { stringRedisTemplate.delete(KEY_PREFIX name); } }4.3 原子性问题解决方案使用Lua脚本保证原子性-- unlock.lua if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 endJava调用private static final DefaultRedisScriptLong UNLOCK_SCRIPT; static { UNLOCK_SCRIPT new DefaultRedisScript(); UNLOCK_SCRIPT.setLocation(new ClassPathResource(unlock.lua)); UNLOCK_SCRIPT.setResultType(Long.class); } public void unlock() { stringRedisTemplate.execute( UNLOCK_SCRIPT, Collections.singletonList(KEY_PREFIX name), ID_PREFIX Thread.currentThread().getId()); }5. 实战经验总结5.1 性能优化关键点库存热点问题使用Redis集群分散压力本地缓存异步刷新策略库存分片如将1000库存分为10个key分布式锁优化锁粒度尽可能小合理设置超时时间不宜过长或过短考虑锁重入需求事务控制避免长事务合理设置事务隔离级别非核心流程可考虑最终一致性5.2 常见问题排查锁未释放确保finally块中释放锁添加锁过期时间作为兜底监控锁持有时间库存不一致定期对账添加库存操作日志实现补偿机制集群时钟不同步部署NTP时间同步服务关键业务使用同一时间源在实际项目中我们还需要考虑限流、降级、熔断等机制来保障系统稳定性。建议在测试环境充分模拟高并发场景通过压测发现潜在问题。