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实现排行榜
相关推荐
OKkankan3 分钟前
Python常用容器与导入语法详解(二)
数据结构·python
Cccp.12313 分钟前
【leetcode】(六) 图和贪心算法
数据结构·算法·leetcode
y1su15 分钟前
【Leetcode】1477. 找两个和为目标值且不重叠的子数组
数据结构·后端·算法·leetcode·职场和发展
微学AI18 分钟前
飞牛 NAS 部署 prompts.chat:自建提示词库、导入社区内容,再配置固定公网访问
数据库·内网穿透
羑悻的小杀马特20 分钟前
从写 YAML 到集群自愈:KES-Operator 把 KES 集群接进 Kubernetes 原生体系
运维·数据库·容器·kubernetes
bksczm23 分钟前
MySQL基础篇之事务
linux·数据库·sql·mysql
李白客25 分钟前
数据库管理工具怎么选?Navicat、DataGrip、DBeaver 六维横向对比与选型建议
运维·数据库
马立杰25 分钟前
mysql中Illegal mix of collations for operation “UNION”错误的解决方法
数据库·sql·网络安全
蓝速科技28 分钟前
蓝速 K10 三防平板在工业恶劣工况下的选型与应用指南
大数据·运维·数据库·人工智能·科技
bksczm29 分钟前
MySQL基础篇之视图与用户管理
linux·数据库·sql·mysql