Redis基础数据结构

一、字符串(String)

1、结构特性

Redis最基础的数据结构,Value可存储字符串(ASCII/UTF-8)、整数/浮点数(支持原子增减)、二进制数据(如图片、序列化对象),单个Value最大容量512MB。底层采用动态字符串(SDS)实现,支持预分配空间,减少扩容开销。

2、应用场景

  • 缓存单值数据:存储高频访问的单个信息,如用户Token、商品详情页缓存、接口返回结果缓存(减少数据库查询压力)。

  • 分布式计数器:基于原子增减指令,实现点赞数、文章阅读量、接口访问量、订单编号生成等(避免并发计数误差)。

  • 分布式锁 :利用 SET key value NX EX 原子指令实现分布式锁(避免 SETNX 与 EXPIRE 分离导致死锁风险)

  • 临时存储:验证码(短信/图形验证码,设置短期过期)、用户登录态(Session共享场景)。

  • 位操作场景:存储布尔值集合(如用户签到状态,1表示签到,0表示未签到),通过位指令高效统计。

3、指令

(1)增删改指令

指令 作用细节 示例
SET key value 覆盖式设置键值对,可搭配NX/XX/EX/PX参数(如SET key val EX 30 等价于SET+EXPIRE) SET name "zhangsan" EX 60
SETNX key value 仅当键不存在时设置(原子操作),分布式锁核心指令,无过期时间需手动设置 SETNX lock:order "1"
SETEX key sec value 原子性设置键值+过期时间(秒),避免SET与EXPIRE分离导致的并发问题 SETEX code:123 180 "654321"
PSETEX key ms value 与SETEX一致,过期时间单位为毫秒 PSETEX code:123 180000 "654321"
GETSET key value 原子性获取旧值并设置新值,适合重置计数器场景 GETSET visit:today 0
DEL key 删除指定键,返回删除成功的键数量(String类型最多返回1) DEL name

(2)查询指令

指令 作用细节 示例
GET key 获取指定键的值,键不存在返回nil GET name
STRLEN key 获取值的字符串长度,非字符串类型报错 STRLEN name
EXISTS key 判断键是否存在,存在返回1,不存在返回0 EXISTS name
TTL key 获取键剩余过期时间(秒),-1表示无过期,-2表示键不存在 TTL code:123
PTTL key 与TTL一致,单位为毫秒 PTTL code:123

(3)数值操作指令

指令 作用细节 示例
INCR key 值自增1,仅支持整数类型,非数字报错,键不存在则先设为0再增1 INCR visit:today
DECR key 值自减1,规则与INCR一致 DECR stock:goods1
INCRBY key n 值自增n(n为整数),支持正负值(负数值等价于递减) INCRBY visit:today 10
DECRBY key n 值自减n(n为整数),等价于INCRBY key -n DECRBY stock:goods1 2
INCRBYFLOAT key f 值自增浮点数f,支持正负值,精度受浮点数存储限制 INCRBYFLOAT score:user1 0.5

(4)位操作指令

指令 作用细节 示例
SETBIT key offset val 设置指定偏移量(offset)的位值(0/1),偏移量从0开始 SETBIT sign:user1 5 1(第5天签到)
GETBIT key offset 获取指定偏移量的位值,偏移量超出范围返回0 GETBIT sign:user1 5
BITCOUNT key 统计值中值为1的位数量,可指定范围(字节) BITCOUNT sign:user1(统计当月签到天数,注意需按日期映射 offset(如 day-1),BITCOUNT 才能统计当月签到次数)

4、 在Spring Boot实现

java 复制代码
@Service
public class StringRedisService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 1. 基础增删改查(带过期时间,原子操作)
    public void setWithExpire(String key, String value, long timeout, TimeUnit timeUnit) {
        stringRedisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    public Long delete(String key) {
        return stringRedisTemplate.delete(key) ? 1L : 0L;
    }

    // 2. 分布式锁(SETNX+过期时间,原子操作)
    public boolean tryLock(String lockKey, String lockValue, long expireSeconds) {
        return Boolean.TRUE.equals(stringRedisTemplate.opsForValue()
                .setIfAbsent(lockKey, lockValue, expireSeconds, TimeUnit.SECONDS));
    }

    // 3. 计数器操作
    public Long increment(String key) {
        return stringRedisTemplate.opsForValue().increment(key);
    }

    public Long incrementBy(String key, long delta) {
        return stringRedisTemplate.opsForValue().increment(key, delta);
    }

    public Double incrementByFloat(String key, double delta) {
        return stringRedisTemplate.opsForValue().increment(key, delta);
    }

    // 4. 位操作(用户签到场景)
    public void setBit(String key, long offset, boolean value) {
        stringRedisTemplate.opsForValue().setBit(key, offset, value);
    }

    public Boolean getBit(String key, long offset) {
        return stringRedisTemplate.opsForValue().getBit(key, offset);
    }

    public Long bitCount(String key) {
        return stringRedisTemplate.execute((connection) -> 
                connection.bitCount(key.getBytes()), true);
    }
}

