Redis常用数据结构与命令详解

一、初识Redis

1.1 关系型与非关系型数据库的核心差异

1.1.1 数据组织方式不同

关系型数据库采用结构化存储,数据必须按照预定义的表结构组织。以用户表为例,每张表有固定的列:

id name age
1 张三 18
2 李四 20

非关系型数据库采用非结构化存储,数据以键值对或文档形式存在,没有固定的列约束:

键值形式:

key value
1001 {id: 1, name: "张三", age: 21}
1.1.2 数据关联方式不同

关系型数据库通过外键建立表与表之间的关联。查询用户订单需要三表连接:

sql 复制代码
SELECT u.name, i.title, i.price 
FROM tb_user u
JOIN tb_order o ON u.id = o.user_id
JOIN tb_item i ON o.item_id = i.id
WHERE u.id = 1;

非关系型数据库采用无关联设计,将相关数据嵌套存储在同一结构中:

json 复制代码
{
  "id": 1,
  "name": "张三",
  "orders": [
    {"id": 1, "item": {"id": 10, "title": "荣耀6", "price": 4999}},
    {"id": 2, "item": {"id": 20, "title": "小米11", "price": 3999}}
  ]
}
1.1.3 查询语言不同

关系型数据库使用标准SQL进行查询:

sql 复制代码
SELECT id, name, age FROM tb_user WHERE id = 1;

非关系型数据库使用各自特有的查询方式:

cmd 复制代码
Redis: get user:1
MongoDB: db.users.find({_id: 1})
Elasticsearch: GET http://localhost:9200/users/1
1.1.4 事务特性不同

关系型数据库遵循ACID原则,保证事务的原子性、一致性、隔离性、持久性,适合对数据安全性要求高的场景。

非关系型数据库遵循BASE理论,追求基本可用、软状态、最终一致性,适合对性能要求高、可接受短暂不一致的场景。

1.2 NoSQL的四种主流类型

  • 键值类型:以key-value形式存储,代表产品Redis,适合缓存、会话管理
  • 文档类型:以JSON/BSON文档存储,代表产品MongoDB,适合内容管理、用户画像
  • 列类型:按列族组织数据,代表产品HBase,适合海量数据离线分析
  • 图类型:以节点和边表示关系,代表产品Neo4j,适合社交网络、推荐系统

1.3 Redis的核心定位

Redis诞生于2009年,全称Remote Dictionary Server(远程词典服务器),是一个基于内存的键值型NoSQL数据库。

1.3.1 六大核心特征

键值存储且value类型丰富

Redis的key通常是String类型,但value支持多种数据结构。同一个Redis实例中,不同key可以对应完全不同的数据类型:

cmd 复制代码
msg → "hello world"           # String类型
user:1 → {name:"Jack"}        # Hash类型
queue → [A, B, C]             # List类型
friends → {A, B, C}           # Set类型
ranking → {A:1, B:2, C:3}     # SortedSet类型

单线程模型保证命令原子性

Redis采用单线程处理命令请求,每个命令执行过程中不会被其他命令打断,天然具备原子性,无需额外加锁机制。

低延迟高性能的三重保障

  • 基于内存操作,避免磁盘IO瓶颈
  • 采用IO多路复用技术,单线程高效处理海量连接
  • 针对不同数据类型采用最优编码策略,减少内存占用

支持数据持久化

虽然数据存储在内存中,但Redis提供机制将数据保存到磁盘,重启后可恢复数据,避免内存断电导致数据丢失。

支持集群架构

  • 主从集群:实现数据备份和读写分离
  • 分片集群:实现数据水平扩展,支撑更大存储容量和并发流量

多语言客户端支持

官方提供Java、Python、Go、Node.js等多种语言的客户端库,方便不同技术栈的项目集成使用。

二、Redis数据结构与命令详解

2.1 数据结构总览

Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样,分为基本类型和特殊类型:

类型分类 数据类型 示例 结构说明
基本类型 String hello world 字符串,最基础的存储单元,底层为字节数组
Hash {name:"Jack", age: 21} 哈希,无序字典,类似Java的HashMap结构
List [A -> B -> C -> C] 列表,双向链表结构,元素有序且可重复
Set {A, B, C} 集合,无序不重复的hash表,value恒为null
SortedSet {A: 1, B: 2, C: 3} 有序集合,每个元素带score属性,按分数自动排序
特殊类型 GEO {A: (120.3, 30.5)} 地理空间索引,存储经纬度坐标,支持距离计算
BitMap 0110110101110101011 位图,用二进制位表示状态,适合签到、在线统计
HyperLogLog 0110110101110101011 基数统计,用极小内存估算去重数量,允许微小误差

