Redis五大核心数据结构实战解析:真实场景与Java代码实现

话外题:

在面试中必问的就是你在开发中用到过redis吗?这五种基本数据类型都是怎么用的具体场景是啥?等等这样的问题,下面就记录下五种基本数据类型的应用场景。

一、String类型:轻量级数据缓存

典型场景:短信验证码缓存

业务需求:用户登录时需要短信验证码验证,要求验证码5分钟内有效且每个手机号60秒内不能重复发送

方案设计

  • 存储结构:key=code:手机号,value=验证码

  • 设置双重过期时间:EX 300(5分钟),NX(不存在时设置)

  • 防重复发送:使用SETNX实现原子操作

Java实现

public class SmsService {
    private JedisPool jedisPool = new JedisPool("redis-server", 6379);

    public String sendCode(String phone) {
        try (Jedis jedis = jedisPool.getResource()) {
            // 防重复发送检查
            String lockKey = "lock:" + phone;
            if (jedis.set(lockKey, "1", "NX", "EX", 60) == null) {
                return "请求过于频繁";
            }

            // 生成验证码
            String code = String.valueOf((int)(Math.random() * 9000 + 1000));
            
            // 存储验证码(包含过期时间)
            String codeKey = "code:" + phone;
            jedis.setex(codeKey, 300, code);
            
            // 调用短信发送API(模拟)
            sendSms(phone, code);
            return "验证码已发送";
        }
    }

    public boolean verifyCode(String phone, String inputCode) {
        try (Jedis jedis = jedisPool.getResource()) {
            String codeKey = "code:" + phone;
            String realCode = jedis.get(codeKey);
            return inputCode != null && inputCode.equals(realCode);
        }
    }
}

二、Hash类型:对象属性存储

典型场景:电商购物车设计

业务需求

  1. 存储用户购物车中的商品信息

  2. 支持商品数量修改

  3. 需要完整获取购物车数据

方案优势

  • 天然适合存储对象属性

  • 支持字段级操作(hincrby)

  • 内存使用更高效(相比String序列化存储)

Java实现

public class CartService {
    private static final String CART_KEY_PREFIX = "cart:";
    
    public void addItem(String userId, String itemId, int quantity) {
        try (Jedis jedis = jedisPool.getResource()) {
            String key = CART_KEY_PREFIX + userId;
            jedis.hincrBy(key, itemId, quantity);
        }
    }

    public Map<String, String> getCart(String userId) {
        try (Jedis jedis = jedisPool.getResource()) {
            String key = CART_KEY_PREFIX + userId;
            return jedis.hgetAll(key);
        }
    }

    public void removeItem(String userId, String... itemIds) {
        try (Jedis jedis = jedisPool.getResource()) {
            String key = CART_KEY_PREFIX + userId;
            jedis.hdel(key, itemIds);
        }
    }
}

三、List类型:消息队列实现

典型场景:订单状态变更通知

业务需求:解耦订单系统与通知系统,实现异步处理

方案特点

  • 使用LPUSH/RPOP实现队列

  • BRPOP实现阻塞式消费

  • 支持多消费者负载均衡

Java消费者示例

public class OrderNotifyConsumer {
    private volatile boolean running = true;
    
    public void start() {
        new Thread(() -> {
            try (Jedis jedis = jedisPool.getResource()) {
                while (running) {
                    List<String> messages = jedis.brpop(30, "queue:order-notify");
                    if (messages != null) {
                        String orderId = messages.get(1);
                        processOrder(orderId);
                    }
                }
            }
        }).start();
    }

    private void processOrder(String orderId) {
        // 实现通知逻辑(邮件、短信等)
    }
}

四、Set类型:社交关系处理

典型场景:共同关注功能

业务需求:快速计算两个用户的共同关注列表

方案优势

  • SINTER命令直接计算集合交集

  • O(N)时间复杂度优于数据库JOIN操作

Java实现

public class RelationService {
    public Set<String> getCommonFollowers(String user1, String user2) {
        try (Jedis jedis = jedisPool.getResource()) {
            String key1 = "followers:" + user1;
            String key2 = "followers:" + user2;
            return jedis.sinter(key1, key2);
        }
    }

    public void followUser(String userId, String targetId) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.sadd("following:" + userId, targetId);
            jedis.sadd("followers:" + targetId, userId);
        }
    }
}

五、Sorted Set:实时排行榜

典型场景:直播间礼物周榜

业务需求

  1. 实时更新用户送礼总值

  2. 按周维度滚动排行

  3. 支持TopN查询和用户排名

Java实现

public class GiftRankService {
    private static final String WEEKLY_RANK_KEY = "rank:gift:week";
    
    public void addGift(String userId, double amount) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.zincrby(WEEKLY_RANK_KEY, amount, userId);
        }
    }

    public List<Map.Entry<String, Double>> getTop10() {
        try (Jedis jedis = jedisPool.getResource()) {
            Set<Tuple> tuples = jedis.zrevrangeWithScores(WEEKLY_RANK_KEY, 0, 9);
            return tuples.stream()
                    .map(t -> new AbstractMap.SimpleEntry<>(t.getElement(), t.getScore()))
                    .collect(Collectors.toList());
        }
    }

    public void resetWeeklyRank() {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.del(WEEKLY_RANK_KEY);
        }
    }
}

六、生产环境最佳实践

  1. 键名设计规范

    • 使用冒号分隔的命名空间(如user:1001:profile

    • 避免过长的键名(控制在100字节内)

  2. 连接池配置

    JedisPoolConfig config = new JedisPoolConfig();
    config.setMaxTotal(128);          // 最大连接数
    config.setMaxIdle(32);            // 最大空闲连接
    config.setMinIdle(8);             // 最小空闲连接
    config.setTestOnBorrow(true);     // 取连接时校验
    JedisPool pool = new JedisPool(config, "redis-host", 6379);

  3. 性能优化技巧

    • Pipeline批量操作提升吞吐量

    • 合理设置过期时间避免内存泄漏

    • 大Value拆分(Hash分桶存储)

七、常见问题解决方案

缓存穿透

// 空值缓存
public String getData(String key) {
    String value = jedis.get(key);
    if ("NULL".equals(value)) {
        return null; // 缓存空值
    }
    if (value == null) {
        value = db.query(key);
        jedis.setex(key, 300, value != null ? value : "NULL");
    }
    return value;
}

热Key处理

  • 本地缓存+Redis多级缓存

  • 使用Hash分片存储


作者建议:实际使用中建议结合Spring Data Redis模板类,并通过@Resource注入RedisTemplate对象。注意合理设置序列化方式(推荐StringRedisSerializer),生产环境务必配置合理的连接超时和重试策略。

延伸阅读

  • Redis持久化机制选择(RDB vs AOF)

  • 集群模式下的数据分片策略

  • Redis事务与Lua脚本原子性操作


注意事项

  1. 所有代码示例基于Jedis 3.7.0实现

  2. 生产环境建议配置SSL/TLS加密连接

  3. 高并发场景建议使用Lettuce客户端

  4. 注意Redis版本差异(部分命令需要Redis 5.0+)

建议将本文代码示例与实际Spring Boot项目结合,通过@Configuration配置RedisTemplate,可以更便捷地集成到企业级应用中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码里看花‌

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

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

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

打赏作者

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

抵扣说明:

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

余额充值