【Redis】Redis五大数据类型

文章目录

  • [一、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。

注意"

  1. String存大JSON,如果只需要修改对象中某一个字段,每次需要全量序列化反序列化,开销大,这种场景优先考虑Hash;
  2. Redis不做字符编码转换,存什么字节,取出来就是什么字节。

内部编码

  1. int:当value是8字节以内的长整型数字,使用int编码,直接存数字,内存开销最小。
  2. embstr:字符串长度 ≤39字节,使用embstr编码,一次性分配内存,减少内存碎片。
  3. 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

典型业务场景

  1. 业务缓存 把MySQL查询结果缓存到Redis。通常JSON序列化存入String,设置EX过期时间。当缓存命中直接返回,未命中查数据库回写缓存。

    • 伪代码逻辑:先get,命中直接返回;未命中查MySQL。set key value ex 3600:设置过期时间,防止缓存数据长期不更新产生脏数据。
  2. 计数器 视频播放量、阅读数、点赞数。在高并发场景中,incr是原子操作,不会出现多线程竞争。Redis做实时计数,异步任务把计数落地到数据库,不直接用数据库做高并发计数。

  3. 分布式Session会话共享 传统单机web服务器session保存在本机内存;负载均衡多实例下用户请求会落到不同服务器,会反复登录。把session全部存入Redis,所有web节点统一读取Redis,可以实现会话共享。

  4. 短信验证码 + 限流 :限制1分钟内只能发五次验证码

    1. 用一个key存验证码,设置5分钟过期;
    2. 另一个key统计手机号发送次数,EX设置1分钟过期,超过阈值拒绝发送短信。

二、Hash 哈希

Hash结构:Redis外层key,内部包含多组 field‑value,类似Java HashMap。

Hash适合存储对象实体,可以只修改对象中某一个属性,不用操作整个对象 。

Hash只是数据结构,不是关系数据库,不能做多条件查询、联表查询。

底层内部编码

  1. ziplist(压缩列表) 满足两个条件自动启用:

    • hash-max-ziplist‑entries 默认512个field;
    • 每个field/value的字节数小于 hash‑max‑ziplist‑value 默认64字节。 ziplist是连续内存存储,节省内存。这两个配置可以根据实际的业务场景更改。
  2. 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表:

  1. 多个独立String :user:1:name、user:1:age
    • 优点:修改单个属性简单;
    • 缺点:key数量爆炸,对象数据分散,管理麻烦,很少生产使用。
  2. String + JSON序列化 :set user:1 "{\"name\":\"xxx\",\"age\":18}"
    • 优点:编码简单;
    • 缺点:修改其中一个字段,必须取出完整JSON →反序列化 →修改 →序列化 →重新set;全部字段都要操作,开销大。
  3. Hash存储 :hmset user:1 name xxx age 18
    • 优点:可以hget单独拿某个字段,hset单独更新某个字段,不需要操作整个对象。
    • 缺点:受ziplist条件限制,如果字段过多、value很大,底层转为hashtable,内存消耗上升。

对象字段不是特别多,优先Hash;对象整体读取远大于局部修改,优先String+JSON。

注意:

  1. hgetall 在field数量巨大的时候会阻塞Redis,线上优先hmget指定需要字段,或者hscan迭代遍历;
  2. Hash没有设置整体过期时间的直接命令!只能对整个hash的外层key设置expire;不能单独给hash里面某一个field设置过期时间。如果需要field级别过期,只能自己业务层实现。

三、List 列表

List是有序、允许元素重复的字符串列表。可以在两端快速插入删除;既可以当栈,又可以当队列。

!\[Pasted image 20260922154935.png]

下标从0开始,支持负数下标,‑1代表最后一个元素。

底层内部编码

  1. ziplist 压缩列表 元素数量小于 list‑max‑ziplist‑entries(默认512),每个元素字节小于list‑max‑ziplist‑value(默认64字节),使用ziplist,连续内存节省空间。

  2. 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

典型业务场景

  1. 简易阻塞消息队列 生产者执行 lpush queue msg;消费者执行 brpop queue 0 阻塞等待消息。

    • 缺点:没有消息确认ACK机制,消费者宕机消息直接丢失;不适合可靠性要求极高业务,高可靠请使用RabbitMQ、Kafka。适合日志、通知等允许少量丢失业务。
  2. 时间线/信息流(微博、朋友圈动态)

    • 用户发布动态:lpush user:1:timeline mblog_id,新的消息插入列表头部;
    • 分页读取:lrange user:1:timeline 0 9,拿前10条动态。
  3. 栈和队列模拟

    • 栈(后进先出):lpush + lpop
    • 普通队列(先进先出):lpush + rpop

使用注意事项

  1. List适合头尾增删,不要频繁访问中间下标,性能会退化;
  2. blpop是阻塞命令,但Redis服务端不会阻塞,只是客户端等待;Redis单线程仍然可以处理别的请求;
  3. ltrim可以用来截断列表,防止list无限膨胀占用大量内存。

四、Set 集合

Set集合:无序,元素不可重复;存入重复元素会自动去重。最强大能力是支持交集、并集、差集等集合运算。集合内部全部是字符串元素,集合中的元素称为member。

底层内部编码

  1. intset整数集合 :集合全部是整数,并且元素数量小于 set‑max‑intset‑entries 默认512,使用intset,内存占用很小。
  2. 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)

