大家都知道,Redis是基于内存读写、单线程原子操作和丰富的数据结构,使其既能作为缓存层降低数据库压力,也能承担分布式锁、计数器、排行榜、消息队列、限流等需要高性能和原子性的业务场景,是互联网高并发系统中使用频率极高的内存数据库。Redis的使用场景到底有哪些了?公众原文
热点数据缓存 (String/Hash)
业务案例:商品详情、用户信息。
核心原理:Cache-Aside 模式(先查缓存,没有则查数据库并写入缓存,设置 TTL)。

// RedisConfig.java(通用配置)
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}

// Service
@Service
public class CacheService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private ProductMapper productMapper; // 数据库 Mapper

    public Product getProduct(Long id) {
        String key = "product:" + id;
        Product product = (Product) redisTemplate.opsForValue().get(key);
        if (product == null) {
            product = productMapper.selectById(id); // 从DB查
            if (product != null) {
                redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES); // Cache-Aside
            }
        }
        return product;
    }

    // Hash(用户信息)
    public void updateUserInfo(Long userId, String field, Object value) {
        String key = "user:" + userId;
        redisTemplate.opsForHash().put(key, field, value);
        redisTemplate.expire(key, 1, TimeUnit.HOURS);
    }
}

分布式会话 (String/Hash)
业务案例:单点登录 SSO、多实例 Session 共享。
核心原理:引入spring-session-data-redis 依赖后,Spring 会自动将 HttpSession 拦截并托管至 Redis。
引入依赖

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

添加配置
application.yml

spring:
  session:
    store-type: redis
    redis:
      namespace: spring:session
  redis:
    host: localhost
    port: 6379

无需额外代码,使用@EnableRedisHttpSession 即可实现多实例 Session 共享。
分布式锁 (String NX)
业务案例:秒杀防超卖、定时任务防重复执行。
核心原理:SETNX 加锁 + Lua 脚本原子解锁。生产环境推荐直接使用 Redisson。

@Service
public class LockService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public boolean tryLock(String lockKey, String requestId, long expireMs) {
        Boolean success = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, requestId, expireMs, TimeUnit.MILLISECONDS);
        return Boolean.TRUE.equals(success);
    }
    // Lua 脚本释放锁(原子操作)
    public boolean releaseLock(String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        return Boolean.TRUE.equals(redisTemplate.execute(
            new DefaultRedisScript<>(script, Boolean.class),
            Collections.singletonList(lockKey), requestId));
    }
}

计数器 (String)
业务案例:文章阅读量、点赞数、API 调用次数。
核心原理:INCR / INCRBY 原子自增。

public void incrementReadCount(Long articleId) {
    String key = "article:read:" + articleId;
    redisTemplate.opsForValue().increment(key); // INCR
    redisTemplate.expire(key, 7, TimeUnit.DAYS);
}

public Long getReadCount(Long articleId) {
    String key = "article:read:" + articleId;
    return redisTemplate.opsForValue().increment(key, 0); // 读取
}

排行榜 (Sorted Set)
业务案例:游戏积分榜、电商热销榜。
核心原理:ZADD 写入分数,ZREVRANGE 降序查询。

@Service
public class LeaderboardService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 上传分数 / 增加分数
    public void addScore(String userId, double score) {
        redisTemplate.opsForZSet().incrementScore("game:leaderboard", userId, score);
    }

    // 获取前10名 (分数从高到低)
    public Set<ZSetOperations.TypedTuple<String>> getTop10() {
        return redisTemplate.opsForZSet().reverseRangeWithScores("game:leaderboard", 0, 9);
    }
}

限流 (ZSet/String)
业务案例:API 频率限制(如:1分钟内最多请求100次)。
核心原理:滑动窗口限流(使用 ZSet 的 score 存时间戳,ZREMRANGEBYSCORE 移除窗口外的数据)。

@Component
public class RateLimiter {
    @Autowired
    private StringRedisTemplate redisTemplate;

    public boolean isAllowed(String userId, String action, int maxCount, int windowSizeSeconds) {
        String key = "limit:" + userId + ":" + action;
        long now = System.currentTimeMillis();
        long windowStart = now - (windowSizeSeconds * 1000);

        // 1. 移除窗口外的数据
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
        // 2. 获取当前窗口内的请求数
        Long count = redisTemplate.opsForZSet().zCard(key);
        if (count != null && count >= maxCount) {
            return false; // 被限流
        }
        // 3. 记录本次请求
        redisTemplate.opsForZSet().add(key, String.valueOf(now), now);
        return true;
    }
}

消息队列 (List/Stream)
业务案例:异步任务、日志收集。
核心原理:LPUSH + BRPOP 阻塞消费(List 简易模式),或使用具有持久化和 Ack 机制的 Stream。

@Service
public class QueueProducer {
    @Autowired
    private StringRedisTemplate redisTemplate;