二、列表(List)

1、结构特性

有序、可重复的字符串集合,底层采用双向链表(底层采用 quicklist 实现(内部由 listpack 组成),对外表现为双端链表结构,支持高效的头尾插入与删除操作。)。支持从头部(left)和尾部(right)高效添加/删除元素(时间复杂度O(1)),按索引访问元素效率较低(O(n))。

2、应用场景

  • 简单消息队列:基于LPUSH+RPOP(生产者从头部添加,消费者从尾部获取)或RPUSH+LPOP实现,适合低并发、无需ACK机制的场景(如日志收集、通知推送)。

  • 阻塞式消息队列:利用BLPOP/BRPOP指令,消费者无消息时阻塞等待,避免轮询空转(如订单状态通知、任务分发)。

  • 最新消息列表:如朋友圈评论列表、商品咨询列表,通过LPUSH添加新消息,LRANGE获取前N条最新消息。

  • 栈/队列实现:栈(LPUSH+LPOP,先进后出)、队列(LPUSH+RPOP,先进先出)。

  • 限流场景:存储用户近期操作记录,通过 LLEN 判断操作次数,适用于低精度、非严格限流场景;生产环境更推荐使用 ZSet + 时间戳实现。

3、指令

(1)增删指令

指令 作用细节 示例
LPUSH key val1 val2 从列表头部批量添加元素,元素顺序与指令中一致(val1先入栈,位于头部) LPUSH msg:queue "msg1" "msg2"
RPUSH key val1 val2 从列表尾部批量添加元素,元素顺序与指令中一致(val1先入队,位于尾部前方) RPUSH msg:queue "msg1" "msg2"
LPOP key 从头部移除并返回元素,列表为空返回nil LPOP msg:queue
RPOP key 从尾部移除并返回元素,列表为空返回nil RPOP msg:queue
LREM key count val 删除列表中count个值为val的元素:count>0从头部删,count<0从尾部删,count=0删除所有 LREM msg:queue 2 "msg1"
LTRIM key start end 保留列表中start到end的元素,删除其他元素(裁剪列表),支持负索引(-1为尾部) LTRIM msg:queue 0 9(保留前10条)
RPOPLPUSH source dest 从source尾部弹出元素,立即插入dest头部(原子操作),适合消息备份 RPOPLPUSH msg:queue msg:backup

(2)阻塞式增删指令

指令 作用细节 示例
BLPOP key1 key2 timeout 阻塞式从多个列表头部取元素,超时时间(秒),0表示永久阻塞,有元素时优先取前面的列表 BLPOP msg:queue1 msg:queue2 10
BRPOP key1 key2 timeout 与BLPOP一致,从尾部取元素 BRPOP msg:queue 0
BRPOPLPUSH source dest timeout 阻塞式执行RPOPLPUSH,无元素时阻塞等待 BRPOPLPUSH msg:queue msg:backup 10

(3)查询指令

指令 作用细节 示例
LRANGE key start end 获取指定范围元素,start=0、end=-1表示获取所有元素,索引越界无报错 LRANGE msg:queue 0 9
LLEN key 获取列表长度,键不存在返回0 LLEN msg:queue
LINDEX key index 获取指定索引的元素,索引越界返回nil LINDEX msg:queue 2

(4)修改指令

指令 作用细节 示例
LSET key index val 修改指定索引的元素值,索引越界报错 LSET msg:queue 1 "newMsg"

4、在Spring Boot实现