注意:基本类型是日常开发中最常用的5种数据结构,特殊类型适用于特定场景,如地理位置、大规模统计等。

Redis为了方便学习,将操作不同数据类型的命令做了分组,在官网可以查看到不同的命令。

使用help命令可以查看命令帮助:

1234567

通过help @可以查看某个命令组的所有命令,例如:

这样可以查看通用命令(generic)组的所有命令列表。

2.2 通用命令:跨数据类型的基础操作

通用命令适用于所有数据类型的key,用于管理key本身而非其内部数据。

2.2.1 查看符合模式的key
cmd 复制代码
KEYS test:user:*

该命令返回所有匹配test:user:*模式的key,例如test:user:1test:user:2

通配符规则:

  • * 匹配任意长度字符
  • ? 匹配单个字符
  • [abc] 匹配指定字符之一

注意:该命令会遍历整个数据库,数据量大时可能阻塞服务,仅建议在调试环境使用。

2.2.2 删除指定key
cmd 复制代码
DEL test:user:1

执行后返回实际删除的key数量。支持一次删除多个key:

cmd 复制代码
DEL test:user:2 test:user:12321
2.2.3 判断key是否存在
cmd 复制代码
EXISTS test:user:1

返回整数:1 表示存在,0 表示不存在。常用于缓存穿透防护场景。

2.2.4 设置key的有效期
cmd 复制代码
EXPIRE test:session:abc 10

表示该key在10秒后自动删除,适合存储会话、验证码等临时数据。

2.2.5 查看key的剩余有效期
cmd 复制代码
TTL test:session:abc

返回值含义:

  • 正整数:剩余存活秒数
  • -1:该key永不过期
  • -2:该key已过期或不存在

2.2.6 查看命令用法帮助
cmd 复制代码
HELP @string    # 查看String类型所有命令
HELP SET        # 查看SET命令详细参数

帮助信息包含命令语法、参数说明、返回值类型、时间复杂度等。

2.3 String类型:最基础的存储单元

2.3.1 三种数据格式

虽然都叫String,但根据内容格式可分为三类,每类支持不同的操作:

普通字符串

存储任意文本内容:

cmd 复制代码
SET msg "hello world"
GET msg  → 返回 "hello world"

整数类型

存储整数值,支持原子自增自减:

cmd 复制代码
SET num 10
INCR num      → num变为11,返回11
INCRBY num 5  → num变为16,返回16
DECR num      → num变为15,返回15

浮点数类型

存储小数值,支持精度自增:

cmd 复制代码
SET score 92.5
INCRBYFLOAT score 0.3  → score变为92.8,返回92.8

注意:无论哪种格式,底层都是字节数组存储,只是编码方式不同。单个value最大不能超过512MB。

2.3.2 核心命令详解

基础读写

cmd 复制代码
SET key value              # 设置值,存在则覆盖
GET key                    # 获取值,不存在返回nil
MSET k1 v1 k2 v2          # 批量设置多个键值对
MGET k1 k2                # 批量获取多个键的值

原子计数

cmd 复制代码
INCR counter               # 整型值+1,返回新值
INCRBY views 10           # 整型值+10
INCRBYFLOAT price 0.5     # 浮点值+0.5

条件设置

SETNX: 仅当key不存在时设置,若存在则不能设置,常用于分布式锁

cmd 复制代码
# 场景1:key不存在,设置成功
127.0.0.1:6379> SETNX mylock "user1"
(integer) 1
127.0.0.1:6379> GET mylock
"user1"

# 场景2:key已存在,设置失败
127.0.0.1:6379> SETNX mylock "user2"
(integer) 0
127.0.0.1:6379> GET mylock
"user1"

在分布式锁的应用原理

cmd 复制代码
# 线程A尝试获取锁
127.0.0.1:6379> SETNX lock:order:1001 "threadA"
(integer) 1  # 成功,获得锁

# 线程B尝试获取同一个锁
127.0.0.1:6379> SETNX lock:order:1001 "threadB"
(integer) 0  # 失败,锁已被占用

# 线程A释放锁
127.0.0.1:6379> DEL lock:order:1001
(integer) 1

