Redis 除了做缓存还能干什么?

作为一个在 Java 堆里摸爬滚打了八年的老油条,我对 Redis 的感情早就不是 "缓存工具" 这么简单了。这玩意儿就像我工位上的瑞士军刀 ------ 看起来是个开快递的,实际上能修电脑、撬瓶盖、甚至当镜子照(夸张了啊)。今天咱就掰开揉碎了聊聊,这些年我用 Redis 搞过的那些 "不务正业" 的操作,全是踩过坑的实战经验,代码示例都是能直接抄的那种。

一、分布式锁:高并发场景下的排队神器

当年踩过的坑

记得刚做电商秒杀那会,多个服务器跟打了鸡血似的抢库存,数据库行锁被压得嗷嗷叫,自己写的锁又总担心 "死锁"------ 就像一群人挤地铁,没个排队规则全乱套。直到用了 Redis 的分布式锁,才算是给线程们套上了 "秩序绳"。

推荐玩法:Redisson 真香警告

scss 复制代码
// 引入依赖时记得选最新版,别问我怎么知道的(踩过旧版bug的痛)
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.20.0</version>
</dependency>

// 初始化就像开罐头,拧开盖子就能用
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);

// 加锁逻辑:就像去银行取号,先等叫号再办事
RLock lock = redisson.getLock("stock:1001");
try {
    // 最多等30秒,锁自动续期30秒(比排队买奶茶还省心)
    boolean locked = lock.tryLock(30, 30, TimeUnit.SECONDS);
    if (locked) {
        // 扣库存这事儿,必须拿到锁才能干
        updateStockInDB();
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt(); // 被打断了就乖乖退出
} finally {
    // 自己的锁自己解,别误伤别人的
    if (lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

避坑指南(划重点!)

  • 新手别用setnx + expire分段写!当年我就因为网络卡了一下,锁没设置过期时间,差点让系统 "卡死" 一晚上(老板至今不知道这事)。
  • 集群模式直接上 RedLock,主节点挂了也不怕,就像老大罢工了马上有替补顶上。

二、计数器:高并发场景下的性能救火员

真实案例:直播在线人数的血与泪

做直播项目时,用数据库UPDATE统计在线人数,QPS 刚到 200 就卡死了。换成 Redis 的INCR后,直接扛住 2 万并发 ------ 这感觉就像把小电驴换成了跑车,油门随便踩。

接口限流代码(直接抄作业)

ini 复制代码
// 初始化Jedis就像开冰箱门,啪嗒一下就打开了
Jedis jedis = new Jedis("redis://127.0.0.1:6379");
String key = "rate_limit:user:" + userId + ":minute";
long count = jedis.incr(key); // 每来一次请求,计数器+1
if (count == 1) {
    jedis.expire(key, 60); // 第一次来才设过期时间,省点力气
}
if (count > 100) { // 一分钟最多100次,再挤就踢出去
    throw new RuntimeException("你点太快了,喝口茶歇会吧!");
}

进阶操作(老码农私藏)

  • 生成订单号:用INCRBY order_id 1比数据库自增快 10 倍,再也不怕订单号重复。
  • 库存预热:秒杀前用SET stock:1001 1000 NX直接把库存塞进 Redis,比现查数据库快到飞起。

三、排行榜:ZSET 的凡尔赛时刻

当年的神优化:从 3 小时到 5 分钟的逆袭

做 "年度博主" 排名时,数据库跑批要 3 小时,换成 ZSET 后,凌晨 3 点跑批 5 分钟搞定 ------ 就像把手动算盘换成了计算器,效率起飞。

点赞排行榜代码(Spring Data Redis 版)

javascript 复制代码
// 点赞时直接给用户加分,就像老师给学生打勾
redisTemplate.opsForZSet().incrementScore("blog:like:rank", "user:1001", 1);

// 取Top10就像查考试排名,倒着查前10名
Set<ZSetOperations.TypedTuple<String>> top10 = redisTemplate.opsForZSet()
    .reverseRangeWithScores("blog:like:rank", 0, 9);

// 查用户排名就像查自己考了多少名,reverseRank就是倒着数
Long rank = redisTemplate.opsForZSet().reverseRank("blog:like:rank", "user:1001");

优化技巧(划重点)

  • 热榜(前 100 名)用 ZSET 实时算,冷榜(100 名之后)每天凌晨归档到数据库,别让 Redis 太累。
  • 近期点赞加时间权重,比如 "昨天点赞算 10 分,上个月点赞算 1 分",让排行榜更 "新鲜"。

四、消息队列:轻量级的异步快递员

微服务里的快递站:订单异步处理

做订单系统时,用 Redis 的 List 当队列,生产者把订单扔进去,消费者慢慢处理,就像快递站先收快递再分拣,再也不怕高峰期爆仓。

两种模式对比(人话版)

模式 像什么 优点 缺点 适合场景
队列模式 快递仓库 快递丢不了(持久化) 得有人一直盯着仓库 订单处理、日志收集
发布订阅 班级群通知 消息秒发 没看消息就错过 实时通知、聊天

可靠消费代码(带重试的那种)

javascript 复制代码
// 生产者:订单一创建就扔到队列里,像扔快递到仓库
jedis.lpush("order_queue", JSON.toJSONString(order));

// 消费者:蹲在仓库门口等快递,没快递就等着(阻塞读取)
while (true) {
    List<String> message = jedis.brpop(0, "order_queue"); // 0表示一直等
    if (message != null && message.size() > 1) {
        String orderJson = message.get(1);
        try {
            processOrder(orderJson); // 处理订单,就像拆快递
        } catch (Exception e) {
            // 处理失败就放回重试队列,再给一次机会
            jedis.lpush("order_retry_queue", orderJson);
        }
    }
}

五、会话共享:分布式系统的登录管家

当年的崩溃时刻:用户登录后突然 "失忆"

做分布式电商时,用户在 A 服务器登录,请求转到 B 服务器就登出了 ------ 就像刚存完包,换个柜子取包就没了。用 Redis 存 Session 后,不管去哪台服务器都能 "记住" 用户。

Spring Session 集成(一行代码搞定)

typescript 复制代码
// 加个依赖就像装了个插件,自动把Session存到Redis
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

// 配置类就像设置保险箱,指定Redis地址就行
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) // 30分钟过期
public class SessionConfig {
    @Bean
    public LettuceConnectionFactory lettuceConnectionFactory() {
        return new LettuceConnectionFactory(new RedisStandaloneConfiguration("127.0.0.1", 6379));
    }
}

安全小技巧

  • SessionID 加随机盐值,就像给保险箱加密码,防止被人猜中。
  • 每次登录生成新 SessionID,就像每次存包都换个柜子,防君子也防小人。

六、地理位置:"附近的人" 背后的摸鱼神器

O2O 项目的救命稻草:附近的门店查询

用 Redis 的 GEO 查附近 5 公里的门店,比数据库算距离快 5 倍 ------ 就像用导航 APP 搜附近餐厅,秒出结果。

代码示例(Lettuce 客户端版)

less 复制代码
// 存门店坐标就像在地图上钉图钉
redisTemplate.geoOps().add("shop:locations", 
    new Point(116.4074, 39.9042), "shop:1001");

// 查附近5公里的门店,就像用放大镜找图钉
GeoResults<RedisGeoCommands.GeoLocation<String>> results = 
    redisTemplate.geoOps().radius("shop:locations", 
        new Circle(new Point(116.4074, 39.9042), Metrics.KILOMETERS.toMeters(5)),
        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().sortAscending());

// 解析结果就像看地图标注
results.getContent().forEach(geoResult -> {
    String shopId = geoResult.getContent().getName();
    double distance = geoResult.getDistance().getValue();
    System.out.println(shopId + "离你还有" + distance + "米,赶紧去!");
});

注意事项(别踩坑)

  • 存的地点别超过 10 万个,不然 Redis 查起来像在图书馆找书,慢得要命。
  • 经纬度小数点后留 6 位就行,精确到 1 米够用了,别存一堆没用的零。

七、其他野路子玩法(老码农私货)

1. 分布式限速:滑动窗口防刷

用 ZSET 记请求时间,每分钟最多 50 次,就像地铁限流,超了就不让进:

ini 复制代码
String key = "rate_limit:user:" + userId;
long now = System.currentTimeMillis();
// 删掉一分钟前的记录,就像清理过期垃圾
redisTemplate.opsForZSet().removeRangeByScore(key, 0, now - 60000);
// 记当前时间,像盖个时间戳
redisTemplate.opsForZSet().add(key, now, now); 
// 数人数,超了就踢人
long count = redisTemplate.opsForZSet().zCard(key); 
if (count > 50) throw new Exception("你刷得太猛了,歇会吧!");

2. 分布式事务:秒杀防超卖

WATCH + MULTI + EXEC就像买东西先锁起来,别人改不了:

ini 复制代码
jedis.watch("stock:1001"); // 盯着库存,别让别人偷偷改
String stock = jedis.get("stock:1001");
if (Integer.parseInt(stock) > 0) {
    jedis.multi(); // 开始办正事
    jedis.decr("stock:1001"); // 库存-1
    List<Object> results = jedis.exec(); // 提交,要是库存被改了就返回null
    if (results == null) {
        // 重试,就像没买到票再排队一次
    }
}

3. 实时在线用户:SET 集合去重神器

用 SET 存用户 ID,上线加进去,下线删出来,比数据库DISTINCT快 10 倍 ------ 就像班级点名,来了的打勾,走了的擦掉。

八、老码农的肺腑之言

  1. 别死磕工具,先想数据结构:Redis 的 List、Set、ZSET 就像不同的工具钳,用对了能省 80% 的力气。

  2. 过期时间是个宝:用完就删,别让 Redis 变成垃圾场,尤其是计数器和排行榜,不然内存爆了够你加班的。

  3. 集群分片要当心热点 :比如 "秒杀商品" 的 key 可能全挤在一个分片,加个随机后缀(如user:1001:seckill:goods)分散一下。

  4. 监控比写代码重要 :用 Prometheus 盯着redis_hit_rate,内存碎片率超过 1.5 就该优化了,别等系统挂了才知道。

说白了就是:Redis牛逼,但得会用。别光拿它存缓存,多想想还能干啥骚操作,指不定就帮你把难题给解决了!

相关推荐
程序员爱钓鱼21 分钟前
Go语言泛型-泛型约束与实践
前端·后端·go
寻月隐君21 分钟前
保姆级教程:Zsh + Oh My Zsh 终极配置,让你的 Ubuntu 终端效率倍增
linux·后端·命令行
程序员爱钓鱼24 分钟前
Go语言泛型-泛型对代码结构的优化
后端·google·go
这里有鱼汤29 分钟前
“对象”?对象你个头!——Python世界观彻底崩塌的一天
后端·python
RainbowSea31 分钟前
跨域问题(Allow CORS)解决(3 种方法)
java·spring boot·后端
sniper_fandc2 小时前
SpringBoot系列—入门
java·spring boot·后端
Piper蛋窝9 小时前
深入 Go 语言垃圾回收:从原理到内建类型 Slice、Map 的陷阱以及为何需要 strings.Builder
后端·go
六毛的毛11 小时前
Springboot开发常见注解一览
java·spring boot·后端
AntBlack11 小时前
拖了五个月 ,不当韭菜体验版算是正式发布了
前端·后端·python
315356691311 小时前
一个简单的脚本,让pdf开启夜间模式
前端·后端