java 复制代码
@Service
public class ListRedisService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    private static final String MSG_QUEUE = "msg:queue";
    private static final String MSG_BACKUP = "msg:backup";

    // 1. 生产者(尾部添加消息)
    public Long produceMsg(String msg) {
        return stringRedisTemplate.opsForList().rightPush(MSG_QUEUE, msg);
    }

    // 2. 消费者(非阻塞,尾部弹出)
    public String consumeMsg() {
        return stringRedisTemplate.opsForList().rightPop(MSG_QUEUE);
    }

    // 3. 阻塞式消费者(尾部弹出,超时10秒)
    public String blockingConsumeMsg() {
        return stringRedisTemplate.opsForList().rightPop(MSG_QUEUE, 10, TimeUnit.SECONDS);
    }

    // 4. 消息备份(RPOPLPUSH,原子操作)
    public String consumeAndBackupMsg() {
        return stringRedisTemplate.opsForList().rightPopAndLeftPush(MSG_QUEUE, MSG_BACKUP);
    }

    // 5. 阻塞式消息备份
    public String blockingConsumeAndBackupMsg() {
        return stringRedisTemplate.opsForList().rightPopAndLeftPush(MSG_QUEUE, MSG_BACKUP, 10, TimeUnit.SECONDS);
    }

    // 6. 获取最新N条消息
    public List<String> getLatestMsgs(int count) {
        // 0到count-1,获取前count条(头部为最新)
        return stringRedisTemplate.opsForList().range(MSG_QUEUE, 0, count - 1);
    }

    // 7. 裁剪列表(保留前N条)
    public void trimList(int retainCount) {
        stringRedisTemplate.opsForList().trim(MSG_QUEUE, 0, retainCount - 1);
    }

    // 8. 获取列表长度(限流判断)
    public Long getListSize() {
        return stringRedisTemplate.opsForList().size(MSG_QUEUE);
    }
}

三、哈希(Hash)

1、结构特性

类似Java中的HashMap<String, String>,一个Hash键对应多个字段(field)和值(value),字段唯一、值可重复。底层采用 listpack + 哈希表(hashtable) 的混合实现,小规模 Hash 使用紧凑结构存储,大规模 Hash 自动切换为哈希表。(字段少用压缩列表,字段多用哈希表),适合存储对象的多个属性,可单独操作字段(无需获取整个对象),Hash 只能对 key 设置过期时间,不能对单个 field 设置 TTL。

2、应用场景

  • 对象缓存:存储用户信息、商品信息、订单信息等(如user:1001的name/age/phone/address字段),相比String存储序列化对象,可单独更新某个字段,减少网络传输量。

  • 用户属性管理:如电商用户的收货地址、偏好设置,按用户ID作为Hash键,属性作为字段存储。

  • 商品属性缓存:如服装商品的颜色、尺码、材质等动态属性,支持单独修改某个属性(如库存字段)。

  • 统计数据存储:如每日各模块访问量,Hash键为stat:20241001,字段为module1/module2,值为访问量。

3、指令

(1)增删改指令

指令 作用细节 示例
HSET key field val 设置单个字段值,字段存在则覆盖,返回1(新增)/0(覆盖) HSET user:1001 name "lisi"
HMSET key f1 v1 f2 v2 批量设置字段值,Redis 4.0+推荐用HSET(支持批量) HMSET user:1001 age 20 phone "13800138000"
HDEL key field1 field2 批量删除指定字段,返回删除成功的字段数量 HDEL user:1001 phone address
HSETNX key field val 仅当字段不存在时设置,返回1(成功)/0(失败),原子操作 HSETNX user:1001 email "lisi@xxx.com"

(2)查询指令

指令 作用细节 示例
HGET key field 获取单个字段值,字段/键不存在返回nil HGET user:1001 name
HMGET key f1 f2 批量获取字段值,返回值顺序与字段顺序一致,不存在的字段返回nil HMGET user:1001 name age
HGETALL key 获取所有字段和值,返回「field1,val1,field2,val2」格式列表,字段多时有性能压力 HGETALL user:1001
HKEYS key 获取所有字段名,返回字段列表 HKEYS user:1001
HVALS key 获取所有字段值,返回值列表 HVALS user:1001
HLEN key 获取字段数量,键不存在返回0 HLEN user:1001
HEXISTS key field 判断字段是否存在,返回1(存在)/0(不存在) HEXISTS user:1001 email

(3)数值操作指令

指令 作用细节 示例
HINCRBY key field n 字段值自增n(n为整数),字段值非数字报错,字段不存在则先设为0再增 HINCRBY goods:1001 stock -1(扣减库存)
HINCRBYFLOAT key field f 字段值自增浮点数f,规则与HINCRBY一致 HINCRBYFLOAT user:1001 balance 50.5