# 线程B再次尝试
127.0.0.1:6379> SETNX lock:order:1001 "threadB"
(integer) 1  # 成功,获得锁
  • SETNX不会覆盖已存在的key
  • 需要配合EXPIRE设置过期时间,防止死锁
  • 现代Redis推荐使用SET key value NX EX seconds替代
2.3.3 Key的命名规范

Redis没有数据库和表的概念,所有key平铺存储。为避免不同业务冲突,建议采用层级命名:

复制代码
格式:项目名:业务名:类型:id

示例:
  test:user:1           # 用户信息,id=1
  test:product:1        # 商品信息,id=1

存储对象时,先序列化为JSON字符串:

cmd 复制代码
SET test:user:1 '{"id":1,"name":"Jack","age":21}'

2.4 Hash类型:对象字段的精细化管理

2.4.1 为什么需要Hash

如果用String存储用户对象:

cmd 复制代码
SET test:user:1 '{"id":1,"name":"Jack","age":21}'

当只需修改年龄时,必须:获取完整字符串→反序列化→修改字段→重新序列化→写回,流程繁琐且浪费带宽。

Hash类型将对象的每个字段独立存储,支持字段级操作:

cmd 复制代码
# 存储字段
HSET test:user:1 name "Jack"
HSET test:user:1 age 21

# 修改单个字段(不影响其他字段)
HSET test:user:1 age 22

# 获取单个字段
HGET test:user:1 name  → 返回 "Jack"

# 获取全部字段
HGETALL test:user:1  → 返回 {name:"Jack", age:22}
2.4.2 核心命令详解

字段增删改查

cmd 复制代码
HSET key field value           # 设置字段值,存在则更新
HGET key field                 # 获取字段值
HMSET key f1 v1 f2 v2         # 批量设置多个字段
HMGET key f1 f2               # 批量获取多个字段值
HGETALL key                    # 获取所有字段及值
HDEL key field                 # 删除指定字段

遍历与统计

cmd 复制代码
HKEYS test:user:1            # 获取所有字段名 [name, age]
HVALS test:user:1            # 获取所有字段值 [Jack, 21]
HLEN test:user:1             # 统计字段数量

原子计数

cmd 复制代码
HINCRBY test:user:1 age 1   # 字段值+1

条件设置

cmd 复制代码
HSETNX test:user:1 created_at "2024-01-01"  # 字段不存在时才设置

2.5 List类型:有序可重复的双向链表

2.5.1 结构特征

List底层是双向链表,具备四个核心特征:

有序性

元素按插入顺序排列,可通过索引访问:

cmd 复制代码
LPUSH list "C" "B" "A"   # 列表: [A, B, C]
LRANGE list 0 1          # 返回 [A, B]

元素可重复

相同值可多次插入,不会自动去重:

cmd 复制代码
RPUSH tags "java" "java" "python"  # 列表: [java, java, python]

插入删除快

头部尾部操作时间复杂度O(1):

cmd 复制代码
LPUSH queue "task1"   # 头部插入,极快
RPOP queue            # 尾部移除,极快

查询速度一般

按索引查询需遍历链表,时间复杂度O(n):

cmd 复制代码
LRANGE list 100 200   # 获取列表中索引从100到200的元素(包含两端)
2.5.2 核心命令详解

两端插入与移除

cmd 复制代码
# 左侧(头部)操作
LPUSH list "A" "B"    # 列表变为 [B, A]
LPOP list             # 移除并返回 "B"

# 右侧(尾部)操作
RPUSH list "C" "D"    # 列表变为 [A, C, D]
RPOP list             # 移除并返回 "D"

范围查询

cmd 复制代码
LRANGE list 0 2       # 获取索引0到2的元素(包含两端)
LRANGE list -3 -1     # 获取最后3个元素(负数表示倒数)

阻塞操作

cmd 复制代码
BLPOP queue 5         # 左侧弹出,无元素时最多等待5秒
BRPOP queue 5         # 右侧弹出,无元素时最多等待5秒
2.5.3 模拟经典数据结构

栈(LIFO):入口出口同侧

cmd 复制代码
入栈:LPUSH stack item
出栈:LPOP stack

队列(FIFO):入口出口异侧

cmd 复制代码
入队:RPUSH queue item
出队:LPOP queue

阻塞队列:消费者无数据时等待

cmd 复制代码
入队:RPUSH queue item
出队:BRPOP queue 10   # 无元素时阻塞等待10秒

