浅谈 Redis 数据类型

浅谈 Redis 数据类型

(一)String 类型

Redis 的 String 类型 是二进制安全的,可以用来存储 文本字符串、int 类型数据和 bitmap 位图 等数据。

1. 字符串操作

  • 适用于存储 文本、JSON、序列化数据 等任意二进制安全的内容

    命令 作用 示例
    SET 设置键值 SET name "Alice"
    GET 获取值 GET name"Alice"
    APPEND 追加内容 APPEND name " Smith""Alice Smith"
    STRLEN 获取字节长度 STRLEN name11
    GETRANGE 截取子串 GETRANGE name 0 4"Alice"
    SETRANGE 覆盖部分内容 SETRANGE name 6 "Taylor""Alice Taylor"
    MSET/MGET 批量操作 MSET k1 "v1" k2 "v2"
  • 二进制安全:可存储图片、序列化对象等任意数据

  • 自动编码 :短字符串用 embstr 编码(内存连续),长字符串用 raw 编码

SETNX 操作实现分布式锁

复制代码
SETNX key value
  • key 不存在 时,设置其值为 value,并返回 OK(成功);若 key 已存在,则不做任何操作,返回 nil(失败)

  • 为防止锁持有者崩溃后锁无法释放,需设置超时(通过 EXPIRE

  • 任务完成后,主动删除键以释放锁(通过 DEL

  • **【风险1】**若 SETNX 成功,但 EXPIRE 未执行(如客户端崩溃),锁会永久占用

    【解决方案】使用 ​SET 命令的 NXEX 选项​​ 原子性加锁和设置过期时间

    复制代码
    SET key value NX EX 10      # 原子操作:仅当不存在时设置,并过期时间为 10 秒
  • **【风险2】**任务未在过期时间内完成,锁被提前释放

    **【解决方案】**看门狗机制:启动后台线程定期续期

    复制代码
    EVAL "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('EXPIRE', KEYS[1], ARGV[2]) else return 0 end" 1 key value 10         # 每 10 秒执行一次该 LUA 脚本以续期 

2. 数值操作

  • 当 String 的值是 整数或浮点数 时,Redis 提供专用命令进行原子增减
命令 作用 示例
INCR 原子 +1 INCR counter1
INCRBY 原子 +N INCRBY counter 56
DECR 原子 -1 DECR counter5
DECRBY 原子 -N DECRBY counter 32
INCRBYFLOAT 原子 +浮点数 INCRBYFLOAT price 1.5"3.5"
  • 编码优化 :数值用 int 编码(固定 8 字节),浮点数用 raw
  • 原子性:无需事务即可避免并发冲突。

3. bitmap 位图操作

  • Bitmap 是 String 的扩展,通过 位(bit)操作 实现布尔统计,可用于布隆过滤器、用户状态统计等场景
命令 作用 示例
SETBIT 设置某位为 0/1 SETBIT login:2023 100 1(用户 100 登录)
GETBIT 获取某位的值 GETBIT login:2023 1001
BITCOUNT 统计 1 的数量 BITCOUNT login:202350(50 人登录)
BITOP 位运算(AND/OR/XOR) BITOP AND result login:day1 login:day2
BITPOS 查找第一个 0/1 位 BITPOS login:2023 1100
  • 内存高效:1 亿用户登录状态仅需约 12MB
  • 高性能:位运算复杂度 O(n),适合批量处理

4. 二进制安全与 Encoding 加速机制

  • Redis 的 String 类型基于简单动态字符串实现,数据按字节数组存储,不预设编码格式,读写时不会做任何转换
  • Redis 为 String 类型设计了多种编码格式(encoding),根据数据内容动态选择最优编码以节省内存和提高性能。通过 OBJECT ENCODING key 可查看编码类型
编码类型 适用场景 优化原理
int 存储 64 位有符号整数(如 123 直接使用整数存储,避免字符串转换。
embstr 短字符串(≤44 字节,Redis 7+) 内存连续分配,减少碎片,CPU 缓存友好。
raw 长字符串(>44 字节)或二进制数据 标准 SDS 动态分配,支持大容量数据。

(二) List 类型

Redis 的 List 类型 是一个双向链表数据结构,支持在头部和尾部高效插入、删除元素,因此可以灵活实现 栈(Stack)、队列(Queue)、数组(Array)阻塞队列(Blocking Queue) 的语义。

  • LPUSH + LPOP
  • 队列RPUSH + LPOP
  • 数组LINDEX + LSET
  • 阻塞队列RPUSH + BRPOP

以下是详细用法解析:

1. List 作为栈(Stack)

特点 :后进先出(LIFO),通过 LPUSH + LPOP 实现

​适用场景​ ​:函数调用栈、撤销操作(Undo)记录

​操作命令​​:

复制代码
# 入栈(左侧插入)
LPUSH stack "task1"
LPUSH stack "task2"  # 栈内顺序: ["task2", "task1"]

# 出栈(左侧弹出)
LPOP stack  # 返回 "task2",栈剩余: ["task1"]

# 查看栈顶元素(不弹出)
LINDEX stack 0  # 返回 "task1"

2. List 作为队列(Queue)

特点 :先进先出(FIFO),通过 RPUSH + LPOP 实现

​适用场景​ ​:任务队列、消息缓冲

​操作命令​​:

复制代码
# 入队(尾部插入)
RPUSH queue "msg1"
RPUSH queue "msg2"  # 队列顺序: ["msg1", "msg2"]

# 出队(头部弹出)
LPOP queue  # 返回 "msg1",队列剩余: ["msg2"]

# 查看队列长度
LLEN queue  # 返回 1

3. List 作为数组(Array)

特点 :支持按索引访问和修改,通过 LINDEX + LSET 实现

​适用场景​ ​:随机访问列表元素、动态数组

​操作命令​​:

复制代码
# 初始化数组
RPUSH array "a" "b" "c"  # 数组: ["a", "b", "c"]

# 按索引读取(索引从0开始)
LINDEX array 1  # 返回 "b"

# 按索引修改
LSET array 1 "B"  # 数组变为: ["a", "B", "c"]

# 获取全部元素
LRANGE array 0 -1  # 返回 ["a", "B", "c"]

4. List 作为阻塞队列(Blocking Queue)

特点 :消费者阻塞等待新元素,通过 BRPOP/BLPOP 实现

​适用场景​ ​:实时消息系统、任务调度(避免轮询)

​操作命令​​:

复制代码
# 生产者入队(尾部插入)
RPUSH bqueue "job1" "job2"

# 消费者阻塞出队(从头部获取,超时时间5秒)
BRPOP bqueue 5  
# 1) 如果队列不为空,立即返回元素(如 "job1")
# 2) 如果队列为空,阻塞 5 秒后返回nil(若期间有新元素插入则立即返回)

(三)Hash 类型

Redis 的 Hash 类型 是一个 键值对集合 ,适合存储对象。它比 String 类型更节省内存,且支持 字段级操作(单独读写某个字段而无需序列化整个对象)。

1. Hash 的底层结构

  • ziplist(压缩列表):当字段数和字段值较小时使用,内存连续,节省空间
  • hashtable(哈希表):字段较多或值较大时自动转换,查询效率 O(1)

2. 核心操作命令

(1)设置与获取字段值

命令 作用 示例
HSET 设置字段值 HSET user:1 name "Alice" age 30
HGET 获取字段值 HGET user:1 name"Alice"
HMSET 批量设置字段 HMSET user:1 name "Alice" age 30
HMGET 批量获取字段 HMGET user:1 name age["Alice", "30"]
HGETALL 获取所有字段和值 HGETALL user:1["name", "Alice", "age", "30"]
  • 示例

    HSET product:1001 name "iPhone" price 999 stock 50
    HGET product:1001 price # 返回 "999"
    HGETALL product:1001 # 返回所有字段和值

(2)字段增减与删除

命令 作用 示例
HINCRBY 字段值整数增减 HINCRBY user:1 age 1
HINCRBYFLOAT 字段值浮点数增减 HINCRBYFLOAT account:1 balance 50.5
HDEL 删除字段 HDEL user:1 age
HEXISTS 判断字段是否存在 HEXISTS user:1 name1(存在)
  • 示例

    HINCRBY product:1001 stock -1 # 库存减1
    HDEL product:1001 price # 删除价格字段

(3)查询与统计

命令 作用 示例
HKEYS 获取所有字段名 HKEYS user:1["name", "age"]
HVALS 获取所有字段值 HVALS user:1["Alice", "30"]
HLEN 获取字段数量 HLEN user:12
HSTRLEN 获取字段值的字节长度 HSTRLEN user:1 name5("Alice"占 5 字节)
  • 示例

    HKEYS product:1001 # 返回 ["name", "price", "stock"]
    HLEN product:1001 # 返回 3

(4)原子操作

命令 作用 示例
HSETNX 字段不存在时才设置 HSETNX user:1 email "[email protected]"
HSCAN 增量迭代字段(大数据量时避免阻塞) HSCAN user:1 0

3. 对象的三种存储策略:单 key 存储、多 key 存储、使用 hash 类型

(1)序列化为字符串(单 Key 存储)

复制代码
SET user:1000 '{"name":"Alice","age":30,"email":"[email protected]"}'

(2)每个字段单独存储(多 Key 存储)

复制代码
SET user:1000:name "Alice"
SET user:1000:age 30
SET user:1000:email "[email protected]"

(3)使用 Hash 类型

复制代码
HSET user:1000 name "Alice" age 30 email "[email protected]"

(4)三种策略比较

维度 序列化为字符串 多 Key 存储 Hash 类型
读写效率 整体读写快,部分更新慢 部分读写快,整体查询慢 部分和批量读写均快
内存占用 低(单个 Key) 高(每个 Key 有元数据) 中(小 Hash 用 zip list)
原子性 高(整个对象原子操作) 低(需事务) 中(单字段操作原子)
字段级 TTL 不支持 支持 不支持
适用字段规模 任意 少量字段 中小规模字段
复杂结构支持 支持(JSON 等序列化方式) 需额外设计 需序列化字段值

(四)Set 类型

Redis 的 Set 类型 是一个 无序、去重的集合,底层基于哈希表实现,支持插入、删除元素以及多个集合的交并差集运算,同时提供了随机返回指定个数元素的功能。

1. Set 的核心特性

  • 无序性:元素没有固定顺序,遍历时顺序不确定
  • 唯一性:自动去重,重复插入的元素会被忽略

2. 常用命令

(1)基本操作

命令 作用 示例
SADD 添加元素(自动去重) SADD tags "redis" "db"
SREM 删除元素 SREM tags "db"
SMEMBERS 获取所有元素 SMEMBERS tags
SISMEMBER 判断元素是否存在 SISMEMBER tags "redis"1(存在)
SCARD 获取集合元素数量 SCARD tags2
SRANDMEMBER 随机返回元素(可指定数量) SRANDMEMBER tags 2
  • 示例

    SADD users:1000:followers "user1" "user2" "user3"
    SMEMBERS users:1000:followers # 返回 ["user1", "user2", "user3"]
    SISMEMBER users:1000:followers "user1" # 返回 1(存在)

(2)集合运算

命令 作用 示例
SINTER 返回多个集合的交集 SINTER set1 set2
SUNION 返回多个集合的并集 SUNION set1 set2
SDIFF 返回第一个集合与其他集合的差集 SDIFF set1 set2
SINTERSTORE 存储交集到新集合 SINTERSTORE result set1 set2
SUNIONSTORE 存储并集到新集合 SUNIONSTORE result set1 set2
SDIFFSTORE 存储差集到新集合 SDIFFSTORE result set1 set2
  • 示例

    SADD group1 "user1" "user2" "user3"
    SADD group2 "user2" "user3" "user4"
    SINTER group1 group2 # 返回 ["user2", "user3"](共同成员)
    SDIFF group1 group2 # 返回 ["user1"](仅在 group1 中的成员)

(3)随机返回元素

  • Redis 的 Set 类型提供了 随机返回元素 的功能,适用于 抽奖、随机推荐、随机选取样本 等场景

    SRANDMEMBER key count

  • key:Set 的键名

  • count :指定返回的元素数量

    • count > 0 :返回 不重复 的元素(最多返回整个 Set 的大小)
    • count < 0 :返回元素可能 重复(适用于允许重复抽奖的场景)

(五)Sorted Set 类型

Redis 中的 Sorted Set 类型 也是一种集合类型,集合中的元素可以按照自定义分值排序,底层基于跳表实现。

1. 特点

  • 唯一性:成员(member)不可重复,但分数(score)可重复
  • 有序性 :按 score 排序(默认升序),相同 score 按字典序排序

2. 核心命令

  • 成员操作命令

    命令 功能
    ZADD key score member 添加/更新成员(支持NX/XX/INCR选项)
    ZREM key member 删除指定成员
    ZINCRBY key increment member 增加成员分数
    ZSCORE key member 获取成员分数
  • 查询命令

    命令 功能
    ZRANGE key start stop 按分数升序返回成员(WITHSCORES显示分数)
    ZREVRANGE key start stop 按分数降序返回成员
    ZRANGEBYSCORE key min max 返回分数在 [min,max] 区间的成员
  • 排名统计命令

    命令 功能
    ZRANK key member 获取成员升序排名(从 0 开始)
    ZREVRANK key member 获取成员降序排名
    ZCARD key 返回成员总数
    ZCOUNT key min max 统计分数范围内的成员数
  • 集合运算命令

    计算多个有序集合的并集存储(交集存储)

    ZUNIONSTORE/ZINTERSTORE destkey numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

    • 必选参数

      • destkey:存储结果的键名
      • numkeys:参与计算的集合数量(后续需对应数量的 key
      • key:参与计算的集合键名(至少一个)
    • 可选参数

      • WEIGHTS:为每个集合的分值设置权重(默认权重为 1
      • AGGREGATE:合并重复成员的分值策略(SUM 求和、MIN 取最小、MAX 取最大,默认 SUM
    • 示例

      ZADD scores1 10 "Alice" 20 "Bob"
      ZADD scores2 5 "Alice" 30 "Charlie"
      ZUNIONSTORE result 2 scores1 scores2 WEIGHTS 2 3 SUM

    • Alice 的分值: 10 * 2 + 5 * 3 = 35

    • Bob 的分值 = 20 * 2 = 40

    • Charlie 的分值 = 30 * 3 = 90

      1. "Alice" 35
      2. "Bob" 40
      3. "Charlie" 90

3. 底层实现

Sorted Set 是 Redis 中最复杂的数据结构之一。它通过 跳表和哈希表 的混合实现,兼顾了高效查询和动态排序的能力。

编码方式 数据结构 触发条件 特点
ziplist 压缩列表 [member1, score1, member2, score2, ...] 元素数量 ≤ zset-max-ziplist-entries(默认128)且所有元素长度 ≤ zset-max-ziplist-value(默认64字节) 内存紧凑,但增删效率低(O(n))
skiplist + dict 跳表 + 哈希表 不满足 ziplist 条件时自动转换 支持高效查询和范围操作(O(log n))
  • 哈希表与跳表的协同

    跳表保证有序性,哈希表加速单成员查询,两者互补

  • 内存与 CPU 的权衡

    小数据用 ziplist 节省内存,大数据用 skiplist 提升操作效率

(1)跳表的结构

  • 跳表(Skip List)是一种基于 多层有序链表 的数据结构,通过 概率平衡 实现高效的动态操作(平均 O(log n) 时间复杂度)

  • 跳表由多层链表组成,每层链表都是有序的,但高层链表是低层链表的【快速通道】:

    • 最底层链表:包含所有元素的有序链表

    • 高层链表:每层节点数约为下一层的一半,形成跳跃路径

(2)查找原理 :平均 O(log n) (最坏 O(n)

  • 从最高层头节点开始,向右遍历:

    • 若当前节点的下一个节点值 ≤ 目标值,则继续向右

    • 若下一个节点值 > 目标值,则向下移动到下一层

  • 重复上述过程,直到最底层,找到目标节点或确认不存在

(3)插入原理 :平均 O(log n)

  • 查找插入位置,记录每一层的前驱节点,即插入位置左侧的节点
  • 随机生成层高,每个新插入节点的层数由 概率决定 ,通常采用 "抛硬币"策略
    1. 初始层高 :新节点至少在第 0 层(最底层,包含所有节点)
    2. 逐层晋升 :每次"抛硬币" 若为"正面"(概率通常为 50%)则层数 +1;否则停止
    3. Redis 优化 :降低晋升概率(p=0.25)和限制最大层数(32),平衡性能与内存开销
  • 在每一层(从生成的最高层到最底层)链表中插入新节点,并更新前后节点的指针
相关推荐
巨龙之路4 分钟前
什么是时序数据库?
数据库·时序数据库
蔡蓝10 分钟前
binlog日志以及MySQL的数据同步
数据库·mysql
猿来入此小猿11 分钟前
基于SSM实现的健身房系统功能实现十六
java·毕业设计·ssm·毕业源码·免费学习·猿来入此·健身平台
goTsHgo1 小时前
Spring Boot 自动装配原理详解
java·spring boot
卑微的Coder1 小时前
JMeter同步定时器 模拟多用户并发访问场景
java·jmeter·压力测试
是店小二呀1 小时前
【金仓数据库征文】金融行业中的国产化数据库替代应用实践
数据库·金融·数据库平替用金仓·金仓数据库2025征文
pjx9871 小时前
微服务的“导航系统”:使用Spring Cloud Eureka实现服务注册与发现
java·spring cloud·微服务·eureka
炒空心菜菜1 小时前
SparkSQL 连接 MySQL 并添加新数据:实战指南
大数据·开发语言·数据库·后端·mysql·spark
柯南二号1 小时前
MacOS 用brew 安装、配置、启动Redis
redis
多多*2 小时前
算法竞赛相关 Java 二分模版
java·开发语言·数据结构·数据库·sql·算法·oracle