一、字符串(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);
}
}