4、在Spring Boot实现

java 复制代码
@Service
public class HashRedisService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 1. 单个字段操作
    public void setField(String key, String field, String value) {
        stringRedisTemplate.opsForHash().put(key, field, value);
    }

    public String getField(String key, String field) {
        return (String) stringRedisTemplate.opsForHash().get(key, field);
    }

    public Boolean hasField(String key, String field) {
        return stringRedisTemplate.opsForHash().hasKey(key, field);
    }

    public Long deleteField(String key, String... fields) {
        return stringRedisTemplate.opsForHash().delete(key, (Object[]) fields);
    }

    // 2. 批量操作
    public void batchSetFields(String key, Map<String, String> fieldValueMap) {
        stringRedisTemplate.opsForHash().putAll(key, fieldValueMap);
    }

    public List<String> batchGetFields(String key, List<String> fields) {
        return stringRedisTemplate.opsForHash().multiGet(key, fields);
    }

    // 3. 数值字段操作(库存、余额)
    public Long incrementField(String key, String field, long delta) {
        return stringRedisTemplate.opsForHash().increment(key, field, delta);
    }

    public Double incrementFieldByFloat(String key, String field, double delta) {
        return stringRedisTemplate.opsForHash().increment(key, field, delta);
    }

    // 4. 全量查询(谨慎使用,字段多时有性能问题)
    public Map<Object, Object> getAllFields(String key) {
        return stringRedisTemplate.opsForHash().entries(key);
    }

    public Set<Object> getAllFieldNames(String key) {
        return stringRedisTemplate.opsForHash().keys(key);
    }

    // 5. 示例:缓存用户信息
    public void cacheUser(Long userId, String name, Integer age, String phone) {
        String key = "user:" + userId;
        Map<String, String> userMap = new HashMap<>();
        userMap.put("name", name);
        userMap.put("age", age.toString());
        userMap.put("phone", phone);
        batchSetFields(key, userMap);
        // 设置过期时间(1小时)
        stringRedisTemplate.expire(key, 3600, java.util.concurrent.TimeUnit.SECONDS);
    }
}

四、集合(Set)

1、结构特性

无序、不可重复的字符串集合,底层会根据元素类型与数量自动选择 intset 或 hashtable 实现;在较新的 Redis 版本中,小规模集合的紧凑存储由 listpack 替代历史上的 ziplist,添加、删除、判断元素是否存在的时间复杂度均为O(1),支持高效的集合运算(交集、并集、差集)。

2、应用场景

  • 元素去重:如用户点赞列表、评论去重、访问IP去重(避免重复统计)。

  • 社交关系管理:如用户的好友列表、关注列表、粉丝列表,通过集合运算获取共同好友、推荐好友。

  • 抽奖活动:如随机抽取N名中奖用户,利用SRANDMEMBER/POP指令随机获取元素。

  • 标签管理:如文章标签、商品分类标签,一个元素可对应多个标签,通过集合运算筛选含指定标签的内容。

  • 黑名单/白名单:如接口访问黑名单,判断用户ID是否在黑名单集合中,实现快速拦截。

3、指令

(1)增删指令

指令 作用细节 示例
SADD key val1 val2 批量添加元素,自动去重,返回新增成功的元素数量(重复元素不计) SADD like:1001 "user1" "user2" "user3"
SREM key val1 val2 批量删除元素,返回删除成功的元素数量 SREM like:1001 "user2" "user4"
SPOP key [count] 随机移除并返回count个元素(默认1个),集合为空返回nil SPOP lottery:users 3(抽取3名中奖用户)
SMOVE source dest val 将source中的val移动到dest(原子操作),成功返回1,失败返回0 SMOVE blacklist:temp blacklist:perm "user5"

(2)查询指令

指令 作用细节 示例
SMEMBERS key 获取集合所有元素,元素无序,集合过大时有性能压力 SMEMBERS like:1001
SCARD key 获取集合元素数量,键不存在返回0 SCARD like:1001
SISMEMBER key val 判断元素是否在集合中,返回1(存在)/0(不存在),O(1)效率 SISMEMBER blacklist:perm "user5"
SRANDMEMBER key [count] 随机返回count个元素(默认1个),不删除元素,count为负返回可重复元素,反之为重复元素 SRANDMEMBER lottery:users 5(预览5名用户)