    public void sendTask(String taskJson) {
        redisTemplate.opsForList().leftPush("task:queue", taskJson);
    }
}

// 消费者线程 (简易消费示例)
@Component
class QueueConsumer implements CommandLineRunner {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void run(String... args) {
        new Thread(() -> {
            while (true) {
                // BRPOP 阻塞读取,超时时间 10 秒
                String task = redisTemplate.opsForList().rightPop("task:queue", 10, TimeUnit.SECONDS);
                if (task != null) {
                    System.out.println("处理异步任务: " + task);
                }
            }
        }).start();
    }
}

发布订阅 (Pub/Sub)
业务案例:实时弹幕推送、聊天室广播。
核心原理:PUBLISH / SUBSCRIBE 消息广播。

@Configuration
public class RedisPubSubConfig {
    // 注册消息监听器容器
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                            MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic("chat:room"));
        return container;
    }

    @Bean
    MessageListenerAdapter listenerAdapter(Receiver receiver) {
        return new MessageListenerAdapter(receiver, "receiveMessage");
    }
}

@Component
class Receiver {
    public void receiveMessage(String message) {
        System.out.println("收到弹幕/广播: " + message);
    }
}

最新列表或时间线 (List)
业务案例:最新评论、朋友圈动态。
核心原理:LPUSH + LTRIM 裁剪,保持列表固定长度(如只保留最新100条)。

@Service
public class TimelineService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    public void addComment(Long articleId, String commentJson) {
        String key = "article:comments:" + articleId;
        // 1. 推入新评论
        redisTemplate.opsForList().leftPush(key, commentJson);
        // 2. 裁剪列表,只保留前 100 条
        redisTemplate.opsForList().trim(key, 0, 99);
    }
}

购物车 (Hash)
业务案例:电商购物车商品及数量增删改查。
核心原理:以用户ID为 Key,商品ID为 Field,数量为 Value。

@Service
public class CartService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 添加商品到购物车
    public void addCart(Long userId, Long productId, int quantity) {
        String key = "cart:" + userId;
        redisTemplate.opsForHash().increment(key, String.valueOf(productId), quantity);
    }

    // 获取购物车所有商品及数量
    public Map<Object, Object> getCart(Long userId) {
        String key = "cart:" + userId;
        return redisTemplate.opsForHash().entries(key);
    }
}

签到或打卡 (Bitmap)
业务案例:用户每日签到、连续签到统计。
核心原理:按位(Bit)存储,用偏移量(Offset)表示天数,1为签到,0为未签到。

@Service
public class SignService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 签到:按年份和月份划分 key,offset 传当天日期减 1 (如 5号 对应 offset=4)
    public void doSign(Long userId, int dayOfMonth) {
        String key = "sign:" + userId + ":202607";
        redisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    }

    // 统计本月签到次数
    public Long getSignCount(Long userId) {
        String key = "sign:" + userId + ":202607";
        return redisTemplate.execute((RedisCallback<Long>) connection -> 
            connection.bitCount(key.getBytes()));
    }
}

地理位置 LBS (Geo)
业务案例:附近的人、附近的店铺。
核心原理:GEOADD 写入经纬度,GEORADIUS / GEOSEARCH 范围检索。

@Service
public class ShopGeoService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 添加店铺位置
    public void addShopLocation(String shopId, double lng, double lat) {
        redisTemplate.opsForGeo().add("shops:location", new Point(lng, lat), shopId);
    }

    // 查询方圆 5 公里内的店铺
    public GeoResults<RedisGeoCommands.GeoLocation<String>> getNearShops(double lng, double lat) {
        Circle circle = new Circle(new Point(lng, lat), new Metric() {
            public double getMultiplier() { return 0.001; } // 米转公里计算
            public String getUnit() { return "km"; }
        });
        return redisTemplate.opsForGeo().search("shops:location", circle);
    }
}

布隆过滤器 (Bloom Filter)
业务案例:缓存穿透防护。
核心原理:由于底层需要 BF.ADD 等扩展模块命令,默认 RedisTemplate 不直接支持,通常通过 Redisson 或执行 Lettuce原生/Lua 驱动。

// 使用 Redisson 实现最为标准和方便
@Service
public class BloomFilterService {
    @Autowired
    private RedissonClient redissonClient;

    public void initBloom() {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("user:exists:filter");
        // 初始化布隆过滤器:预期插入100万数据,误差率 3%
        bloomFilter.tryInit(1000000L, 0.03);
        bloomFilter.add("user123");
    }

    public boolean checkIfContains(String userId) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("user:exists:filter");
        return bloomFilter.contains(userId); // 高效判断是否存在,若返回 false 则绝对不存在
    }
}

全局唯一 ID (String)
业务案例:分布式订单号生成。
核心原理:时间戳 + INCRBY 批量取号段,减少网络 IO。

@Component
public class OrderIdGenerator {
    @Autowired
    private StringRedisTemplate redisTemplate;

    public long generateOrderId() {
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String key = "order:id:" + dateStr;
        // 每次自增 1,结合日期组合成唯一 ID
        Long increment = redisTemplate.opsForValue().increment(key);
        return Long.parseLong(dateStr + String.format("%06d", increment));
    }
}

社交关系 (Set)
业务案例:共同好友、关注与粉丝。
核心原理:SADD 建立集合,SINTER 求交集(共同好友),SDIFF 求差集(可能认识的人)。

@Service
public class SocialService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 关注某人
    public void follow(Long userId, Long followUserId) {
        redisTemplate.opsForSet().add("following:" + userId, String.valueOf(followUserId));
        redisTemplate.opsForSet().add("followers:" + followUserId, String.valueOf(userId));
    }

    // 求共同关注 (交集)
    public Set<String> getCommonFollowing(Long userA, Long userB) {
        return redisTemplate.opsForSet().intersect("following:" + userA, "following:" + userB);
    }
}

延迟任务 (Sorted Set)
业务案例:订单超时取消。
核心原理:将任务执行的绝对时间戳作为 Score 存入 ZSet,定时任务拉取已过期的 Score 进行消费。

@Service
public class DelayTaskService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 提交延迟任务
    public void addDelayTask(String orderId, long delaySeconds) {
        long executeTime = System.currentTimeMillis() + (delaySeconds * 1000);
        redisTemplate.opsForZSet().add("order:delay:queue", orderId, executeTime);
    }

    // 定时轮询拉取过期任务 (一般用 Scheduled 触发)
    @Scheduled(fixedRate = 1000)
    public void consumeDelayTask() {
        long now = System.currentTimeMillis();
        // 获取所有分数小于等于当前时间戳的任务
        Set<String> orderIds = redisTemplate.opsForZSet().rangeByScore("order:delay:queue", 0, now);
        if (orderIds != null && !orderIds.isEmpty()) {
            for (String orderId : orderIds) {
                // 用 remove 保证分布式集群下只有一个节点抢到该任务
                Long removed = redisTemplate.opsForZSet().remove("order:delay:queue", orderId);
                if (removed != null && removed > 0) {
                    System.out.println("执行取消订单逻辑: " + orderId);
                }
            }
        }
    }
}

用户画像或UV 统计 (HyperLogLog)
业务案例:网页 UV 基数去重统计。
核心原理:PFADD 注入用户,PFCOUNT 模糊去重计数。标准误差 0.81%,极省内存。

@Service
public class UvService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 记录访问用户
    public void recordVisit(String pageName, String userId) {
        String key = "uv:" + pageName;
        redisTemplate.opsForHyperLogLog().add(key, userId);
    }

    // 获取 UV 统计值
    public Long getUvCount(String pageName) {
        String key = "uv:" + pageName;
        return redisTemplate.opsForHyperLogLog().size(key);
    }
}

向量检索 (Vector - Redis Stack)
业务案例:AI 语义搜索、RAG 知识库召回。
核心原理:通过 Redis Stack 提供的 FT.SEARCH 命令执行 KNN 相似度检索。

// Spring Data Redis 官方需要借助 Jedis/Lettuce 原生或 RedisModulesCommands。
// 以下为基于 Jedis/Lettuce 原始客户端连接执行自定义指令的通用伪代码架构:
@Service
public class VectorSearchService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    public List<String> searchSimilarDocs(float[] queryVector, int topK) {
        // 将 float[] 转换为 byte[] 字节流
        byte[] vectorBytes = convertToBytes(queryVector);

        // 构建 Redis 向量检索的原始命令 (FT.SEARCH index "(*)=>[KNN 5 @vector $vec] -> {$yield_distance_as: dist}" PARAMS 2 vec vectorBytes DIALECT 2)
        String indexName = "doc_index";
        String query = "(*)=>[KNN " + topK + " @doc_vector $vec]";

        // 执行 Redis 原始命令进行 K 邻近 (KNN) 匹配并返回文档 ID
        List<Object> results = redisTemplate.execute((RedisCallback<List<Object>>) connection -> {
            // 通过底层原生客户端(如 RedisModules / FT.SEARCH 命令)进行复杂查询
            // 返回召回的 TopK 条相似文本/实体 ID
            return null; 
        });
        return new ArrayList<>(); 
    }

    private byte[] convertToBytes(float[] input) {
        ByteBuffer buffer = ByteBuffer.allocate(input.length * 4).order(ByteOrder.LITTLE_ENDIAN);
        for (float v : input) buffer.putFloat(v);
        return buffer.array();
    }
}

Redis 数据存于内存,单位成本远高于磁盘,不适合存储大规模冷数据;海量数据场景需控制 key 数量与 value 体积,避免大 key 阻塞主线程。
在这里插入图片描述

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