建站案例

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

发表日期:2026-04-13

在很多小伙伴的印象中,Redis就是一个用来做缓存的工具。

但如果你只把它当作缓存,那真是“杀鸡用牛刀”了。

Redis的强大远不止于此,它的高级特性可以让你的系统在性能、扩展性、可靠性上直接起飞。

今天这篇文章就跟大家聊聊Redis中10种高级用法,希望对你会有所帮助。

更多项目实战在项目实战网:java突击队

一、布隆过滤器

有些小伙伴在做高并发系统时,最怕的就是缓存穿透——大量请求直接打穿缓存,压垮数据库。

布隆过滤器就是解决这个问题的利器。

1.1 什么是布隆过滤器

布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是否存在于一个集合中。

它的特点是:

  • 极省内存:存储1亿个元素,只需要约100MB内存

  • 存在误判:判断“不在”一定准确;判断“在”可能误判(小概率)

  • 不可删除:不支持删除元素

1.2 原理

布隆过滤器的原理如图:

image.png

查询数据的流程如下图:

image.png

1.3 代码示例(使用Redisson)

typescript体验AI代码助手代码解读复制代码@Component publicclass BloomFilterService {       @Autowired     private RedissonClient redissonClient;       private RBloomFilter 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脚本保证原子性,并内置了看门狗机制自动续期。

image.png

2.3 代码示例

scss体验AI代码助手代码解读复制代码@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 原理图

image.png

3.2 代码示例

typescript体验AI代码助手代码解读复制代码@Component public class DelayQueueService {       @Autowired     private RedissonClient redissonClient;       private RBlockingQueue blockingQueue;     private RDelayedQueue 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 令牌桶原理

image.png

4.2 Lua脚本实现

swift体验AI代码助手代码解读复制代码@Component publicclass RateLimiterService {       @Autowired     private RedisTemplate 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) {         DefaultRedisScript 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个bit

5.2 代码示例

typescript体验AI代码助手代码解读复制代码@Component publicclass BitmapService {       @Autowired     private RedisTemplate 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(             (RedisCallback) 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 代码示例

typescript体验AI代码助手代码解读复制代码@Component publicclass UVService {       @Autowired     private RedisTemplate 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(List 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 代码示例

typescript体验AI代码助手代码解读复制代码@Component publicclass GeoService {       @Autowired     private RedisTemplate 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 List findNearbyStores(double lng, double lat, double radiusKm) {         String key = "stores:geo";         Circle circle = new Circle(new Point(lng, lat), new Distance(radiusKm, Metrics.KILOMETERS));         GeoResults<RedisGeoCommands.GeoLocation> results =              redisTemplate.opsForGeo().radius(key, circle);           List stores = new ArrayList<>();         for (GeoResult<RedisGeoCommands.GeoLocation> 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 架构图

image.png

8.2 代码示例(使用Redisson)

typescript体验AI代码助手代码解读复制代码@Component publicclass StreamService {       @Autowired     private RedissonClient redissonClient;       private RStream stream;       @PostConstruct     public void init() {         stream = redissonClient.getStream("order-stream");         // 创建消费组(如果不存在)         stream.createGroup("order-group", StreamMessageId.AUTO);     }       // 生产者     public void publish(String orderId) {         Map data = new HashMap<>();         data.put("orderId", orderId);         data.put("timestamp", String.valueOf(System.currentTimeMillis()));         stream.add(data);     }       // 消费者(批量拉取)     @Async     public void consume() {         while (true) {             Map<StreamMessageId, Map> messages =                  stream.readGroup("order-group", "consumer-1", 10);             for (Map.Entry<StreamMessageId, Map> 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 代码示例

typescript体验AI代码助手代码解读复制代码@Component publicclass LuaScriptService {       @Autowired     private RedisTemplate 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) {         DefaultRedisScript 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) {         DefaultRedisScript script = new DefaultRedisScript<>(LOCK_SCRIPT, Long.class);         Long result = redisTemplate.execute(script, Collections.singletonList(key), value, expireSec);         return result != null && result == 1L;     } }


优点:原子性、网络传输少、性能高。

缺点:调试困难,脚本需谨慎编写。

适用场景:库存扣减、限流、复杂条件更新。

十、RedisJSON

RedisJSON模块支持将JSON文档直接存储在Redis中,并可对文档内的字段进行增删改查。

10.1 安装

RedisJSON需要作为模块加载(Redis Stack已包含),或单独编译安装。

10.2 代码示例(使用Lettuce)

typescript体验AI代码助手代码解读复制代码@Component publicclass RedisJsonService {       @Autowired     private RedisTemplate 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)。

缺点:需要额外安装模块,内存占用较高。

适用场景:用户配置、会话信息、动态表单。

更多项目实战在项目实战网:java突击队

总结

以上10种高级用法,涵盖了缓存之外Redis的诸多强大能力:

用法核心优势典型场景
布隆过滤器内存极省,防穿透缓存击穿防护、垃圾邮件
Redisson分布式锁自动续期,可重入分布式互斥
延迟队列精准定时订单超时、延迟通知
令牌桶限流平滑突发流量API限流
位图极省内存签到、在线状态
HyperLogLog固定内存UV统计
GEO地理位置搜索附近的人
Stream轻量MQ任务队列
Lua脚本原子操作库存扣减
RedisJSON文档存储用户配置

在实际项目中,合理组合这些高级特性,可以让你用极小的成本实现高性能、高可用的分布式系统。

需要注意的是,Redis依然是内存数据库,数据量大的场景要考虑内存成本,必要时结合持久化方案。

希望这篇文章能帮你打开Redis高级用法的大门。


文章来源网络收集