资讯详情

资讯详情

Redis实战:缓存三兄弟与分布式锁解决方案

1. Redis实战问题整理黑马点评项目深度解析作为一款高性能的内存数据库Redis在现代互联网项目中扮演着越来越重要的角色。我在参与黑马点评项目开发过程中遇到了各种Redis相关的典型问题从基础配置到高级应用场景都有涉及。这篇文章将系统梳理这些实战问题包含缓存三兄弟缓存穿透、击穿、雪崩的解决方案、分布式锁的实现细节、秒杀场景的优化手段等核心内容。如果你是刚开始接触Redis的开发者或者正在准备Redis相关面试这篇文章将为你提供从入门到进阶的完整参考。所有解决方案都经过线上环境验证包含参数调优建议和性能对比数据可以直接应用到你的项目中。2. Redis基础环境搭建与配置2.1 Windows/Linux环境安装指南Redis官方推荐在Linux环境下运行但开发阶段Windows环境也能满足基本需求。Windows安装可以直接下载微软维护的Redis-x64-xxx.zip压缩包解压后运行redis-server.exe即可启动服务。但要注意几个关键配置# redis.windows.conf 关键参数 maxmemory 1024mb maxmemory-policy allkeys-lru timeout 300Linux环境下通过源码编译安装能获得更好的性能wget http://download.redis.io/releases/redis-6.2.6.tar.gz tar xzf redis-6.2.6.tar.gz cd redis-6.2.6 make make install重要提示生产环境务必设置密码认证requirepass参数并禁用危险命令通过rename-command配置2.2 可视化客户端选型对比开发调试阶段推荐使用Another Redis Desktop Manager相比Redis Desktop Manager具有以下优势支持集群模式管理提供内存分析工具支持命令自动补全免费开源无广告3. 缓存三兄弟问题解决方案3.1 缓存穿透防护实战黑马点评项目中遇到的典型场景频繁查询不存在的店铺ID导致大量请求穿透到数据库。我们最终采用布隆过滤器空值缓存的组合方案使用Redisson的RBloomFilter实现布隆过滤器查询前先检查布隆过滤器对于确实不存在的数据缓存空值设置较短过期时间// 布隆过滤器初始化 RBloomFilterString bloomFilter redisson.getBloomFilter(shopIds); bloomFilter.tryInit(1000000L, 0.03); // 查询逻辑 if(!bloomFilter.contains(shopId)) { return Result.fail(店铺不存在); } Object value redisTemplate.opsForValue().get(cache: shopId); if(value ! null) { return .equals(value) ? null : value; }3.2 缓存击穿应对策略热点key突然失效时采用互斥锁方案保证只有一个线程重建缓存public Shop queryWithMutex(Long id) { String key cache:shop: id; // 1. 查缓存 String shopJson redisTemplate.opsForValue().get(key); if (StrUtil.isNotBlank(shopJson)) { return JSONUtil.toBean(shopJson, Shop.class); } // 2. 获取锁 String lockKey lock:shop: id; try { boolean isLock tryLock(lockKey); if (!isLock) { Thread.sleep(50); return queryWithMutex(id); // 重试 } // 3. 查数据库 Shop shop getById(id); // 4. 写缓存 redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), 30, TimeUnit.MINUTES); return shop; } finally { unlock(lockKey); } }3.3 缓存雪崩预防方案我们通过以下措施预防雪崩设置随机过期时间基础时间随机偏移量热点数据永不过期后台定时更新多级缓存架构Redis本地缓存// 随机过期时间实现 int baseTime 3600; // 1小时 int randomTime new Random().nextInt(600); // 0-10分钟随机 redisTemplate.opsForValue().set(key, value, baseTime randomTime, TimeUnit.SECONDS);4. 分布式锁深度优化4.1 Redisson实现可重入锁黑马点评的秒杀功能最初使用简单的SETNX实现分布式锁后来升级为Redisson解决方案RLock lock redissonClient.getLock(order: userId); try { // 尝试加锁最多等待100秒上锁后30秒自动解锁 boolean res lock.tryLock(100, 30, TimeUnit.SECONDS); if (res) { // 处理业务逻辑 } } finally { lock.unlock(); }4.2 锁续期与看门狗机制Redisson的看门狗机制会自动延长锁持有时间默认30秒周期避免业务未执行完锁已过期的问题。关键配置参数# application.properties spring.redis.redisson.configclasspath:redisson.yamlredisson.yaml配置示例singleServerConfig: idleConnectionTimeout: 10000 connectTimeout: 10000 timeout: 3000 retryAttempts: 3 retryInterval: 1500 subscriptionsPerConnection: 5 clientName: null address: redis://127.0.0.1:6379 lockWatchdogTimeout: 30000 # 看门狗超时时间5. 秒杀系统核心设计5.1 库存预热与原子扣减提前将秒杀商品库存加载到Redis// 预热库存 redisTemplate.opsForValue().set(seckill:stock: seckillId, 100); // Lua脚本原子扣减 String script if tonumber(redis.call(get, KEYS[1])) 0 then return redis.call(decr, KEYS[1]) else return -1 end; RedisScriptLong redisScript new DefaultRedisScript(script, Long.class); Long result redisTemplate.execute(redisScript, Collections.singletonList(seckill:stock: seckillId));5.2 限流与队列削峰使用Redis实现滑动窗口限流public boolean tryAcquire(String key, int windowSize, int maxCount) { long now System.currentTimeMillis(); Long count redisTemplate.opsForZSet().count(key, now - windowSize * 1000, now); if (count ! null count maxCount) { return false; } redisTemplate.opsForZSet().add(key, String.valueOf(now), now); redisTemplate.expire(key, windowSize, TimeUnit.SECONDS); return true; }订单消息进入RabbitMQ队列异步处理6. Redis持久化与高可用6.1 RDB与AOF配置策略黑马点评生产环境采用混合持久化方案# redis.conf save 900 1 save 300 10 save 60 10000 appendonly yes aof-use-rdb-preamble yes aof-rewrite-incremental-fsync yes6.2 哨兵模式部署要点三节点哨兵配置示例# sentinel.conf sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 17. 性能优化实战技巧7.1 管道与批量操作对比测试显示使用管道技术可提升5-10倍性能redisTemplate.executePipelined((RedisCallbackObject) connection - { for (int i 0; i 1000; i) { connection.stringCommands().set((key: i).getBytes(), (value: i).getBytes()); } return null; });7.2 内存优化方案使用Hash结构存储对象时控制field数量建议不超过1000对于大量小对象考虑使用RedisJSON模块定期执行MEMORY PURGE命令释放内存碎片8. 常见面试问题解析8.1 Redis数据结构应用场景String计数器、分布式锁Hash对象存储、购物车List消息队列、最新列表Set标签、共同好友ZSet排行榜、延迟队列8.2 持久化机制对比特性RDBAOF文件大小小大恢复速度快慢数据安全性可能丢失最后几分钟数据最多丢失1秒数据性能影响保存时影响性能每次写入都有开销适用场景灾难恢复需要更高数据安全性9. 生产环境问题排查实录9.1 连接池耗尽问题现象服务突然无法获取Redis连接 排查步骤检查maxTotal配置建议设置100-500分析连接泄露使用CLIENT LIST命令监控连接数波动解决方案Bean public LettuceConnectionFactory redisConnectionFactory() { LettucePoolingClientConfiguration config LettucePoolingClientConfiguration.builder() .poolConfig(new GenericObjectPoolConfig() {{ setMaxTotal(200); setMaxIdle(50); setMinIdle(10); }}) .build(); return new LettuceConnectionFactory(new RedisStandaloneConfiguration(localhost, 6379), config); }9.2 热点Key发现与处理使用redis-cli --hotkeys命令识别热点Key后我们采取以下措施本地缓存热点数据对Key进行分片如user:1:info → user:1:{slot}:info使用Redis集群分散压力10. 项目经验总结与建议经过黑马点评项目的实战我总结了以下几点Redis使用心得任何缓存操作都要考虑异常情况做好降级方案分布式锁不仅要考虑加锁更要设计好解锁和续期逻辑大Value是性能杀手单个Value建议不超过10KB定期使用SCAN命令检查异常Key模式监控指标要包含命中率、延迟、内存碎片率等关键指标对于准备面试的同学建议重点掌握缓存问题的解决方案分布式锁的实现原理Redis持久化机制集群模式的工作原理各种数据结构的实际应用场景
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →