用Redis五大核心数据类型解决真实开发痛点:从电商秒杀到社交推荐,代码级实战!

摘要:Redis不仅是缓存,更是高并发场景下的瑞士军刀!本文揭秘String、List、Hash、Set、Sorted Set五大类型在真实项目中的高阶玩法,结合Java代码手把手教你解决缓存穿透、实时排行榜、分布式锁等10大棘手问题。

一、String:不只是键值存储的十八般武艺

场景1:分布式锁(解决集群环境定时任务重复执行)

// 基于SETNX命令实现分布式锁
public boolean tryLock(String lockKey, String requestId, int expireTime) {
    Jedis jedis = jedisPool.getResource();
    try {
        String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
        return "OK".equals(result);
    } finally {
        jedis.close();
    }
}

// 使用示例:定时任务调度
if(tryLock("order_task_lock", "server1", 30)) {
    // 执行订单超时取消逻辑
}

场景2:缓存穿透防护(空值缓存策略)

public String getProductInfo(String productId) {
    String cacheKey = "product:" + productId;
    String value = jedis.get(cacheKey);
    
    if(value == null) {
        // 查数据库
        Product product = productDao.getById(productId);
        if(product != null) {
            jedis.setex(cacheKey, 3600, product.toJson());
        } else {
            // 缓存空对象,设置较短过期时间
            jedis.setex(cacheKey, 300, "NULL");
        }
        return product != null ? product.toJson() : null;
    }
    
    return "NULL".equals(value) ? null : value;
}
二、Hash:对象存储的艺术与性能优化

场景:电商购物车实现(原子操作优化性能)

// 添加商品到购物车
public void addToCart(String userId, String itemId, int count) {
    String cartKey = "cart:" + userId;
    jedis.hincrBy(cartKey, itemId, count);
    
    // 设置购物车整体过期时间
    jedis.expire(cartKey, 7 * 24 * 3600);
}

// 获取完整购物车
public Map<String, String> getCart(String userId) {
    return jedis.hgetAll("cart:" + userId);
}

// 技巧:采用HSCAN分页获取大Hash,避免阻塞
public List<Map.Entry<String, String>> scanBigHash(String key, int batchSize) {
    ScanParams params = new ScanParams().count(batchSize);
    String cursor = ScanParams.SCAN_POINTER_START;
    List<Map.Entry<String, String>> result = new ArrayList<>();
    
    do {
        ScanResult<Map.Entry<String, String>> scanResult = jedis.hscan(key, cursor, params);
        result.addAll(scanResult.getResult());
        cursor = scanResult.getCursor();
    } while (!cursor.equals("0"));
    
    return result;
}
三、List:消息队列的轻量级解决方案

场景:抢购活动的请求排队(削峰填谷)

// 秒杀请求入队
public void enqueueSeckillRequest(String itemId, String userId) {
    String queueKey = "seckill_queue:" + itemId;
    jedis.lpush(queueKey, userId);
}

// 异步处理队列
new Thread(() -> {
    while(true) {
        List<String> users = jedis.brpop(60, queueKey);
        // 处理秒杀业务逻辑
        processSeckill(itemId, users.get(1));
    }
}).start();

// 实现优先队列(VIP用户优先处理)
jedis.lpush(queueKey, "VIP:user123");  // VIP用户添加特殊前缀
四、Set:社交关系与实时推荐的秘密武器

场景:共同关注计算(百万级数据毫秒响应)

// 获取共同关注列表
public Set<String> getMutualFollows(String user1, String user2) {
    String key1 = "follows:" + user1;
    String key2 = "follows:" + user2;
    return jedis.sinter(key1, key2);
}

// 实时推荐可能认识的人(差集运算)
public Set<String> recommendUsers(String userId) {
    String myFollows = "follows:" + userId;
    return jedis.sdiff("all_users", myFollows, "blocked:" + userId);
}

// 使用SPOP实现随机抽奖
public String luckyDraw(String activityId) {
    return jedis.spop("activity:" + activityId);
}
五、Sorted Set:排行榜与时间窗口统计的终极方案

场景1:实时游戏排行榜(ZINCRBY原子操作)

// 更新玩家分数
public void updateScore(String playerId, double score) {
    jedis.zincrby("game_leaderboard", score, playerId);
}

// 获取TOP10
public Set<Tuple> getTop10() {
    return jedis.zrevrangeWithScores("game_leaderboard", 0, 9);
}

// 带时间维度的排行榜(使用时间戳作为score)
public void addTimedScore(String playerId, double score) {
    double timestampScore = score + (1.0 / (System.currentTimeMillis()/1000));
    jedis.zadd("timed_leaderboard", timestampScore, playerId);
}

场景2:延迟队列实现(时间戳作为score)

// 添加延迟任务
public void addDelayTask(String taskId, long delaySeconds) {
    double score = System.currentTimeMillis() + delaySeconds * 1000;
    jedis.zadd("delay_queue", score, taskId);
}

// 扫描可执行任务
public List<String> pollDelayTasks() {
    long now = System.currentTimeMillis();
    Set<String> tasks = jedis.zrangeByScore("delay_queue", 0, now);
    jedis.zremrangeByScore("delay_queue", 0, now);
    return new ArrayList<>(tasks);
}
六、类型选型黄金法则(决策树)
  1. 需要原子计数器? → String

  2. 处理对象属性? → Hash

  3. 维护有序集合? → Sorted Set

  4. 去重需求? → Set

  5. 消息队列/最新列表? → List

结语:Redis数据类型的选择直接影响系统性能和代码复杂度。通过本文的实战案例可以看出,合理利用数据结构特性,可以在不增加复杂中间件的情况下,优雅解决诸多分布式系统难题。记住:没有最好的数据结构,只有最合适的场景选择!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码里看花‌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值