文章目录
- [一、String 字符串](#一、String 字符串)
-
- 内部编码
- 核心命令详解
-
- [1. set / get](#1. set / get)
- [2. mset / mget](#2. mset / mget)
- [3. setnx](#3. setnx)
- [4. setex / psetex](#4. setex / psetex)
- [5. incr / decr / incrby / decrby / incrbyfloat](#5. incr / decr / incrby / decrby / incrbyfloat)
- [6. append](#6. append)
- [7. getrange](#7. getrange)
- [8. setrange](#8. setrange)
- [9. strlen](#9. strlen)
- 典型业务场景
- [二、Hash 哈希](#二、Hash 哈希)
-
- 底层内部编码
- 核心命令详解
-
- [1. hset](#1. hset)
- [2. hget](#2. hget)
- [3. hexists](#3. hexists)
- [4. hdel](#4. hdel)
- [5. hkeys / hvals](#5. hkeys / hvals)
- [6. hgetall / hmget](#6. hgetall / hmget)
- [7. hscan](#7. hscan)
- [8. hlen](#8. hlen)
- [9. hsetnx](#9. hsetnx)
- [10. hincrby / hincrbyfloat](#10. hincrby / hincrbyfloat)
- 典型业务场景:对象存储
- [三、List 列表](#三、List 列表)
-
- 底层内部编码
- 核心命令详解
-
- [1. lpush / lpushx / rpush /rpushx](#1. lpush / lpushx / rpush /rpushx)
- [2. lrange](#2. lrange)
- [3. lpop / rpop](#3. lpop / rpop)
- [4. lindex](#4. lindex)
- [5. linsert](#5. linsert)
- [6. lset](#6. lset)
- [7. llen](#7. llen)
- [8. blpop / brpop](#8. blpop / brpop)
- [9. lrem](#9. lrem)
- [10. ltrim](#10. ltrim)
- 典型业务场景
- 使用注意事项
- [四、Set 集合](#四、Set 集合)
-
- 底层内部编码
- 核心命令详解
-
- [1. sadd / srem](#1. sadd / srem)
- [2. smove](#2. smove)
- [3. smembers / sismember / scard](#3. smembers / sismember / scard)
- [4. spop / srandmember](#4. spop / srandmember)
- [5. sinter / sunion / sdiff](#5. sinter / sunion / sdiff)
- [6. sinterstore / sunionstore / sdiffstore](#6. sinterstore / sunionstore / sdiffstore)
- 典型业务场景
- [五、ZSet 有序集合](#五、ZSet 有序集合)
-
- 底层内部编码
- 核心命令详解
-
- [1. zadd / zincrby](#1. zadd / zincrby)
- [2. zcard / zcount](#2. zcard / zcount)
- [3. zrange / zrevrange / zrangebyscore](#3. zrange / zrevrange / zrangebyscore)
- [4. zrank / zrevrank / zscore](#4. zrank / zrevrank / zscore)
- [5. zrem / zremrangebyrank / zremrangebyscore](#5. zrem / zremrangebyrank / zremrangebyscore)
- [6. zpopmax / zpopmin](#6. zpopmax / zpopmin)
- [7. bzpopmax / bzpopmin](#7. bzpopmax / bzpopmin)
- [8. zinterstore/ zunionstore](#8. zinterstore/ zunionstore)
- 典型业务场景
- 使用注意事项
- 六、其他类型
- 五大数据类型对比
一、String 字符串
String是Redis最基础的数据类型,所有Redis的key本身都是字符串类型,其他四种数据结构内部存储的元素也全部都是字符串。
value支持:普通字符串、数字、JSON字符串、XML、二进制流(图片、音频);单value最大上限 512MB。
注意"
- String存大JSON,如果只需要修改对象中某一个字段,每次需要全量序列化反序列化,开销大,这种场景优先考虑Hash;
- Redis不做字符编码转换,存什么字节,取出来就是什么字节。
内部编码
- int:当value是8字节以内的长整型数字,使用int编码,直接存数字,内存开销最小。
- embstr:字符串长度 ≤39字节,使用embstr编码,一次性分配内存,减少内存碎片。
- raw:字符串长度 >39字节,使用raw编码。
小数直接按照字符串存储,因此小数计算时需要从字符串转换成小数,计算后再转回字符串,有一定的开销。
可以使用命令
object encoding key查看key当前底层编码。
核心命令详解
redis文档:
[]是一个独立的单元,表示可选项|表示或者的关系,只能选择一个;[]之间可以同时存在
1. set / get
bash
set key value [expiration EX seconds|PX milliseconds] [NX|XX]
get key
选项:
- EX:秒为单位的过期时间
- PX:毫秒为单位的过期时间;
- NX:key不存在才设置;key存在则返回nil
- XX:key存在则直接覆盖原有key,原TTL会清除;key不存在则返回nil
get命令用于获取key对应字符串,非string类型会报错
时间复杂度: O(1)
2. mset / mget
可以一次操作多个键值对,减少网络传输开销。但是也不能一次操作成千上万个key,会阻塞后续的命令
bash
mset key [key ...]
mget key [key ...]
时间复杂度: O(N) N为key数量
3. setnx
设置 key-value 但只允许在 key 之前不存在的情况下。
bash
SETNX key value
返回值
- 1:设置成功
- 0:没有设置
时间复杂度: O(1)
4. setex / psetex
- setex:设置 key-value 同时设置过期时间,单位为秒
- psetex:设置 key-value 同时设置过期时间,单位为毫秒
bash
setex key seconds value
psetex key milliseconds value
时间复杂度: O(1)
5. incr / decr / incrby / decrby / incrbyfloat
- incr / decr:对数值字符串+1/-1;key不存在则默认视为0;非数值字符串或范围超过64位有符号整型会报错。是原子操作,不需要加锁。
- incrby / decrby:对数值字符串+/-指定数值,key不存在则默认视为0;非数值字符串或范围超过64位有符号整型会报错。指定数值可以是正数,也可以是负数。
- incrbyfloat:对数值字符串+/- 浮点数
语法:
bash
incr key
decr key
incrby key increment
decrby key decrement
incrbyfloat increment
返回值: integer类型计算后的数值
时间复杂度: O(1)
6. append
在字符串末尾追加,key不存在时等价于set。
bash
append key value
返回值: 追加后String的长度,单位是字节。
时间复杂度: O(1)
redis不会对字符编码做任何处理,因此redis中的基本单位是字节而不是字符。在xshell终端一般以utf-8作为字符编码集,在utf-8中汉字一般占3个字节。
Java中String类型使用utf-8编码;char类型使用unicode编码,一般汉字占两个字节。
使用get命令获取汉字value时只能返回utf-8的二进制编码,而不是汉字,需要在启动redis客户端时加上--raw,能够使redis客户端自动对二进制数据尝试翻译。
7. getrange
返回 key 对应的 string 的子串,由 start 和 end 确定(左闭右闭)。
可以使用负数表示倒数。-1 代表倒数第一个字符,-2 代表倒数第二个,以此类推。超过范围的偏移量会根据 string 的长度调整成正确的值。
语法
bash
GETRANGE key start end
返回值: string 类型的子串
时间复杂度: O(N),N为start和end区间长度,一般string长度比较短,可以认为时间复杂度为O(1)
8. setrange
覆盖字符串的一部分,从指定的偏移开始。如果key不存在,则创建key,偏移量之前的字符都是0x00,之后为value的内容。
语法
bash
SETRANGE key offset value
时间复杂度:O(N), N 为 value 的长度. 由于一般给的 value 比较短, 通常视为 O(1).
返回值:替换后的 string 的长度。
9. strlen
获取 key 对应的 string 的长度,单位是字节。当 key 存放的类似不是 string 时,报错
语法
bash
strlen key
返回值: string 的长度。或者当 key 不存在时,返回 0。
时间复杂度: O(1)
示例:
bash
127.0.0.1:6379> get k3
中文字符
127.0.0.1:6379> strlen k3
12
典型业务场景
-
业务缓存 把MySQL查询结果缓存到Redis。通常JSON序列化存入String,设置EX过期时间。当缓存命中直接返回,未命中查数据库回写缓存。
- 伪代码逻辑:先get,命中直接返回;未命中查MySQL。
set key value ex 3600:设置过期时间,防止缓存数据长期不更新产生脏数据。
- 伪代码逻辑:先get,命中直接返回;未命中查MySQL。
-
计数器 视频播放量、阅读数、点赞数。在高并发场景中,
incr是原子操作,不会出现多线程竞争。Redis做实时计数,异步任务把计数落地到数据库,不直接用数据库做高并发计数。 -
分布式Session会话共享 传统单机web服务器session保存在本机内存;负载均衡多实例下用户请求会落到不同服务器,会反复登录。把session全部存入Redis,所有web节点统一读取Redis,可以实现会话共享。
-
短信验证码 + 限流 :限制1分钟内只能发五次验证码
- 用一个key存验证码,设置5分钟过期;
- 另一个key统计手机号发送次数,EX设置1分钟过期,超过阈值拒绝发送短信。
二、Hash 哈希
Hash结构:Redis外层key,内部包含多组 field‑value,类似Java HashMap。
Hash适合存储对象实体,可以只修改对象中某一个属性,不用操作整个对象 。
Hash只是数据结构,不是关系数据库,不能做多条件查询、联表查询。
底层内部编码
-
ziplist(压缩列表) 满足两个条件自动启用:
- hash-max-ziplist‑entries 默认512个field;
- 每个field/value的字节数小于 hash‑max‑ziplist‑value 默认64字节。 ziplist是连续内存存储,节省内存。这两个配置可以根据实际的业务场景更改。
-
hashtable(哈希表) 当field数量超过512,或者任意一个value大于64字节,自动切换为hashtable。底层真正哈希表,读写O(1),内存开销更大。
核心命令详解
1. hset
给 hash key 设置 field‑value。如果 key 不存在,新建 hash;如果 field 已经存在,覆盖旧值。
语法:
bash
hset key field value [field value ...]
返回值:新增字段的个数,覆盖已有字段返回 0。
时间复杂度: O(N) N为插入feild的数量
2. hget
获取哈希表中指定 field 对应的值。
语法:
bash
hget key field
返回值:返回 field 对应的 value;key 不存在或者 field 不存在返回 nil。
时间复杂度: O(1)
3. hexists
判断哈希里面某个 field 是否存在。
语法:
bash
hexists key field
返回值:存在返回 1;key 不存在或者 field 不存在返回 0。
时间复杂度: O(1)
4. hdel
删除哈希表中一个或者多个指定 field。
语法:
bash
hdel key field [field ...]
返回值:成功删除的字段数量;key 不存在返回 0。
时间复杂度: O (N),N 为要删除的 field 个数
5. hkeys / hvals
获取指定key中全部字段名 / 全部value,与keys类似,如果一次获取大量数据可能阻塞服务器。
语法:
bash
HKEYS key
HVALS key
时间复杂度: O(N),N为feild的个数
示例:
bash
127.0.0.1:6379> hset hash f1 v1
(integer) 1
127.0.0.1:6379> hset hash f2 v2 f3 v3 f4 v4 f5 v5
(integer) 4
127.0.0.1:6379> hkeys hash
1) "f1"
2) "f2"
3) "f3"
4) "f4"
5) "f5"
127.0.0.1:6379> hvals hash
6) "v1"
7) "v2"
8) "v3"
9) "v4"
10) "v5"
6. hgetall / hmget
- hgetall:获取指定 key 中的所有feild-value。
- hmget:批量获取多个field
使用hgetall命令时,field很多会阻塞Redis,生产谨慎使用。可以改用hscan渐进遍历
bash
HGETALL key
HMGET key field [field ...]
**时间复杂度:**O(N), N 为查询元素个数.
示例:
bash
127.0.0.1:6379> hset hash f1 v1
(integer) 1
127.0.0.1:6379> hset hash f2 v2 f3 v3 f4 v4 f5 v5
(integer) 4
127.0.0.1:6379> hgetall hash
1) "f1"
2) "v1"
3) "f2"
4) "v2"
5) "f3"
6) "v3"
7) "f4"
8) "v4"
9) "f5"
10) "v5"
127.0.0.1:6379> hmget hash f1 f2 f3
1) "v1"
2) "v2"
3) "v3"
7. hscan
渐进式遍历,每次只遍历一部分,连续执行多次可以完成整个遍历过程。可以避免一次执行太多操作导致阻塞服务器
语法:
bash
hscan key cursor [MATCH pattern] [COUNT count]
cursor:游标,第一次遍历传0 ;返回结果里拿到新游标,把新游标作为下一次hscan的cursor;游标返回0代表遍历完毕MATCH pattern:可选,对field做通配符过滤,不是客户端过滤,是服务器端过滤;⚠️注意:匹配是在遍历之后做的,COUNT不代表匹配结果数量COUNT count:可选,提示redis尽量返回多少个元素,只是提示值,不是强制数量
返回值:第一部分是下一轮游标;第二部分是本次迭代拿到的field‑value列表。key不存在返回游标0+空列表。
时间复杂度:单轮O(1);完整遍历整个hash整体O(N),N为hash内field总数
8. hlen
获取hash里面field的总数量
语法:
bash
HLEN key
返回值: 字段个数
时间复杂度: O(1)
9. hsetnx
在字段不存在的情况下,设置 hash 中的字段和值。
语法:
bash
HSETNX key field value
返回值
- 1:设置成功
- 0:没有设置
时间复杂度: O(1)
10. hincrby / hincrbyfloat
对hash内部某个field做原子自增
语法
bash
1 HINCRBY key field increment
HINCRBYFLOAT key field increment
返回值: 该字段变化之后的值
时间复杂度: O(1)
典型业务场景:对象存储
假设存储一张UserInfo表:
- 多个独立String :
user:1:name、user:1:age- 优点:修改单个属性简单;
- 缺点:key数量爆炸,对象数据分散,管理麻烦,很少生产使用。
- String + JSON序列化 :
set user:1 "{\"name\":\"xxx\",\"age\":18}"- 优点:编码简单;
- 缺点:修改其中一个字段,必须取出完整JSON →反序列化 →修改 →序列化 →重新set;全部字段都要操作,开销大。
- Hash存储 :
hmset user:1 name xxx age 18- 优点:可以hget单独拿某个字段,hset单独更新某个字段,不需要操作整个对象。
- 缺点:受ziplist条件限制,如果字段过多、value很大,底层转为hashtable,内存消耗上升。
对象字段不是特别多,优先Hash;对象整体读取远大于局部修改,优先String+JSON。
注意:
- hgetall 在field数量巨大的时候会阻塞Redis,线上优先hmget指定需要字段,或者hscan迭代遍历;
- Hash没有设置整体过期时间的直接命令!只能对整个hash的外层key设置expire;不能单独给hash里面某一个field设置过期时间。如果需要field级别过期,只能自己业务层实现。
三、List 列表
List是有序、允许元素重复的字符串列表。可以在两端快速插入删除;既可以当栈,又可以当队列。
!\[Pasted image 20260922154935.png]
下标从0开始,支持负数下标,‑1代表最后一个元素。
底层内部编码
-
ziplist 压缩列表 元素数量小于
list‑max‑ziplist‑entries(默认512),每个元素字节小于list‑max‑ziplist‑value(默认64字节),使用ziplist,连续内存节省空间。 -
linkedlist 双向链表 超过上面阈值,切换双向链表;链表头尾操作O(1),访问中间下标元素需要遍历,性能差。
Redis3.2之后list底层使用quicklist,结合ziplist和linkedlist两者优势。
核心命令详解
1. lpush / lpushx / rpush /rpushx
- lpush:将一个或者多个元素从左侧放入(头插)到 list 中。
- rpush:将一个或者多个元素从右侧放入(尾插)到 list 中
- lpushx / rpushx:当key存在时才允许插入,不存在时直接返回
Redis 没有显式 create 命令,List 键是插入元素的时候自动创建;清空所有元素后键会自动消失。
语法:
bash
LPUSH key element [element ...]
LPUSHX key element [element ...]
RPUSH key element [element ...]
RPUSHX key element [element ...]
时间复杂度: O(N), N 为插入元素个数.
返回值: 插入后 list 的长度。
2. lrange
获取从 start 到 end 区间的所有元素,左闭右闭。支持负数下标
bash
LRANGE key start stop
时间复杂度:O(N) 返回值:指定区间的元素。
如果下标非法,只会返回合法范围内的数据,不会直接报错,这也体现了redis具有很好的鲁棒性,容错能力强。
3. lpop / rpop
头部/尾部弹出元素;
语法:
bash
LPOP key
RPOP key
返回值: 取出的元素;key不存在时返回nil。
时间复杂度: O(1)
4. lindex
按下标获取元素;访问中间元素性能较差
语法:
bash
LINDEX key index
返回值: 取出的元素;key不存在时返回nil
时间复杂度: O(N) N表示列表长度
5. linsert
在指定的元素前或后插入元素,基准值为列表中从左往右第一个元素
语法:
bash
LINSERT key pivot element
时间复杂度: O(N) N表示列表长度
返回值:插入后的 list 长度。
6. lset
根据下标修改元素
语法:
bash
LSET key index element
时间复杂度: O(N) N为列表长度
如果下标非法会直接报错,不会像lrange处理非法下标
7. llen
获取list长度
语法:
bash
LLEN key
时间复杂度: O(1)
8. blpop / brpop
阻塞弹出。当列表没有数据,客户端阻塞等待timeout秒;timeout=0无限等待。
阻塞期间redis可以执行其他命令。
这两个命令支持监听多个list key,当多个list全部为空时等待timeout秒,任意一个有值就可以立即返回
bash
BLPOP key [key ...] timeout
BRPOP key [key ...] timeout
时间复杂度: O(1)
返回值: 对于非空队列,返回一个二元组,包括key和弹出的元素;
9. lrem
删除列表指定value元素
- count > 0:从左开始删除count个
- count < 0:从右开始删除|count|个
- count = 0:全部删除
语法:
bash
lrem key count value
时间复杂度: O(N+M) N为列表长度,M为待删除元素个数
10. ltrim
保留区间内元素,其余全部删除,用于限制list最大长度
语法:
bash
ltrim key start stop
典型业务场景
-
简易阻塞消息队列 生产者执行
lpush queue msg;消费者执行brpop queue 0阻塞等待消息。- 缺点:没有消息确认ACK机制,消费者宕机消息直接丢失;不适合可靠性要求极高业务,高可靠请使用RabbitMQ、Kafka。适合日志、通知等允许少量丢失业务。
-
时间线/信息流(微博、朋友圈动态)
- 用户发布动态:
lpush user:1:timeline mblog_id,新的消息插入列表头部; - 分页读取:
lrange user:1:timeline 0 9,拿前10条动态。
- 用户发布动态:
-
栈和队列模拟
- 栈(后进先出):
lpush + lpop - 普通队列(先进先出):
lpush + rpop
- 栈(后进先出):
使用注意事项
- List适合头尾增删,不要频繁访问中间下标,性能会退化;
- blpop是阻塞命令,但Redis服务端不会阻塞,只是客户端等待;Redis单线程仍然可以处理别的请求;
- ltrim可以用来截断列表,防止list无限膨胀占用大量内存。
四、Set 集合
Set集合:无序,元素不可重复;存入重复元素会自动去重。最强大能力是支持交集、并集、差集等集合运算。集合内部全部是字符串元素,集合中的元素称为member。
底层内部编码
- intset整数集合 :集合全部是整数,并且元素数量小于
set‑max‑intset‑entries默认512,使用intset,内存占用很小。 - hashtable哈希表:只要出现非整数元素,或者元素超过512,切换hashtable。
核心命令详解
1. sadd / srem
向集合添加或删除一个或多个元素。集合本身无序且不重复。
sadd遇到已存在的元素自动忽略;srem遇到不存在的元素也自动忽略- key 不存在时
sadd会先隐式创建、srem返回 0。
bash
SADD key member [member ...]
SREM key member [member ...]
时间复杂度: O(N),N 为本次操作的元素个数
返回值: 真正新增 / 真正删除的元素个数
2. smove
把一个元素从 source 集合转移到 destination 集合,是一个原子操作,相当于"先 srem 再 sadd",但过程中不会出现元素丢失或重复的中间状态,适合做"待处理队列 → 已完成集合"的搬运。
若元素不在 source 中则不做任何修改;destination 已存在该元素时会被自动忽略(集合去重),但 source 中仍会被移除。
bash
SMOVE source destination member
时间复杂度: O(1)
返回值: 1 表示移动成功,0 表示元素不在 source 中(未做任何移动)
补充:source 与 destination 相同时,只要元素在集合中即返回 1,不会产生变化;source 中元素被移空后该 key 会被删除。
3. smembers / sismember / scard
smembers一次返回全部元素,元素量大时会阻塞 Redis 主线程并占用带宽,生产环境优先用SSCAN增量遍历;sismember判断某元素是否为成员,返回 1 存在、0 不存在;scard返回集合元素个数(基数),不返回元素内容,开销最小。
bash
SMEMBERS key
SISMEMBER key member
SCARD key
时间复杂度:
- SMEMBERS O(N)
- SISMEMBER O(1)
- SCARD O(1)
补充:判断"集合是否为空"用
SCARD key而不建议用SMEMBERS后判长度,可避免把整个集合拉到客户端。
4. spop / srandmember
两者都做随机抽取,区别在是否删除
spop随机弹出一个/多个元素并从集合中移除,真正的抽奖、发奖场景;srandmember只读取不删除,可用于"预览中奖结果"或"同一批奖品反复抽"。
srandmember 的 count 为负数时允许返回重复元素,spop 的 count 超过集合大小时会取出全部元素。
bash
SPOP key [count]
SRANDMEMBER key [count]
时间复杂度: O(N),N 为返回的元素个数(count 省略时为 O(1))
返回值: 省略 count 返回单个元素;指定 count 返回元素数组;集合为空时返回 nil / 空数组
5. sinter / sunion / sdiff
对多个集合做交集、并集、差集,结果直接返回给客户端,不落盘,不会新建一个key存储
sinter取所有集合共同存在的元素,适合共同好友、共同关注业务场景;sunion取全部不重复元素;sdiff取A集合存在且B集合不存在的元素
bash
SINTER key [key ...]
SUNION key [key ...]
SDIFF key [key ...]
时间复杂度:
- SINTER O(M×N),M 为最大的集合元素个数,N为最小的集合元素个数
- SUNION O(N)
- SDIFF O(N)
6. sinterstore / sunionstore / sdiffstore
与上面命令语义完全一致,唯一区别是把结果写入一个新的 destination key 而不返回全部元素,避免大量数据经网络传输到客户端。
destination 已存在会被先清空覆盖;若要拿到结果内容,落盘后再分页读取。
bash
SINTERSTORE destination key [key ...]
SUNIONSTORE destination key [key ...]
SDIFFSTORE destination key [key ...]
时间复杂度: O(N)
返回值: 结果集合的元素个数
典型业务场景
- 标签系统 用户打标签,
sadd user:1:tags 体育 电影 音乐; 求两个用户共同兴趣标签:sinter user:1:tags user:2:tags。 - 社交关系(粉丝、关注)
- user:1:follow 存放用户1关注的人;
- user:1:fans存放用户1粉丝;
- 共同关注:
sinter user:1:follow user:2:follow。
- 去重统计 统计页面独立访客UV,把访问用户id sadd存入set,scard直接拿到去重数量。
- 抽奖活动 spop随机从集合取出用户,取出即代表中奖,自动去重,同一个用户不会多次中奖。
五、ZSet 有序集合
ZSet = Set(成员不可重复)+ 分数score排序。每个成员对应一个double类型score;成员唯一,score允许重复。集合按照score自动排序,score相同情况下,按照成员字符串字典序排序。
member和score的概念与键值对不完全相同:键值对的角色是固定的,一般情况根据键去找值,是单项的查找方向;而member与score类似pair的关系,二者角色不固定,都可以根据自己的值找到另一个值。
底层内部编码
- ziplist压缩列表 :元素数量小于
zset‑max‑ziplist‑entries默认128,每个member小于64字节,使用ziplist节省内存。 - skiplist跳表:超过阈值切换跳表;跳表可以快速范围查找、范围排序,时间复杂度O(logN)。
核心命令详解
1. zadd / zincrby
zadd用于新增成员并指定分数,或修改已有成员的分数zincrby对成员分数做原子加减,成员不存在时按 0 起算。适合点赞数、热度值累加等业务场景,
zadd 参数:
NX:只新增、已存在则不更新XX:只更新、不存在则不新增GT/LT:仅在更大/更小时才更新CH:返回变更个数而非新增个数INCR:把 zadd 当 zincrby 用;
bash
ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...]
ZINCRBY key increment member
时间复杂度: O(logN),N 为有序集合元素个数(一次命令写入多个成员时为 M×logN)
返回值:
- ZADD 返回新增(或 CH 时变更)的成员个数,带 INCR 时返回新分数;
- ZINCRBY 返回增加后的新分数
2. zcard / zcount
只返回数量、不返回成员,适合"榜单规模""分数达标人数""区间分布"这类统计需求。
zcard统计整个有序集合的元素总数,相当于不加区间限制;zcount统计分数落在[min, max]区间内的成员数,min/max 支持-inf/+inf(正无穷/负无穷),默认闭区间,写成(1、(1.5表示开区间。两端都是开区间要写成(min (max
bash
ZCARD key
ZCOUNT key min max
时间复杂度: 均为 O(logN)
zcard维护了长度计数(实际近乎 O(1))zcount靠两次分数定位相减:先根据max和min找到对应的元素,
返回值: 符合条件的成员个数(key 不存在返回 0)
ZLEXCOUNT key min max是同一思路的字典序版本(配合所有成员 score 相同使用);判断"集合是否为空"用ZCARD比先ZRANGE再判长度更省资源。
3. zrange / zrevrange / zrangebyscore
zrange:按下标升序取[start, stop]区间zrevrange:反向降序取(TopN 最常用),两者下标支持负数(-1表示最后一个);zrangebyscore:按分数区间过滤,与zcount作用类似。支持-inf/+inf开闭区间写法与LIMIT offset count分页;
适合读取排行榜和榜单元数据等业务场景
bash
ZRANGE key start stop [WITHSCORES]
ZREVRANGE key start stop [WITHSCORES]
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
时间复杂度: O(logN + M),N 为集合大小,M 为返回的元素个数
返回值: 成员数组;带 WITHSCORES 时为"成员, 分数, 成员, 分数..."交替的数组;区间内无元素返回空数组
Redis 6.2 起推荐统一使用
ZRANGE key start stop BYSCORE|BYLEX [REV] [LIMIT ...],zrangebyscore/zrevrangebyscore属于旧写法但被完整保留。
4. zrank / zrevrank / zscore
定位某个成员当前所处的位置或它的分数,都是精确查询单个成员、不返回列表。
zrank取升序排名(从 0 开始),zrevrank取降序排名;zscore只取分数,成员不存在时排名返回 nil,分数返回 nil。
bash
ZRANK key member
ZREVRANK key member
ZSCORE key member
时间复杂度:
- ZRANK / ZREVRANK:O(logN);
- ZSCORE:O(1)
返回值: 排名返回整数下标(0 基),ZSCORE 返回分数(字符串形式的浮点数),成员不存在返回 nil
Redis 7.2 起
ZRANK key member WITHSCORE可在一次调用中同时拿到排名和分数;排名是 0 基,展示给用户时通常加 1。
5. zrem / zremrangebyrank / zremrangebyscore
zrem:按成员名删除一个或多个成员;zremrangebyrank:按下标区间批量删除(如"只保留前 100 名")zremrangebyscore:按分数区间批量删除(如"清理 7 天前的时间戳分数")。批量区间删除适合做榜单截断和历史数据清理,避免逐条zrem产生大量往返。
bash
ZREM key member [member ...]
ZREMRANGEBYRANK key start stop
ZREMRANGEBYSCORE key min max
时间复杂度: ZREM O(M×logN);ZREMRANGEBYRANK / ZREMRANGEBYSCORE O(logN + M),M 为被删除的元素个数
返回值: 整数------实际删除的成员个数(key 不存在时为 0)
删除后若有序集合为空,对应的 key 会被自动删除。
6. zpopmax / zpopmin
删除并返回有序集合中分数最高(最低)的一个或 count 个成员,count 省略时返回单个成员(包括member和score),key 不存在返回空数组。
删除的数量严格按照count,如果count为1,存在多个最大score的member,会按照字典序删除第一个。
bash
ZPOPMAX key [count]
ZPOPMIN key [count]
时间复杂度: O(logN × M),N 为集合大小、M 为弹出的成员个数(count 省略时即 O(logN))
返回值: 扁平数组 [member, score, member, score, ...] 不是嵌套二元组
7. bzpopmax / bzpopmin
zpopmax / zpopmin 的阻塞版本。当所有指定 key 都不存在或为空时,客户端阻塞等待,直到某个 key 有成员被写入或超时。
语义与列表的 BLPOP 一致,适合"实时抢单 / 异步派奖 / 事件驱动消费"场景------消费者挂在空集合上等,生产者一 zadd 就立即被唤醒,避免了空轮询。
阻塞期间 Redis 仍可处理其他命令;timeout=0 表示无限等待,单位是秒,Redis 6.0 起支持毫秒(小数)。支持一次监听多个 key,任意一个集合有成员即立即返回。
bash
BZPOPMAX key [key ...] timeout
BZPOPMIN key [key ...] timeout
时间复杂度: O(logN) 有成员时立即弹出并返回,因此与数量无关
返回值: 三元组 [key, member, score]。与 ZPOPMAX 的扁平数组不同,多了一个 key 字段,用于区分是哪个集合被弹出了元素;超时则返回空数组/nil
BZPOPMAX中key是弹出集合的名字、member是被弹出的成员、score是其分数,取值时注意别按ZPOPMAX的二元格式错位解析。要想在阻塞版里一次拿多个成员,需要循环调用,因为阻塞版不支持count。
8. zinterstore/ zunionstore
对多个有序集合求交集或并集,并把结果写入 destination key。
numkeys:参与运算的key的个数,防止key与后面参数混淆(与http协议Header中通过Content-Length规定正文长度的原理类似)WEIGHTS:为每个输入集合指定乘法权重AGGREGATE SUM|MIN|MAX:指定同一成员在多集合中出现时的分数聚合规则(默认 SUM)。
与集合类似,落盘避免了大量结果经网络传输,且结果天然按 score 排好序。
bash
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
时间复杂度: O(N×K) + O(M×logM),N 为最小集合的大小、K 为集合个数、M 为结果元素个数(通常简写为 O(NlogN) 量级)
返回值: 结果集合的成员个数(destination 已存在会被先清空覆盖)
,Redis 6.2+ 可用
ZINTER/ZUNION直接返回结果,不落盘。
典型业务场景
-
各类排行榜系统 视频点赞榜、游戏积分排行榜、热度榜单。
- 用户获得点赞:
zincrby rank:20260918 1 user_id; - 查询热度前10名:
zrevrange rank:20260918 0 9 WITHSCORES.
- 用户获得点赞:
-
延时队列 把任务到期时间戳作为score,任务ID作为member;后台线程轮询zrangebyscore获取score小于当前时间戳的任务,执行任务。
-
权重优先级队列 score代表优先级数值,分数越高优先级越高,读取使用zrevrange拿到高优先级任务。
使用注意事项
- zadd、zincrby都是O(logN),性能很好,但大量数据写入也要控制QPS;
- score是double浮点数,有精度丢失风险,不要存超长整型;
- zrange/zrevrange做top‑N非常适合,但不要一次性取出全部数据,大数据使用zscan遍历。
六、其他类型
除了以上介绍的数据类型,redis还提供了Stream、Geospetial、HyperLogLog、Bitmaps、Bitfeilds这些类型适合在特定的场景中使用。
- Stream:Redis 自带的可靠消息队列。消息存硬盘,消息丢了能重读,支持分组消费,消息处理完再标记完成。适合存储事件。
- GEO 地理信息:存储经纬度坐标,能算两个地点之间多远。底层就是有序集合。
- HyperLogLog:通过位操作估算集合中元素个数。适合粗略统计有多少不同的人访问页面(UV)。不存用户明细,非常省内存,但数字有误差。
- Bitmap 位图:用一个个比特位记录是否签到 / 是否在线,只有 0 和 1,非常省空间。
- Bitfield:也是操作字符串里的比特。Bitmap 只能一次改 1 个 bit;Bitfield 可以一次性读 / 写多个 bit 组成的小数字,打包存一堆小整数。
五大数据类型对比
| 数据结构 | 核心特点 | 底层编码 | 最佳业务场景 | 避坑提醒 |
|---|---|---|---|---|
| String | 通用key‑value,支持原子数字操作 | int、embstr、raw | 缓存、计数器、session、验证码、简单JSON对象 | 修改对象单个字段开销大;不适合超大value |
| Hash | key内部包含多个field‑value,局部更新字段 | ziplist、hashtable | 用户/商品对象,需要频繁修改部分属性 | 不能给单个field设置过期;field数量巨大慎用hgetall |
| List | 有序,允许重复;头尾操作快,中间慢 | ziplist / quicklist | 简易消息队列、时间线、栈、队列 | 不要频繁访问中间下标;消息无ACK,不可靠 |
| Set | 无序,自动去重,支持集合运算 | intset、hashtable | 标签、共同好友、UV去重、抽奖 | 无序;大数据量不要用smembers,用sscan |
| ZSet | 成员唯一,按score自动排序 | ziplist、skiplist | 排行榜、优先级队列、延时任务 | score浮点数存在精度丢失风险 |