Redis 客户端工具体系

文章主旨

这一组知识点,讲的其实都是:Java 程序怎么去操作 Redis

它们都服务于同一个目标:让应用更方便、更高效、更安全地使用 Redis,只是各自擅长场景不一样。

所以,这不是简单地记住 4 个名词,而是要理解:它们共同组成了 Java 操作 Redis 的客户端工具体系。

你可以把它们想成一套"分层工具链":

第一层:底层连接与通信( **Lettuce**

它负责和 Redis 服务器真正通信,偏底层,偏性能,偏生产环境能力。

第二层:Spring 风格的基础操作封装( **RedisTemplate**/ **StringRedisTemplate**

它们让你在 Spring 项目里更方便地做常规 Redis 操作。

第三层:高级能力封装( **Redisson**

它不只是操作 Redis 数据,而是把 Redis 封装成了很多可直接使用的分布式组件。


分点讲解客户端

RedisTemplate

  1. RedisTemplate 是什么?

:::color3
RedisTemplate 是 Spring Data Redis 提供的一个 通用型 Redis 操作模板

它的核心作用,就是把对 Redis 的各种操作,包装成 Java 里比较统一、比较好调用的 API。

:::

  1. RedisTemplate 有什么作用?

:::color3
不仅仅是封装了操作命令,还管理连接数

为 SpringBoot操作Redis不仅仅是要封装各种不同的命令,还要管理连接,以及数据序列化等等。

于是 Spring 提供了 RedisTemplate,把这些常见动作封装起来,让你在业务代码里可以更自然地写。

:::

  1. RedisTemplate 的优点

:::color3

  • **通用性强:**它不是只支持字符串,而是支持 Redis 多种数据结构。
  • **Spring 兼容性好:**在 Spring Boot 项目里配置后就能直接注入使用。
  • **适合操作 Java 对象:**如果你要往 Redis 存一个对象,RedisTemplate 比较自然。

:::

  1. RedisTemplate 的缺点

:::color3
序列化配置比较麻烦。

因为 Redis 本质上存的是二进制或字符串数据,而 Java 对象要存进去,就得先做序列化。

如果 RedisTemplate 的 key、value、hashKey、hashValue 序列化器没有配置好,就容易出现这些问题:

  • Redis 里存出来的 key 像乱码
  • value 可读性差
  • 不同服务之间序列化不兼容
  • 反序列化报错

所以很多人说它"好用,但配置烦",原因就在这里。

:::

  1. 对比其他客户端

:::color3

  • **<font style="color:#117CEE;">RedisTemplate</font>**vs **<font style="color:#117CEE;">StringRedisTemplate</font>**
    StringRedisTemplate 本质上可以看作是 RedisTemplate 的字符串特化版本
  • **<font style="color:#117CEE;">RedisTemplate</font>**** **vs **<font style="color:#117CEE;">Lettuce</font>**
    RedisTemplate 是上层模板,底层往往还是依赖 Lettuce 去跟 Redis 通信。
  • **<font style="color:#117CEE;">RedisTemplate</font>** vs **<font style="color:#117CEE;">Redisson</font>**
    RedisTemplate 适合做基础数据操作;Redisson 更擅长高级分布式功能。

:::


StringRedisTemplate

  1. StringRedisTemplate 是什么?

:::color3
StringRedisTemplate 可以理解为:**RedisTemplate<String, String>**** 的专用版本。**

也就是说,key和value 默认就是拿字符串。

所以它特别适合那种"Redis 中存的就是纯字符串内容"的场景。

:::

  1. StringRedisTemplate 避免了复杂的序列化配置

:::color3

你可以想一下,实际开发中,有大量 Redis 使用场景其实都是字符串:

  • 验证码:phone:code -> 123456
  • token:token:xxx -> userId
  • 计数器
  • 简单 JSON 字符串缓存
  • 分布式锁标记值

如果还用通用 RedisTemplate,就得处理更多序列化细节。

StringRedisTemplate 直接帮你把最常见的字符串场景优化好了。

所以它出现的意义就是:

针对最常见的字符串操作场景,降低配置复杂度,提高可读性。

:::

  1. StringRedisTemplate 适用场景

:::color3
它特别适合:key 是字符串、value 也是字符串、数据希望在 Redis 可直接读懂、不想折腾复杂序列化配置

常见应用包括:缓存 JSON 字符串、存验证码、存 token、存状态标记、做简单计数

:::

  1. StringRedisTemplate 的局限性

:::color3

它虽然方便,但也有边界:

  • 它更适合字符串,不适合直接存复杂 Java 对象
  • 如果你频繁操作对象,最后还是要手动转 JSON,或者换成 RedisTemplate

:::


Redisson

  1. Redisson 是什么?

:::color3
Redisson **是Redis 的增强能力工具箱,**封装了很多 **面向对象的分布式工具,**比如:

  • 分布式锁
  • 布隆过滤器
  • 限流器
  • 延迟队列
  • 分布式集合
  • 分布式对象
  • 可重入锁、读写锁、公平锁等

:::

  1. Redisson 不仅仅为了存个值,更多的是解决分布式问题

:::color3

Redisson 价值在于:把这些复杂的分布式问题封装成现成 API,让你少造轮子。

因为在真实业务里,我们用 Redis,往往不只是为了"存个值"。

很多时候我们真正要解决的是这些问题:

  • 多个服务实例同时抢同一份资源,怎么加锁?
  • 重复请求怎么控制?
  • 大量数据判重,怎么节省空间?
  • 分布式环境下,怎么做同步控制?

这些问题如果只用 RedisTemplate 或原生命令,也不是不能做,但会有两个问题:

  • 实现复杂
  • 容易踩坑

例如分布式锁,表面看上去好像一个 setnx 就够了,实际上还涉及:

  • 锁过期
  • 误删别人的锁
  • 可重入
  • 看门狗续期
  • 异常场景释放锁

:::

  1. Redisson 独特优点

:::color3

  • 高级能力丰富。:很多分布式场景,直接就有现成实现。
  • **封装成熟:**比自己基于 Redis 命令手搓要安全很多。
  • **面向对象使用体验好:**你调用的是 RLockRBloomFilterRRateLimiter,代码可读性更好。

:::

  1. Redisson 的局限性

:::color3

**代码更重:**它比简单的 RedisTemplate 要更"厚",引入后你其实是引入了一整套高级抽象。

**学习成本:**你需要理解它封装出来的分布式语义,而不只是 Redis 基本命令。

**杀鸡用牛刀:**如果你只是缓存几个字符串,用 Redisson 就有点"杀鸡用牛刀"。

:::


Lettuce

  1. Lettuce 是什么?

:::color3
Lettuce 是一个 现代、高性能的 Redis Java 客户端(更底层)

它是真正负责和 Redis 服务器建立连接、发送命令、接收结果的那一层工具。

即使你业务代码写的是 RedisTemplate,底层实际用的也可能是 Lettuce

:::

  1. Lettuce 的局限性

:::color3
Lettuce 更偏底层,所以直接使用时,你会更接近 Redis 原生命令和连接细节。

这意味着:灵活是灵活,但业务开发层面不一定最省事

:::


SpringBoot 整合客户端

StringRedisTemplate

  1. 导入 Maven 依赖
xml 复制代码
<dependencies>
  <!-- Spring Boot Redis -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>

  <!-- Spring Boot Web,方便演示接口 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
</dependencies>
  1. application.yaml 配置
yaml 复制代码
spring:
  redis:
    host: localhost
    port: 6379
    password:
    database: 0
    timeout: 5000
  1. service 业务代码
java 复制代码
package com.example.redisdemo.service;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class StringRedisService {

    private final StringRedisTemplate stringRedisTemplate;

    public StringRedisService(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public void setValueWithExpire(String key, String value, long timeoutSeconds) {
        // 设置带过期时间的字符串缓存
        stringRedisTemplate.opsForValue().set(key, value, timeoutSeconds, TimeUnit.SECONDS);
        // 获取字符串缓存
        stringRedisTemplate.opsForValue().get(key);
        // 删除缓存
        stringRedisTemplate.delete(key);
        // 计数器 +1
        stringRedisTemplate.opsForValue().increment(key);
    }
}

RedisTemplate

  1. 导入 Maven 依赖
xml 复制代码
<dependencies>
  <!-- Spring Boot Redis -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>

  <!-- Spring Boot Web,方便演示接口 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
</dependencies>
  1. application.yaml 配置
yaml 复制代码
spring:
  redis:
    host: localhost
    port: 6379
    password:
    database: 0
    timeout: 5000
  1. 创建一个实体类,用于演示
java 复制代码
package com.example.redisdemo.model;

import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private Long id;
    private String name;
    private Integer age;
}
  1. RedisTemplate 配置类

这里的配置类中序列化知识点,在下一篇文章说明。

java 复制代码
package com.example.redisdemo.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // key 采用 String 序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // value 采用 JSON 序列化
        Jackson2JsonRedisSerializer<Object> jacksonSerializer =
                new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(
                objectMapper.getPolymorphicTypeValidator(),
                ObjectMapper.DefaultTyping.NON_FINAL
        );
        jacksonSerializer.setObjectMapper(objectMapper);

        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jacksonSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jacksonSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}
  1. Service 业务代码
java 复制代码
package com.example.redisdemo.service;

import com.example.redisdemo.model.User;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class UserRedisService {

    private final RedisTemplate<String, Object> redisTemplate;

    public UserRedisService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void saveUser(String key, User user) {
        redisTemplate.opsForValue().set(key, user, 10, TimeUnit.MINUTES);
    }

    public User getUser(String key) {
        Object value = redisTemplate.opsForValue().get(key);
        if (value instanceof User) {
            return (User) value;
        }
        return null;
    }
}

Redisson

  1. Maven 依赖
xml 复制代码
<dependencies>
  <dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.27.2</version>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
</dependencies>
  1. application.yaml 配置
yaml 复制代码
spring:
  redis:
    host: localhost
    port: 6379
    password:
    database: 0
  1. Service 业务代码
java 复制代码
package com.example.redisdemo.service;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class OrderService {

    private final RedissonClient redissonClient;

    public OrderService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public String createOrder(Long userId) {
        String lockKey = "lock:order:" + userId;
        RLock lock = redissonClient.getLock(lockKey);

        boolean locked = false;
        try {
            // 尝试加锁,最多等 5 秒,锁自动释放时间 10 秒
            locked = lock.tryLock(5, 10, TimeUnit.SECONDS);

            if (!locked) {
                return "系统繁忙,请稍后重试";
            }

            // 模拟业务处理
            System.out.println("用户 " + userId + " 正在创建订单...");
            Thread.sleep(3000);

            return "订单创建成功";
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return "线程被中断";
        } finally {
            // 只有当前线程持有锁时才释放
            if (locked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

Lettuce

  1. Maven 导入依赖
xml 复制代码
<dependencies>
  <dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.3.2.RELEASE</version>
  </dependency>
</dependencies>
  1. 使用示例
java 复制代码
package com.example.redisdemo.lettuce;

import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;

public class LettuceDemo {

    public static void main(String[] args) {
        // 创建客户端
        RedisClient redisClient = RedisClient.create("redis://localhost:6379");

        // 建立连接
        StatefulRedisConnection<String, String> connection = redisClient.connect();

        try {
            // 获取同步命令接口
            RedisCommands<String, String> commands = connection.sync();

            // 设置值
            commands.set("name", "lettuce");

            // 获取值
            String value = commands.get("name");

            System.out.println("value = " + value);
        } finally {
            // 关闭连接和客户端
            connection.close();
            redisClient.shutdown();
        }
    }
}

封装 RedisUtils

java 复制代码
package com.example.demo.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // ========================= 通用操作 =========================

    /**
     * 设置过期时间
     */
    public boolean expire(String key, long timeout, TimeUnit unit) {
        try {
            return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
        } catch (Exception e) {
            throw new RuntimeException("设置过期时间失败", e);
        }
    }

    /**
     * 获取过期时间
     */
    public long getExpire(String key, TimeUnit unit) {
        try {
            Long expire = redisTemplate.getExpire(key, unit);
            return expire == null ? -1 : expire;
        } catch (Exception e) {
            throw new RuntimeException("获取过期时间失败", e);
        }
    }

    /**
     * 判断 key 是否存在
     */
    public boolean hasKey(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            throw new RuntimeException("判断 key 是否存在失败", e);
        }
    }

    /**
     * 删除单个 key
     */
    public boolean delete(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.delete(key));
        } catch (Exception e) {
            throw new RuntimeException("删除 key 失败", e);
        }
    }

    /**
     * 批量删除 key
     */
    public long delete(Collection<String> keys) {
        try {
            Long count = redisTemplate.delete(keys);
            return count == null ? 0 : count;
        } catch (Exception e) {
            throw new RuntimeException("批量删除 key 失败", e);
        }
    }

    // ========================= String =========================

    /**
     * 普通 set
     */
    public void set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception e) {
            throw new RuntimeException("String set 失败", e);
        }
    }

    /**
     * 带过期时间 set
     */
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        try {
            redisTemplate.opsForValue().set(key, value, timeout, unit);
        } catch (Exception e) {
            throw new RuntimeException("String set(带过期时间) 失败", e);
        }
    }

    /**
     * get
     */
    public Object get(String key) {
        try {
            return redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            throw new RuntimeException("String get 失败", e);
        }
    }

    /**
     * 自增
     */
    public long increment(String key, long delta) {
        try {
            Long value = redisTemplate.opsForValue().increment(key, delta);
            return value == null ? 0 : value;
        } catch (Exception e) {
            throw new RuntimeException("String increment 失败", e);
        }
    }

    /**
     * 自减
     */
    public long decrement(String key, long delta) {
        try {
            Long value = redisTemplate.opsForValue().increment(key, -delta);
            return value == null ? 0 : value;
        } catch (Exception e) {
            throw new RuntimeException("String decrement 失败", e);
        }
    }

    // ========================= List =========================

    /**
     * 从左侧插入
     */
    public long leftPush(String key, Object value) {
        try {
            Long count = redisTemplate.opsForList().leftPush(key, value);
            return count == null ? 0 : count;
        } catch (Exception e) {
            throw new RuntimeException("List leftPush 失败", e);
        }
    }

    /**
     * 从右侧插入
     */
    public long rightPush(String key, Object value) {
        try {
            Long count = redisTemplate.opsForList().rightPush(key, value);
            return count == null ? 0 : count;
        } catch (Exception e) {
            throw new RuntimeException("List rightPush 失败", e);
        }
    }

    /**
     * 获取列表范围
     */
    public List<Object> listRange(String key, long start, long end) {
        try {
            List<Object> list = redisTemplate.opsForList().range(key, start, end);
            return list == null ? Collections.emptyList() : list;
        } catch (Exception e) {
            throw new RuntimeException("List range 失败", e);
        }
    }

    /**
     * 获取列表长度
     */
    public long listSize(String key) {
        try {
            Long size = redisTemplate.opsForList().size(key);
            return size == null ? 0 : size;
        } catch (Exception e) {
            throw new RuntimeException("List size 失败", e);
        }
    }

    /**
     * 左侧弹出
     */
    public Object leftPop(String key) {
        try {
            return redisTemplate.opsForList().leftPop(key);
        } catch (Exception e) {
            throw new RuntimeException("List leftPop 失败", e);
        }
    }

    /**
     * 右侧弹出
     */
    public Object rightPop(String key) {
        try {
            return redisTemplate.opsForList().rightPop(key);
        } catch (Exception e) {
            throw new RuntimeException("List rightPop 失败", e);
        }
    }

    // ========================= Hash =========================

    /**
     * put 一个 field
     */
    public void hPut(String key, String hashKey, Object value) {
        try {
            redisTemplate.opsForHash().put(key, hashKey, value);
        } catch (Exception e) {
            throw new RuntimeException("Hash put 失败", e);
        }
    }

    /**
     * putAll
     */
    public void hPutAll(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
        } catch (Exception e) {
            throw new RuntimeException("Hash putAll 失败", e);
        }
    }

    /**
     * get 一个 field
     */
    public Object hGet(String key, String hashKey) {
        try {
            return redisTemplate.opsForHash().get(key, hashKey);
        } catch (Exception e) {
            throw new RuntimeException("Hash get 失败", e);
        }
    }

    /**
     * 获取整个 hash
     */
    public Map<Object, Object> hEntries(String key) {
        try {
            return redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            throw new RuntimeException("Hash entries 失败", e);
        }
    }

    /**
     * 删除一个或多个 field
     */
    public long hDelete(String key, Object... hashKeys) {
        try {
            Long count = redisTemplate.opsForHash().delete(key, hashKeys);
            return count == null ? 0 : count;
        } catch (Exception e) {
            throw new RuntimeException("Hash delete 失败", e);
        }
    }

    /**
     * 判断 field 是否存在
     */
    public boolean hHasKey(String key, String hashKey) {
        try {
            return Boolean.TRUE.equals(redisTemplate.opsForHash().hasKey(key, hashKey));
        } catch (Exception e) {
            throw new RuntimeException("Hash hasKey 失败", e);
        }
    }

    // ========================= Set =========================

    /**
     * 添加元素
     */
    public long sAdd(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            return count == null ? 0 : count;
        } catch (Exception e) {
            throw new RuntimeException("Set add 失败", e);
        }
    }

    /**
     * 获取所有元素
     */
    public Set<Object> sMembers(String key) {
        try {
            Set<Object> members = redisTemplate.opsForSet().members(key);
            return members == null ? Collections.emptySet() : members;
        } catch (Exception e) {
            throw new RuntimeException("Set members 失败", e);
        }
    }

    /**
     * 判断元素是否存在
     */
    public boolean sIsMember(String key, Object value) {
        try {
            return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
        } catch (Exception e) {
            throw new RuntimeException("Set isMember 失败", e);
        }
    }

    /**
     * 删除元素
     */
    public long sRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count == null ? 0 : count;
        } catch (Exception e) {
            throw new RuntimeException("Set remove 失败", e);
        }
    }

    /**
     * 获取集合大小
     */
    public long sSize(String key) {
        try {
            Long size = redisTemplate.opsForSet().size(key);
            return size == null ? 0 : size;
        } catch (Exception e) {
            throw new RuntimeException("Set size 失败", e);
        }
    }

    // ========================= ZSet =========================

    /**
     * 添加元素和分数
     */
    public boolean zAdd(String key, Object value, double score) {
        try {
            return Boolean.TRUE.equals(redisTemplate.opsForZSet().add(key, value, score));
        } catch (Exception e) {
            throw new RuntimeException("ZSet add 失败", e);
        }
    }

    /**
     * 按区间获取元素(升序)
     */
    public Set<Object> zRange(String key, long start, long end) {
        try {
            Set<Object> set = redisTemplate.opsForZSet().range(key, start, end);
            return set == null ? Collections.emptySet() : set;
        } catch (Exception e) {
            throw new RuntimeException("ZSet range 失败", e);
        }
    }

    /**
     * 按分数区间获取元素
     */
    public Set<Object> zRangeByScore(String key, double min, double max) {
        try {
            Set<Object> set = redisTemplate.opsForZSet().rangeByScore(key, min, max);
            return set == null ? Collections.emptySet() : set;
        } catch (Exception e) {
            throw new RuntimeException("ZSet rangeByScore 失败", e);
        }
    }

    /**
     * 获取元素分数
     */
    public Double zScore(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().score(key, value);
        } catch (Exception e) {
            throw new RuntimeException("ZSet score 失败", e);
        }
    }

    /**
     * 删除元素
     */
    public long zRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForZSet().remove(key, values);
            return count == null ? 0 : count;
        } catch (Exception e) {
            throw new RuntimeException("ZSet remove 失败", e);
        }
    }

    /**
     * 给元素增加分数
     */
    public Double zIncrementScore(String key, Object value, double delta) {
        try {
            return redisTemplate.opsForZSet().incrementScore(key, value, delta);
        } catch (Exception e) {
            throw new RuntimeException("ZSet incrementScore 失败", e);
        }
    }

    /**
     * 获取排名(升序,0 开始)
     */
    public Long zRank(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().rank(key, value);
        } catch (Exception e) {
            throw new RuntimeException("ZSet rank 失败", e);
        }
    }

    /**
     * 获取倒序排名(0 开始)
     */
    public Long zReverseRank(String key, Object value) {
        try {
            return redisTemplate.opsForZSet().reverseRank(key, value);
        } catch (Exception e) {
            throw new RuntimeException("ZSet reverseRank 失败", e);
        }
    }
}
相关推荐
超级无敌葛大侠2 小时前
Redis主从复制
java·redis
ErizJ2 小时前
Redis|学习笔记
redis·笔记·学习
小道仙972 小时前
Redisson源码解析,分布式锁解析
redis·分布式锁·redisson
PaperData2 小时前
1988-2025年《中国人口和就业统计年鉴》全年份excel+PDF
数据库·人工智能·数据分析·经管
星河耀银海3 小时前
C语言与数据库交互:SQLite实战与数据持久化
c语言·数据库·sqlite·交互
过期动态3 小时前
MySQL中的约束
android·java·数据库·spring boot·mysql
追梦开发者3 小时前
Redis 避坑指南①:从安装到连接,这 9 个坑 90% 的人都踩过
redis·缓存·database
程序员陆通3 小时前
月烧 400 刀到不到 20 刀:我是怎么把 OpenClaw 的 Token 账单砍掉 95% 的
java·前端·数据库
Shan12053 小时前
站在计算机领域视角看:SQL注入攻击
网络·数据库·sql