(3)集合运算指令

指令 作用细节 示例
SINTER key1 key2 ... 获取所有集合的交集(同时存在于所有集合的元素) SINTER friend:user1 friend:user2(共同好友)
SINTERSTORE dest key1 key2 ... 将交集结果存储到dest集合,返回交集元素数量 SINTERSTORE common:friend friend:user1 friend:user2
SUNION key1 key2 ... 获取所有集合的并集(存在于任意一个集合的元素) SUNION tag:tech tag:life(所有标签)
SUNIONSTORE dest key1 key2 ... 将并集结果存储到dest集合,返回并集元素数量 SUNIONSTORE all:tag tag:tech tag:life
SDIFF key1 key2 ... 获取差集(存在于key1,不存在于其他集合的元素) SDIFF friend:user1 friend:user2(user1有而user2没有的好友)
SDIFFSTORE dest key1 key2 ... 将差集结果存储到dest集合,返回差集元素数量 SDIFFSTORE diff:friend friend:user1 friend:user2

4、在Spring Boot实现

java 复制代码
@Service
public class SetRedisService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    private static final String LIKES_PREFIX = "like:";
    private static final String LOTTERY_KEY = "lottery:users";
    private static final String BLACKLIST_KEY = "blacklist:perm";

    // 1. 元素操作(点赞/取消点赞)
    public boolean addLike(Long articleId, Long userId) {
        String key = LIKES_PREFIX + articleId;
        // 新增成功返回1,重复点赞返回0
        Long count = stringRedisTemplate.opsForSet().add(key, userId.toString());
        return count != null && count > 0;
    }

    public boolean cancelLike(Long articleId, Long userId) {
        String key = LIKES_PREFIX + articleId;
        // 删除成功返回1,未点赞返回0
        Long count = stringRedisTemplate.opsForSet().remove(key, userId.toString());
        return count != null && count > 0;
    }

    public boolean isLiked(Long articleId, Long userId) {
        String key = LIKES_PREFIX + articleId;
        return Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(key, userId.toString()));
    }

    // 2. 抽奖场景
    public Set<String> drawLottery(int count) {
        // 随机抽取count名用户,不删除(预览)
        return stringRedisTemplate.opsForSet().randomMembers(LOTTERY_KEY, count);
    }

    public Set<String> drawAndRemoveLottery(int count) {
        // 随机抽取并删除count名用户(确定中奖)
        return stringRedisTemplate.opsForSet().pop(LOTTERY_KEY, count);
    }

    // 3. 集合运算(社交场景)
    public Set<String> getCommonFriends(String user1Key, String user2Key) {
        // 获取共同好友(交集)
        return stringRedisTemplate.opsForSet().intersect(user1Key, user2Key);
    }

    public Set<String> getRecommendFriends(String user1Key, String user2Key) {
        // 获取user1可推荐的好友(user2有而user1没有的,差集)
        return stringRedisTemplate.opsForSet().difference(user2Key, user1Key);
    }

    // 4. 黑名单判断
    public boolean isInBlacklist(Long userId) {
        return Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(BLACKLIST_KEY, userId.toString()));
    }

    // 5. 获取集合大小
    public Long getSetSize(String key) {
        return stringRedisTemplate.opsForSet().size(key);
    }
}

五、有序集合(Sorted Set/ZSet)

1、结构特性

有序、不可重复的字符串集合,每个元素关联一个浮点型分数(score),Redis按分数从小到大排序。底层采用 listpack + 跳表(skiplist)+ 字典(dict) 的混合实现,用于同时支持按成员快速定位和按分数有序访问,支持按分数范围、排名范围查询,分数可动态调整。

2、应用场景

  • 排行榜场景:如商品销量榜、用户积分榜、游戏战力榜、文章阅读量榜,支持按排名/分数查询TopN。

  • 延时队列:以时间戳作为分数,元素为任务ID,通过ZRANGEBYSCORE获取到期任务,实现延时执行(如订单超时取消、消息延时推送)。基于 ZRANGEBYSCORE + ZREMRANGEBYSCORE 实现的延时队列在多消费者场景下非原子,生产环境需结合 Lua 脚本保证"获取 + 删除"的原子性。

  • 带权重的消息排序:如优先级任务队列,高分任务优先执行。

  • 范围查询场景:如按用户积分等级划分用户群体(积分100-500为青铜,500-1000为白银)。

  • 地理位置存储:Redis扩展功能,将经纬度转换为分数存储,实现附近的人、距离排序功能(基于ZSet底层)。

