Redis中的10种高级用法,直接起飞!

张开发
2026/4/6 2:09:36 15 分钟阅读

分享文章

Redis中的10种高级用法,直接起飞!
前言在很多小伙伴的印象中Redis就是一个用来做缓存的工具。但如果你只把它当作缓存那真是“杀鸡用牛刀”了。Redis的强大远不止于此它的高级特性可以让你的系统在性能、扩展性、可靠性上直接起飞。今天这篇文章就跟大家聊聊Redis中10种高级用法希望对你会有所帮助。一、布隆过滤器有些小伙伴在做高并发系统时最怕的就是缓存穿透——大量请求直接打穿缓存压垮数据库。布隆过滤器就是解决这个问题的利器。1.1 什么是布隆过滤器布隆过滤器是一种空间效率极高的概率型数据结构用于判断一个元素是否存在于一个集合中。它的特点是极省内存存储1亿个元素只需要约100MB内存存在误判判断“不在”一定准确判断“在”可能误判小概率不可删除不支持删除元素1.2 原理布隆过滤器的原理如图查询数据的流程如下图1.3 代码示例使用RedissonComponent publicclass BloomFilterService { Autowired private RedissonClient redissonClient; private RBloomFilterString bloomFilter; PostConstruct public void init() { bloomFilter redissonClient.getBloomFilter(user:bloom); // 初始化预计插入100万条数据误判率0.01 bloomFilter.tryInit(1000000L, 0.01); } // 添加白名单数据 public void addUser(Long userId) { bloomFilter.add(userId.toString()); } // 查询前进行拦截 public User getUserById(Long userId) { // 如果不在布隆过滤器中直接返回空避免查库 if (!bloomFilter.contains(userId.toString())) { returnnull; } // 存在则查库缓存兜底 return queryFromDB(userId); } }优点内存占用极小能有效防止缓存穿透。缺点存在误判不支持删除如需删除可用计数布隆过滤器。适用场景防止恶意请求穿透缓存、垃圾邮件过滤、爬虫URL去重。二、Redisson分布式锁Redis实现分布式锁很多人还在用SET NX EX但这在复杂场景下存在诸多隐患。Redisson的分布式锁提供了更完善的解决方案。2.1 为什么不用SET NX简单SET NX的问题锁过期时间设置不当可能导致锁提前释放释放锁时未校验持有者可能误删他人锁无法自动续期长任务执行一半锁就没了2.2 Redisson分布式锁原理Redisson使用Lua脚本保证原子性并内置了看门狗机制自动续期。2.3 代码示例Service publicclass OrderService { Autowired private RedissonClient redissonClient; public void processOrder(String orderId) { RLock lock redissonClient.getLock(order:lock: orderId); try { // 尝试加锁最多等待10秒锁有效期30秒自动续期 if (lock.tryLock(10, 30, TimeUnit.SECONDS)) { // 执行业务逻辑 doProcess(orderId); } else { thrownew RuntimeException(获取锁失败); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); thrownew RuntimeException(中断, e); } finally { // 必须释放锁 if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } }优点自动续期避免死锁可重入支持读写锁、红锁等。缺点引入Redisson依赖比原生Redis略重。适用场景分布式任务调度、库存扣减、订单创建等需要互斥的场景。三、Redisson延迟队列很多场景需要延迟处理比如订单30分钟未支付自动取消。传统方案用定时任务扫表效率低下且存在延迟。Redisson的延迟队列基于Redis的Sorted Set实现精准可靠。3.1 原理图3.2 代码示例Component publicclass DelayQueueService { Autowired private RedissonClient redissonClient; private RBlockingQueueOrder blockingQueue; private RDelayedQueueOrder delayedQueue; PostConstruct public void init() { blockingQueue redissonClient.getBlockingQueue(order:queue); delayedQueue redissonClient.getDelayedQueue(blockingQueue); } // 添加延迟任务 public void addOrder(Order order, long delay, TimeUnit unit) { delayedQueue.offer(order, delay, unit); } // 消费者单独线程 Async public void startConsumer() { while (true) { try { Order order blockingQueue.take(); // 处理延迟到期的订单 processExpiredOrder(order); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } }优点精准定时、分布式、自动持久化。缺点依赖Redis消息消费失败需自行补偿。适用场景订单超时关闭、延迟通知、定时任务调度。四、令牌桶限流面对突发流量限流是保护系统的关键。Redis Lua可以实现高性能的令牌桶算法。4.1 令牌桶原理4.2 Lua脚本实现Component publicclass RateLimiterService { Autowired private RedisTemplateString, Object redisTemplate; privatestaticfinal String LUA_SCRIPT local key KEYS[1]\n local limit tonumber(ARGV[1])\n local interval tonumber(ARGV[2])\n local current redis.call(get, key)\n if current and tonumber(current) limit then\n return 0\n else\n redis.call(incr, key)\n redis.call(expire, key, interval)\n return 1\n end; public boolean tryAcquire(String key, int limit, int intervalSec) { DefaultRedisScriptLong script new DefaultRedisScript(LUA_SCRIPT, Long.class); Long result redisTemplate.execute(script, Collections.singletonList(key), limit, intervalSec); return result ! null result 1L; } }优点支持平滑突发流量实现简单。缺点需要结合滑动窗口做更精细的限流。适用场景API限流、防刷、秒杀入口控制。五、位图统计位图Bitmap是Redis中一种非常高效的数据结构适合统计布尔型数据。它适合做海量数据的极简统计。5.1 应用场景统计日活用户用用户ID作为偏移量1表示活跃签到记录一年365天一个用户只需365个bit5.2 代码示例Component publicclass BitmapService { Autowired private RedisTemplateString, Object redisTemplate; // 用户签到 public void signIn(Long userId, LocalDate date) { String key sign: date.toString(); redisTemplate.opsForValue().setBit(key, userId, true); } // 统计某天签到人数 public Long countSignIn(LocalDate date) { String key sign: date.toString(); return redisTemplate.execute( (RedisCallbackLong) connection - connection.bitCount(key.getBytes()) ); } // 统计连续签到天数需结合Lua public Long continuousSignDays(Long userId, LocalDate date) { // 使用BITFIELD命令获取连续签到天数 // 实现略 } }优点内存占用极低1亿用户只需12MB运算速度快。缺点只能表示0/1状态不适合复杂统计。适用场景用户签到、在线状态、布隆过滤器实现。六、HyperLogLog它是去重统计的“省内存神器”。如果需要统计UV独立访客但数据量极大千万级用Set会占用大量内存。HyperLogLog是解决方案。6.1 原理HyperLogLog是一种概率算法用固定内存约12KB统计海量数据的基数误差在0.81%左右。6.2 代码示例Component publicclass UVService { Autowired private RedisTemplateString, Object redisTemplate; // 添加访问记录 public void addVisit(String date, String userId) { String key uv: date; redisTemplate.opsForHyperLogLog().add(key, userId); } // 统计UV public Long countUV(String date) { String key uv: date; return redisTemplate.opsForHyperLogLog().size(key); } // 合并多天UV如周活 public Long countWeeklyUV(ListString dates) { String destKey uv:weekly: LocalDate.now(); for (String date : dates) { redisTemplate.opsForHyperLogLog().union(destKey, uv: date); } return redisTemplate.opsForHyperLogLog().size(destKey); } }优点固定内存适合超大规模去重。缺点不精确不能单独判断某个元素是否存在。适用场景UV统计、网站访问量、大规模去重。七、GEO地理位置它在附近的人、门店查询功能中常用。Redis 3.2开始支持地理位置功能基于Sorted Set实现可以轻松计算距离、搜索附近位置。7.1 原理使用GeoHash编码经纬度以Score形式存储支持按半径、按矩形范围搜索。7.2 代码示例Component publicclass GeoService { Autowired private RedisTemplateString, Object redisTemplate; // 添加门店位置 public void addStore(Long storeId, double lng, double lat) { String key stores:geo; redisTemplate.opsForGeo().add(key, new Point(lng, lat), storeId.toString()); } // 搜索附近门店 public ListStore findNearbyStores(double lng, double lat, double radiusKm) { String key stores:geo; Circle circle new Circle(new Point(lng, lat), new Distance(radiusKm, Metrics.KILOMETERS)); GeoResultsRedisGeoCommands.GeoLocationObject results redisTemplate.opsForGeo().radius(key, circle); ListStore stores new ArrayList(); for (GeoResultRedisGeoCommands.GeoLocationObject result : results) { // 解析结果构造Store对象 } return stores; } // 计算两点距离 public Distance distanceBetweenStores(Long storeId1, Long storeId2) { String key stores:geo; return redisTemplate.opsForGeo().distance(key, storeId1.toString(), storeId2.toString()); } }优点功能丰富支持半径、矩形搜索计算距离准确。缺点精度受GeoHash影响索引更新需要重建。适用场景外卖骑手派单、附近店铺推荐、打车匹配。八、Stream消息队列Redis 5.0引入的Stream是一种强大的消息队列数据结构支持消费组、消息确认、历史回溯等特性可替代部分场景下的MQ。它是轻量级MQ的替代方案。8.1 架构图8.2 代码示例使用RedissonComponent publicclass StreamService { Autowired private RedissonClient redissonClient; private RStreamString, String stream; PostConstruct public void init() { stream redissonClient.getStream(order-stream); // 创建消费组如果不存在 stream.createGroup(order-group, StreamMessageId.AUTO); } // 生产者 public void publish(String orderId) { MapString, String data new HashMap(); data.put(orderId, orderId); data.put(timestamp, String.valueOf(System.currentTimeMillis())); stream.add(data); } // 消费者批量拉取 Async public void consume() { while (true) { MapStreamMessageId, MapString, String messages stream.readGroup(order-group, consumer-1, 10); for (Map.EntryStreamMessageId, MapString, String entry : messages.entrySet()) { // 处理消息 process(entry.getValue()); // 确认消费 stream.ack(order-group, entry.getKey()); } // 无消息时短暂休眠 if (messages.isEmpty()) { try { Thread.sleep(1000); } catch (InterruptedException e) { break; } } } } }优点支持消息持久化、消费组、消息确认、消息回溯。缺点相比专业MQ功能较简单适合轻量级场景。适用场景任务队列、日志收集、消息通知。九、Lua脚本有些复杂操作需要多个Redis命令原子执行Lua脚本是最佳方案。9.1 典型场景扣减库存先检查库存再扣减防止超卖设置带条件的锁复杂数据聚合9.2 代码示例Component publicclass LuaScriptService { Autowired private RedisTemplateString, Object redisTemplate; // Lua脚本库存扣减 privatestaticfinal String DECREASE_STOCK_SCRIPT local key KEYS[1]\n local count tonumber(ARGV[1])\n local stock redis.call(get, key)\n if not stock or tonumber(stock) count then\n return 0\n else\n redis.call(decrby, key, count)\n return 1\n end; public boolean decreaseStock(String productId, int count) { DefaultRedisScriptLong script new DefaultRedisScript(DECREASE_STOCK_SCRIPT, Long.class); Long result redisTemplate.execute(script, Collections.singletonList(stock: productId), count); return result ! null result 1L; } // 带超时的分布式锁 privatestaticfinal String LOCK_SCRIPT if redis.call(setnx, KEYS[1], ARGV[1]) 1 then\n redis.call(expire, KEYS[1], ARGV[2])\n return 1\n else\n return 0\n end; public boolean lock(String key, String value, int expireSec) { DefaultRedisScriptLong script new DefaultRedisScript(LOCK_SCRIPT, Long.class); Long result redisTemplate.execute(script, Collections.singletonList(key), value, expireSec); return result ! null result 1L; } }优点原子性、网络传输少、性能高。缺点调试困难脚本需谨慎编写。适用场景库存扣减、限流、复杂条件更新。十、RedisJSONRedisJSON模块支持将JSON文档直接存储在Redis中并可对文档内的字段进行增删改查。10.1 安装RedisJSON需要作为模块加载Redis Stack已包含或单独编译安装。10.2 代码示例使用LettuceComponent publicclass RedisJsonService { Autowired private RedisTemplateString, Object redisTemplate; // 存储JSON对象 public void saveUser(Long userId, User user) { String key user: userId; redisTemplate.opsForValue().set(key, user); // 实际上RedisJSON需要专门命令这里演示思路真实使用时需使用Lettuce的JSON命令集 } // 更新字段 public void updateUserAge(Long userId, int age) { // 使用JSON.SET命令 // redisTemplate.execute(connection - connection.execute(JSON.SET, key, $.age, String.valueOf(age))); } }优点直接操作JSON内部字段支持索引和查询配合RediSearch。缺点需要额外安装模块内存占用较高。适用场景用户配置、会话信息、动态表单。总结以上10种高级用法涵盖了缓存之外Redis的诸多强大能力用法核心优势典型场景布隆过滤器内存极省防穿透缓存击穿防护、垃圾邮件Redisson分布式锁自动续期可重入分布式互斥延迟队列精准定时订单超时、延迟通知令牌桶限流平滑突发流量API限流位图极省内存签到、在线状态HyperLogLog固定内存UV统计GEO地理位置搜索附近的人Stream轻量MQ任务队列Lua脚本原子操作库存扣减RedisJSON文档存储用户配置在实际项目中合理组合这些高级特性可以让你用极小的成本实现高性能、高可用的分布式系统。需要注意的是Redis依然是内存数据库数据量大的场景要考虑内存成本必要时结合持久化方案。希望这篇文章能帮你打开Redis高级用法的大门。

更多文章