2.6 Set类型:无序不重复的集合运算

2.6.1 结构特征

Set底层是value恒为null的hash表,具备四个核心特征:

无序性

元素无固定顺序,遍历结果顺序不确定:

cmd 复制代码
SADD tags "java" "python" "go"
SMEMBERS tags  → 返回顺序是无序的

元素不可重复

重复添加相同元素自动去重:

cmd 复制代码
SADD users "alice" "alice" "bob"  # 实际存储: {alice, bob}

查找速度快

判断元素是否存在时间复杂度O(1):

cmd 复制代码
SISMEMBER users "alice"  → 快速返回1或0

支持集合运算

原生支持交集、并集、差集,无需客户端计算:

复制代码
SINTER set1 set2   # 交集
SUNION set1 set2   # 并集
SDIFF set1 set2    # 差集
2.6.2 实战案例:好友关系管理

数据准备

cmd 复制代码
# 张三的好友
SADD test:friend:zhangsan "lisi" "wangwu" "zhaoliu"

# 李四的好友
SADD test:friend:lisi "wangwu" "mazi" "ergou"

需求1:统计张三好友数量

cmd 复制代码
SCARD test:friend:zhangsan  → 返回 3

需求2:找出共同好友(交集)

cmd 复制代码
SINTER test:friend:zhangsan test:friend:lisi  → 返回 ["wangwu"]

需求3:张三是好友但李四不是(差集)

cmd 复制代码
SDIFF test:friend:zhangsan test:friend:lisi  → 返回 ["zhangsan", "zhaoliu"]

需求4:两人好友合并去重(并集)

cmd 复制代码
SUNION test:friend:zhangsan test:friend:lisi  
→ 返回 ["lisi", "wangwu", "zhaoliu", "mazi", "ergou"]

需求5:判断李四是否是张三的好友

cmd 复制代码
SISMEMBER test:friend:zhangsan "lisi"  → 返回 1(存在)

需求6:判断张三是否是李四的好友

cmd 复制代码
SISMEMBER test:friend:lisi "zhangsan"  → 返回 0(不存在)

需求7:将李四从张三好友中移除

cmd 复制代码
SREM test:friend:zhangsan "lisi"
2.6.3 核心命令汇总
cmd 复制代码
# 增删查
SADD key member [member...]    # 添加元素(自动去重)
SREM key member [member...]    # 删除元素
SMEMBERS key                   # 获取所有元素
SISMEMBER key member           # 判断元素是否存在
SCARD key                      # 统计元素个数

# 集合运算
SINTER key1 key2 [key3...]     # 交集
SUNION key1 key2 [key3...]     # 并集
SDIFF key1 key2 [key3...]      # 差集

2.7 SortedSet类型:带权重的有序集合

2.7.1 结构原理

SortedSet每个元素关联一个score(双精度浮点数),系统根据score自动排序。底层采用跳表+hash表双结构:

  • 跳表:支持按分数范围快速查询、按排名访问
  • hash表:支持按成员快速查找分数

具备四个核心特征:

可排序

插入时按score升序排列,score相同时按member字典序:

cmd 复制代码
ZADD ranking 85 "Jack" 89 "Lucy" 82 "Rose"
ZRANGE ranking 0 -1  → 返回 [Rose, Jack, Lucy]   # -1表示最后一个元素的索引,0 -1表示获取从第一个到最后一个的所有元素  

元素不重复

相同member自动更新score,不会重复存储:

cmd 复制代码
ZADD scores 90 "Alice"
ZADD scores 95 "Alice"  # Alice的score更新为95

查询速度快

按分数范围、按排名范围查询均高效:

复制代码
ZRANGEBYSCORE scores 80 90    # 分数范围查询
ZRANGE scores 0 2             # 排名范围查询

分数可更新

支持原子增减,适合实时排行榜:

cmd 复制代码
ZINCRBY scores 5 "Alice"  # Alice分数+5
2.7.2 实战案例:班级成绩排行榜

数据准备

cmd 复制代码
ZADD test:score:class1 85 "Jack" 89 "Lucy" 82 "Rose" 95 "Tom" 78 "Jerry" 92 "Amy" 76 "Miles"

需求1:删除Tom同学

cmd 复制代码
ZREM test:score:class1 "Tom"

需求2:获取Amy同学的分数

cmd 复制代码
ZSCORE test:score:class1 "Amy"  → 返回 92