3、指令

(1)增删改指令

指令 作用细节 示例
ZADD key score1 val1 score2 val2 批量添加元素及分数,元素存在则更新分数,返回新增元素数量 ZADD sales:rank 100 "goods1" 80 "goods2"
ZREM key val1 val2 批量删除元素,返回删除成功的元素数量 ZREM sales:rank "goods2" "goods3"
ZINCRBY key increment val 增加元素分数(increment可正负),返回更新后的分数,元素不存在则先设为0再增减 ZINCRBY sales:rank 10 "goods1"(销量+10)
ZADD key NX score member NX 为 ZADD 的参数,用于仅在元素不存在时添加 ZADD sales:rank NX 50 "goods4"

(2)查询指令(按排名)

|-----------------------------------------|-----------------------------------------------------------|---------------------------------------------|
| 指令 | 作用细节 | 示例 |
| ZRANGE key start stop [WITHSCORES] | 按分数升序返回排名start到stop的元素,WITHSCORES参数可同时返回分数,支持负索引(-1为最后一位) | ZRANGE sales:rank 0 4 WITHSCORES(获取前5名及销量) |
| ZREVRANGE key start stop [WITHSCORES] | 按分数降序返回排名start到stop的元素,与ZRANGE排序方向相反,适合TopN排行榜 | ZREVRANGE sales:rank 0 2 WITHSCORES(获取销量前三) |
| ZRANK key member | 返回元素按分数升序的排名(从0开始),元素不存在返回nil | ZRANK sales:rank "goods1" |
| ZREVRANK key member | 返回元素按分数降序的排名(从0开始),排行榜场景常用 | ZREVRANK sales:rank "goods1"(查看商品1销量排名) |

(3)查询指令(按分数)