返回值: 结果集合的元素个数

典型业务场景

  1. 标签系统 用户打标签,sadd user:1:tags 体育 电影 音乐; 求两个用户共同兴趣标签:sinter user:1:tags user:2:tags。
  2. 社交关系(粉丝、关注)
    • user:1:follow 存放用户1关注的人;
    • user:1:fans存放用户1粉丝;
    • 共同关注:sinter user:1:follow user:2:follow。
  3. 去重统计 统计页面独立访客UV,把访问用户id sadd存入set,scard直接拿到去重数量。
  4. 抽奖活动 spop随机从集合取出用户,取出即代表中奖,自动去重,同一个用户不会多次中奖。

五、ZSet 有序集合

ZSet = Set(成员不可重复)+ 分数score排序。每个成员对应一个double类型score;成员唯一,score允许重复。集合按照score自动排序,score相同情况下,按照成员字符串字典序排序。

member和score的概念与键值对不完全相同:键值对的角色是固定的,一般情况根据键去找值,是单项的查找方向;而member与score类似pair的关系,二者角色不固定,都可以根据自己的值找到另一个值。

底层内部编码

  1. ziplist压缩列表 :元素数量小于 zset‑max‑ziplist‑entries 默认128,每个member小于64字节,使用ziplist节省内存。
  2. 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 直接返回结果,不落盘。

典型业务场景

  1. 各类排行榜系统 视频点赞榜、游戏积分排行榜、热度榜单。

    • 用户获得点赞:zincrby rank:20260918 1 user_id;
    • 查询热度前10名:zrevrange rank:20260918 0 9 WITHSCORES.
  2. 延时队列 把任务到期时间戳作为score,任务ID作为member;后台线程轮询zrangebyscore获取score小于当前时间戳的任务,执行任务。

  3. 权重优先级队列 score代表优先级数值,分数越高优先级越高,读取使用zrevrange拿到高优先级任务。

使用注意事项

  1. zadd、zincrby都是O(logN),性能很好,但大量数据写入也要控制QPS;
  2. score是double浮点数,有精度丢失风险,不要存超长整型;
  3. zrange/zrevrange做top‑N非常适合,但不要一次性取出全部数据,大数据使用zscan遍历。

六、其他类型

除了以上介绍的数据类型,redis还提供了Stream、Geospetial、HyperLogLog、Bitmaps、Bitfeilds这些类型适合在特定的场景中使用。

  1. Stream:Redis 自带的可靠消息队列。消息存硬盘,消息丢了能重读,支持分组消费,消息处理完再标记完成。适合存储事件。
  2. GEO 地理信息:存储经纬度坐标,能算两个地点之间多远。底层就是有序集合。
  3. HyperLogLog:通过位操作估算集合中元素个数。适合粗略统计有多少不同的人访问页面(UV)。不存用户明细,非常省内存,但数字有误差。
  4. Bitmap 位图:用一个个比特位记录是否签到 / 是否在线,只有 0 和 1,非常省空间。
  5. 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浮点数存在精度丢失风险
相关推荐
java_nnnn1 小时前
Linux 学习第二篇:从零认识 Linux 文件系统、目录结构与基本命令
linux·运维·学习·1024程序员节
一尺之棰1 小时前
DSP-定时器原理
1024程序员节
全栈小51 小时前
【C#】体验和使用墨衍MoGrow平台写一篇技术总结文章,提供C#,Quart.Net,统一捕获异常,定时任务关键词,看看出来效果如何
1024程序员节
蹊黎1 小时前
C++中stack、queue、priority queue类的部分简单实现
数据结构·c++·visualstudio·1024程序员节
hixiong1231 小时前
纯C# Paddle2Onnx 转换器
1024程序员节
愤怒火龙果1 小时前
AIShield 靶场 WP
人工智能·网络安全·1024程序员节
软件派1 小时前
Eden AI 保姆级介绍:一站式 AI 聚合网关,一个 API 调用上百种大模型与视觉语音能力
1024程序员节·统一 api·多模态 api
ai_coder_ai1 小时前
论平台工程(平台化架构)在云原生系统中的设计与实践
1024程序员节
惊讶的猫1 小时前
mysql索引
mysql·1024程序员节