需求3:获取Rose同学的排名

cmd 复制代码
ZRANK test:score:class1 "Rose"  → 返回 2(升序第3名,从0开始)
ZREVRANK test:score:class1 "Rose"  → 返回 4(降序第5名)

需求4:查询80分以下有几个学生

cmd 复制代码
ZCOUNT test:score:class1 0 80  → 返回 2(Jerry和Miles)

需求5:给Amy同学加2分

cmd 复制代码
ZINCRBY test:score:class1 2 "Amy"  # Amy分数变为94

需求6:查出成绩前3名的同学

cmd 复制代码
ZREVRANGE test:score:class1 0 2 WITHSCORES
→ 返回: ["Tom", "95", "Amy", "94", "Lucy", "89"]

需求7:查出成绩80分以下的所有同学

cmd 复制代码
ZRANGEBYSCORE test:score:class1 0 80 WITHSCORES
→ 返回: ["Miles", "76", "Jerry", "78"]
2.7.3 核心命令分类

基础操作

cmd 复制代码
ZADD key score member [score member...]  # 添加/更新元素
ZREM key member [member...]              # 删除元素
ZSCORE key member                        # 获取元素分数
ZCARD key                                # 统计元素总数

排名查询

cmd 复制代码
ZRANK key member         # 升序排名(从0开始)
ZREVRANK key member      # 降序排名
ZRANGE key start stop [WITHSCORES]      # 按排名获取(升序)
ZREVRANGE key start stop [WITHSCORES]   # 按排名获取(降序)

分数范围查询

cmd 复制代码
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
ZCOUNT key min max       # 统计分数区间元素个数

分数更新

cmd 复制代码
ZINCRBY key increment member  # 分数原子增减

集合运算

cmd 复制代码
ZDIFF key1 key2 ...     # 差集
ZINTER key1 key2 ...    # 交集
ZUNION key1 key2 ...    # 并集

注意:所有范围命令默认升序,需降序时在Z后加REV;WITHSCORES参数可同时返回元素和分数

三、学习要点回顾

3.1 五种核心数据结构对比

类型 有序性 可重复 核心优势 典型场景
String - - 读写最快,支持原子计数 缓存、计数器、分布式锁
Hash - - 字段级操作,节省序列化开销 对象存储、用户信息
List 两端操作高效,支持阻塞 消息队列、时间线
Set 自动去重,集合运算原生支持 标签、好友、去重统计
SortedSet 自动排序,双维度查询 排行榜、带权任务队列

3.2 Key命名规范

复制代码
推荐格式:项目名:业务名:类型:id

示例:
  test:user:1           # 用户信息
  test:product:1001     # 商品信息
  test:cart:user:1      # 用户购物车

3.3 命令学习技巧

  • 使用HELP @类型查看某类数据结构的所有命令
  • 使用HELP 命令名查看具体命令的详细用法
  • 先掌握每种结构的3-5个高频命令,再逐步扩展
  • 结合业务场景练习,如用Set实现好友管理、用SortedSet实现排行榜
相关推荐
小李云雾9 小时前
Redis 从入门到实战:核心知识点与架构搭建全解析
数据库·redis·架构
SelectDB9 小时前
- 别把懂语义和查事实混为一谈:企业级 Agent 真正缺的是什么?
数据库·数据分析·agent
Lao A(zhou liang)的菜园9 小时前
深入详细解释Oracle 全量 CHECKPOINT 与增量 CHECKPOINT
数据库·oracle
数据库小学妹9 小时前
异构数据库同步实战:如何打通Oracle/MySQL/SQL Server的数据孤岛
数据库·mysql·oracle
数据库知识分享者小北10 小时前
安全可靠数据库选型之阿里云 PolarDB MySQL 版V2.0
数据库·mysql·阿里云·国产数据库·polardb·安全可靠数据库·polardb-m
一只fish10 小时前
Oracle官方文档翻译《Database Concepts 26ai》第16章-数据库实例
数据库·oracle
艾莉丝努力练剑10 小时前
【Linux:文件】库的制作与原理进阶
linux·运维·服务器·网络·数据库·c++·人工智能
深念Y10 小时前
DeepSeek/MiMo 推理链缓存代理:从内存到 SQLite 的两级缓存架构实战
数据库·缓存·架构·sqlite·内存·优化·分层
一点事10 小时前
oracle:手动同步数据库
数据库·oracle