|--------------------------------------------------------------------|---------------------------------------------------|----------------------------------------------------------------|
| 指令 | 作用细节 | 示例 |
| ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] | 按分数升序返回分数在min到max之间的元素,LIMIT控制分页,min/max可加(表示不包含) | ZRANGEBYSCORE sales:rank 50 100 LIMIT 0 10(销量50-100的前10个商品) |
| ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] | 按分数降序返回分数在max到min之间的元素,适合按分数范围取TopN | ZREVRANGEBYSCORE sales:rank 200 100 LIMIT 0 5(销量100-200的前5个商品) |
| ZSCORE key member | 返回元素对应的分数,元素不存在返回nil,分数为浮点型 | ZSCORE sales:rank "goods1"(查看商品1销量) |
| ZCOUNT key min max | 统计分数在min到max之间的元素数量,支持(排除边界) | ZCOUNT sales:rank (80 150(销量80-150不含80的商品数) |

(4)删除指令(按条件)

|--------------------------------|--------------------------------|------------------------------------------------|
| 指令 | 作用细节 | 示例 |
| ZREMRANGEBYRANK key start stop | 按排名删除start到stop之间的元素,返回删除成功的数量 | ZREMRANGEBYRANK sales:rank 10 -1(删除排名10及以后的商品) |
| ZREMRANGEBYSCORE key min max | 按分数删除min到max之间的元素,返回删除成功的数量 | ZREMRANGEBYSCORE sales:rank 0 30(删除销量30及以下的商品) |

(5)集合运算指令

|------------------------------------------------------------------------------------------|---------------------------------------------------------------------|----------------------------------------------------------------------------------|
| 指令 | 作用细节 | 示例 |
| ZINTERSTORE dest numkeys key1 key2 ... [WEIGHTS w1 w2 ...] [AGGREGATE SUM|MIN|MAX] | 计算多个ZSet的交集存入dest,numkeys指定集合数量,WEIGHTS设权重,AGGREGATE指定分数聚合方式(默认SUM) | ZINTERSTORE hot:goods 2 sales:rank click:rank WEIGHTS 0.6 0.4(销量60%+点击40%计算热门商品) |
| ZUNIONSTORE dest numkeys key1 key2 ... [WEIGHTS w1 w2 ...] [AGGREGATE SUM|MIN|MAX] | 计算多个ZSet的并集存入dest,参数规则与ZINTERSTORE一致 | ZUNIONSTORE all:goods 2 sales:rank collect:rank(合并销量榜和收藏榜) |

4、在Spring Boot实现

java 复制代码
@Service
public class ZSetRedisService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    private static final String SALES_RANK_KEY = "sales:rank";
    private static final String DELAY_QUEUE_KEY = "delay:queue";

    // 1. 排行榜场景:添加/更新商品销量
    public Boolean addOrUpdateSales(String goodsId, double sales) {
        return stringRedisTemplate.opsForZSet().add(SALES_RANK_KEY, goodsId, sales);
    }

    // 增加商品销量(原子操作)
    public Double incrementSales(String goodsId, double delta) {
        return stringRedisTemplate.opsForZSet().incrementScore(SALES_RANK_KEY, goodsId, delta);
    }

    // 获取销量TopN(降序,带销量)
    public Set<ZSetOperations.TypedTuple<String>> getSalesTopN(int topN) {
        // 0到topN-1,降序排列,返回元素和分数
        return stringRedisTemplate.opsForZSet().reverseRangeWithScores(SALES_RANK_KEY, 0, topN - 1);
    }

    // 获取商品销量排名(降序)
    public Long getGoodsSalesRank(String goodsId) {
        return stringRedisTemplate.opsForZSet().reverseRank(SALES_RANK_KEY, goodsId);
    }

    // 2. 延时队列场景:添加延时任务(分数为时间戳,单位毫秒)
    public Boolean addDelayTask(String taskId, long delayMillis) {
        long expireTimestamp = System.currentTimeMillis() + delayMillis;
        return stringRedisTemplate.opsForZSet().add(DELAY_QUEUE_KEY, taskId, expireTimestamp);
    }

    // 消费到期任务(获取当前时间戳前的所有任务)
    public Set<String> consumeExpiredTasks() {
        long currentTimestamp = System.currentTimeMillis();
        // 分数小于等于当前时间戳的任务均为到期任务,取完后删除
        Set<String> expiredTasks = stringRedisTemplate.opsForZSet().rangeByScore(DELAY_QUEUE_KEY, 0, currentTimestamp);
        if (expiredTasks != null && !expiredTasks.isEmpty()) {
            stringRedisTemplate.opsForZSet().removeRangeByScore(DELAY_QUEUE_KEY, 0, currentTimestamp);
        }
        return expiredTasks;
    }

    // 3. 范围查询:按分数获取商品(销量区间)
    public Set<String> getGoodsBySalesRange(double minSales, double maxSales, int offset, int count) {
        return stringRedisTemplate.opsForZSet().rangeByScore(SALES_RANK_KEY, minSales, maxSales, offset, count);
    }

    // 4. 集合运算:计算热门商品(销量+点击权重聚合)
    public Long calculateHotGoods(String destKey, String key1, String key2, double weight1, double weight2) {
        // 并集运算,权重分别为weight1和weight2,分数聚合方式为SUM
        return stringRedisTemplate.opsForZSet().unionAndStore(key1, key2, destKey,
                ZSetOperations.UnionAndIntersectOptions.weighted(weight1, weight2)
                        .aggregate(ZSetOperations.Aggregate.SUM));
    }

    // 5. 删除指定排名之后的元素(精简排行榜)
    public Long removeRankAfter(int rank) {
        return stringRedisTemplate.opsForZSet().removeRange(SALES_RANK_KEY, rank, -1);
    }
}
相关推荐
2301_8002561116 小时前
数据库设计中的 “数据依赖→设计异常→关系分解(范式)” 核心逻辑
数据库·postgresql
汽车仪器仪表相关领域16 小时前
光轴精准测量,安全照明保障——NHD-8101/8000型远近光检测仪项目实战分享
数据库·人工智能·安全·压力测试·可用性测试
Qhumaing16 小时前
C++学习:【PTA】数据结构 7-2 实验6-2(图-邻接表)
数据结构·c++·学习
方便面不加香菜16 小时前
基于顺序表实现通讯录项目
c语言·数据结构
大爱编程♡16 小时前
Spring IoC&DI
数据库·mysql·spring
king_harry16 小时前
金仓数据库KingbaseES中WalMiner接口使用
数据库·kingbase·walminer
爱潜水的小L16 小时前
自学嵌入式day43,商城网页
数据库·oracle
IvorySQL16 小时前
PostgreSQL 的 SQL 查询之旅
数据库·人工智能·postgresql·开源
musenh17 小时前
redis和jedis
数据库·redis·缓存