【redis 基础】redis 的常用数据结构及其核心操作
一、String(字符串)
1. 核心概念
String 是 Redis 最简单、最基础的数据类型。一个 Key 对应一个 Value。它不仅仅是"字符串",它实际上是二进制安全的,意味着它可以存储任何二进制序列,比如一张图片或者一个序列化后的对象。
2. 典型应用场景
- 缓存:缓存数据库查询结果、HTML 片段等
- 计数器:统计网站访问量、文章点赞数等
- Session 存储:在分布式系统中存储用户登录信息
- 简单的键值存储:存储用户令牌(Token)、配置项等
3. 核心操作命令
命令 | 格式 | 描述 | 实用示例 |
---|---|---|---|
SET | SET key value [EX seconds] [PX milliseconds] [NX|XX] | 设置一个键值对。可附加过期时间(EX/PX)和条件(NX-仅键不存在时设置,XX-仅键存在时设置) | SET user:1000 "Alice" EX 3600 (设置值并使其1小时后过期) |
GET | GET key | 获取指定 key 的字符串值。如果 key 不存在,返回 nil | GET user:1000 |
DEL | DEL key | 删除指定的 key | DEL user:1000 |
INCR | INCR key | 将 key 中存储的数字值增加 1。如果 key 不存在,会先被初始化为 0 再执行操作。原子操作,非常适合做计数器 | INCR article:100:likes (文章点赞数+1) |
DECR | DECR key | 将 key 中存储的数字值减 1 | DECR inventory:product50 (商品库存-1) |
INCRBY | INCRBY key increment | 将 key 所储存的值加上给定的增量值 | INCRBY user:1000:score 50 (用户积分增加50) |
DECRBY | DECRBY key decrement | 将 key 所储存的值减去给定的减量值 | DECRBY inventory:product50 5 (商品库存减少5) |
MSET | MSET key value [key value ...] | 同时设置多个键值对 | MSET user:1000:name "Alice" user:1000:email "alice@example.com" |
MGET | MGET key [key ...] | 同时获取所有给定 key 的值 | MGET user:1000:name user:1000:email |
GETSET | GETSET key value | 设置新值并返回旧值。适用于需要获取当前值并重置的场景 | GETSET daily_visits 0 (获取今天的访问量,然后重置为0) |
STRLEN | STRLEN key | 返回 key 所储存的字符串值的长度 | STRLEN user:1000:name |
SETNX | SETNX key value | 是 SET key value NX 的简写形式。只有在 key 不存在时才设置值。常用于分布式锁的实现 | SETNX lock:order 1 (获取一个锁) |
4. 注意事项
- 适合存储独立、完整的值或需要原子计数的场景
- 存储对象时无法单独更新字段,建议复杂对象使用 Hash
二、List(列表)
1. 核心概念
Redis 的 List 是一个简单的字符串列表,按插入顺序排序。可以在列表的头部(左边)或尾部(右边)添加元素。它的本质是一个双向链表,这意味着在头部和尾部操作元素的速度极快,但通过索引访问中间元素的速度较慢。
2. 典型应用场景
- 消息队列:利用 LPUSH + BRPOP 实现一个简单的生产者-消费者模型
- 最新列表:记录用户的最新动态、最新新闻等(例如朋友圈时间线)
- 记录操作历史:比如用户最近的搜索记录
3. 核心操作命令
命令 | 格式 | 描述 | 实用示例 |
---|---|---|---|
LPUSH | LPUSH key value [value ...] | 将一个或多个值插入到列表的头部(左边) | LPUSH friends:1000 "Bob" (向用户1000的好友列表头部添加Bob) |
RPUSH | RPUSH key value [value ...] | 将一个或多个值插入到列表的尾部(右边) | RPUSH tasks "send_email" (向任务列表尾部添加一个任务) |
LPOP | LPOP key | 移除并返回列表的第一个元素(头部元素,左边) | LPOP tasks (获取并移除下一个要处理的任务) |
RPOP | RPOP key | 移除并返回列表的最后一个元素(尾部元素,右边) | |
LRANGE | LRANGE key start stop | 获取列表中指定范围内的元素。0 表示第一个元素,-1 表示最后一个元素。这是一个查看操作,不会移除元素 | LRANGE news 0 9 (获取最新的10条新闻) |
LLEN | LLEN key | 获取列表的长度 | LLEN friends:1000 (获取用户1000的好友数量) |
LINDEX | LINDEX key index | 通过索引(从0开始)获取列表中的元素 | LINDEX friends:1000 0 (获取用户1000的第一个好友) |
LTRIM | LTRIM key start stop | 只保留列表中指定范围内的元素,其余全部删除。非常适合用来维护一个固定大小的列表 | LTRIM latest_logs 0 99 (只保留最新的100条日志) |
BLPOP | BLPOP key [key ...] timeout | 阻塞式的列表弹出操作。如果列表没有元素,命令会阻塞连接,直到等待超时或有元素可弹出为止。这是实现简单消息队列的关键 | BLPOP task_queue 30 (等待任务,最多等30秒) |
4. 注意事项
- 适合需要顺序性的场景
- 元素可重复,不适合需要唯一性的场景
三、Set(集合)
1. 核心概念
Redis 的 Set 是 String 类型的无序集合。它最大的特点是:元素唯一,不允许重复。底层基于哈希表实现,所以查找、插入、删除操作的时间复杂度都是 O(1)。
2. 典型应用场景
- 标签(Tag)系统:给文章、用户打标签
- 共同好友/共同关注:快速求两个用户的共同好友
- 抽奖、随机推荐:利用随机弹出元素的功能
- 黑白名单:快速判断一个用户 ID 是否在名单内
3. 核心操作命令
命令 | 格式 | 描述 | 实用示例 |
---|---|---|---|
SADD | SADD key member [member ...] | 向集合中添加一个或多个成员。已存在的成员会被忽略 | SADD article:100:tags "redis" "database" (给文章100打标签) |
SREM | SREM key member [member ...] | 移除集合中一个或多个成员 | SREM article:100:tags "database" |
SMEMBERS | SMEMBERS key | 获取集合中的所有成员。注意:数据量大时谨慎使用,可能会阻塞! | SMEMBERS article:100:tags |
SISMEMBER | SISMEMBER key member | 判断 member 元素是否是集合 key 的成员。这是集合的核心优势之一,查询效率极高 | SISMEMBER blacklist "user123" (判断用户是否在黑名单中) |
SCARD | SCARD key | 获取集合的成员总数(Set Cardinality) | SCARD article:100:tags (获取文章100的标签数量) |
SINTER | SINTER key [key ...] | 返回所有给定集合的交集。实现"共同好友"的核心命令 | SINTER user:1000:friends user:2000:friends (获取用户1000和2000的共同好友) |
SUNION | SUNION key [key ...] | 返回所有给定集合的并集 | |
SDIFF | SDIFF key [key ...] | 返回第一个集合与其他集合之间的差集 | SDIFF user:1000:friends user:2000:friends (获取用户1000有而2000没有的好友) |
SPOP | SPOP key [count] | 随机移除并返回集合中的一个或多个元素。非常适合抽奖 | SPOP lottery:winners 3 (随机抽取3名中奖者) |
SRANDMEMBER | SRANDMEMBER key [count] | 随机返回集合中一个或多个元素,但不删除。适合随机推荐 | SRANDMEMBER recommended:products 5 (随机推荐5个商品) |
4. 注意事项
- 适合需要唯一性和集合运算的场景
- 无序存储,需要排序时使用 ZSet
四、ZSet(Sorted Set,有序集合)
1. 核心概念
ZSet 可以说是 Set 和 Hash 的混合体。它同样是 String 类型元素的集合,且不允许重复。但每个元素都会关联一个 score(分数),Redis 正是通过这个分数来对集合中的元素进行从小到大的排序。
2. 典型应用场景
- 排行榜:游戏玩家积分榜、视频热度榜
- 带权重的队列:score 代表优先级,实现优先级队列
- 时间轴:将时间戳作为 score,轻松实现按时间排序
3. 核心操作命令
命令 | 格式 | 描述 | 实用示例 |
---|---|---|---|
ZADD | ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...] | 向有序集合中添加一个或多个成员,或者更新已存在成员的分数。选项复杂,功能强大 | ZADD leaderboard 1000 "Alice" 800 "Bob" (设置玩家分数) ZADD leaderboard LT 1100 "Alice" (仅当新分数比旧分数小时更新) |
ZRANGE | ZRANGE key start stop [WITHSCORES] | 按分数从小到大顺序返回索引在 start 和 stop 之间的成员。WITHSCORES 选项会同时返回分数。0 -1 表示所有 | ZRANGE leaderboard 0 2 WITHSCORES (获取排行榜前三名) |
ZREVRANGE | ZREVRANGE key start stop [WITHSCORES] | 按分数从大到小顺序返回成员(即降序)。查看排行榜就用这个! | ZREVRANGE leaderboard 0 9 WITHSCORES (获取排行榜前十名) |
ZRANK | ZRANK key member | 返回指定成员的排名(升序),从 0 开始 | ZRANK leaderboard "Bob" (查询Bob的升序排名) |
ZREVRANK | ZREVRANK key member | 返回指定成员的排名(降序),从 0 开始。这才是实际的排行榜名次! | ZREVRANK leaderboard "Bob" (查询Bob是第几名) |
ZSCORE | ZSCORE key member | 返回有序集合中成员的分数 | ZSCORE leaderboard "Alice" (查询Alice的积分) |
ZINCRBY | ZINCRBY key increment member | 有序集合的分数增加操作,非常核心 | ZINCRBY leaderboard 50 "Alice" (给Alice加50分) |
ZCOUNT | ZCOUNT key min max | 返回分数在 min 和 max 之间的成员数量 | ZCOUNT leaderboard 800 1000 (查询800分到1000分之间有多少人) |
ZRANGEBYSCORE | ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] | 返回分数在 min 和 max 之间的所有成员(升序) | ZRANGEBYSCORE leaderboard 800 1000 (查询800分到1000分之间的所有玩家) |
ZREM | ZREM key member [member ...] | 移除有序集合中的一个或多个成员 | ZREM leaderboard "Bob" (将Bob从排行榜移除) |
4. 注意事项
- 实现排行榜功能的理想选择
- 支持范围查询,适合按分数区间检索
五、Hash(哈希)
1. 核心概念
Hash 是一个 String 类型的 field(字段)和 value(值)的映射表,非常适合用来存储对象。
2. 典型应用场景
- 存储对象信息:存储用户、商品等对象的属性
- 购物车:key 是用户ID,field 是商品ID,value 是商品数量
- 配置项管理
3. 核心操作命令
命令 | 格式 | 描述 | 实用示例 |
---|---|---|---|
HSET | HSET key field value [field value ...] | 设置哈希表中一个或多个字段的值 | HSET user:1000 name "Alice" age 30 email "alice@example.com" (设置用户信息) |
HGET | HGET key field | 获取哈希表中指定字段的值 | HGET user:1000 name -> "Alice" |
HGETALL | HGETALL key | 获取哈希表中所有的字段和值。数据量大时谨慎使用 | HGETALL user:1000 |
HDEL | HDEL key field [field ...] | 删除哈希表中的一个或多个字段 | HDEL user:1000 age (删除年龄字段) |
HMSET | HMSET key field value [field value ...] | HSET 的别名,功能相同 | |
HMGET | HMGET key field [field ...] | 同时获取哈希表中多个字段的值 | HMGET user:1000 name email |
HINCRBY | HINCRBY key field increment | 为哈希表中的数字值字段加上增量。非常适合购物车增减数量 | HINCRBY cart:1000 product:50 1 (商品数量+1) HINCRBY cart:1000 product:50 -1 (商品数量-1) |
HKEYS | HKEYS key | 获取哈希表中的所有字段 | HKEYS user:1000 -> 1) "name" 2) "email" |
HVALS | HVALS key | 获取哈希表中的所有值 | HVALS user:1000 -> 1) "Alice" 2) "alice@example.com" |
HLEN | HLEN key | 获取哈希表中字段的数量 | HLEN user:1000 |
HEXISTS | HEXISTS key field | 检查哈希表中指定字段是否存在 | HEXISTS user:1000 name -> (integer) 1 |
4. 注意事项
- 适合存储和修改对象属性,支持字段级操作
- HGETALL 在字段多时可能阻塞,建议使用 HMGET 获取指定字段
六、总结与选择指南
1. 各数据结构核心特点与选择指南
String(字符串)
- 本质:最简单的类型,就是普通的键值对
- 选择时机:需要存储一个独立的、整体的数据时;需要原子计数器时
- 注意:虽然也能用 String 存 JSON 字符串来表示对象,但无法单独更新某个字段,不如 Hash 结构高效
List(列表)
- 本质:一个有序且元素可重复的链表
- 选择时机:需要顺序性的场景;需要模拟栈或队列
- 注意:因为元素可重复,所以不适合需要唯一性的场景
Set(集合)
- 本质:一个无序且元素唯一的集合
- 选择时机:需要快速判断某个元素是否存在时;需要求多个集合之间的关系时;需要随机获取不重复元素时
- 注意:它是无序的!如果你需要顺序,应该使用 ZSet
ZSet / Sorted Set(有序集合)
- 本质:在 Set 的基础上,为每个元素关联了一个 score(分数),从而实现了排序
- 选择时机:所有需要排行榜的场景;需要按范围(Range)高效查询的场景;可以作为带优先级的队列来处理
- 注意:这是 Redis 中最复杂但功能最强大的数据结构之一,是实现排行榜功能的不二之选
Hash(哈希)
- 本质:一个 field(字段)和 value(值)的映射表,非常适合用来表示一个对象
- 选择时机:需要存储和修改对象属性时;需要单独操作对象中的某个字段时;实现类似购物车的功能
- 注意:HGETALL 在字段非常多时可能会慢,可以考虑用 HMGET 来获取指定字段
2. 总结与记忆技巧
记住这几个关键词,就能快速做出选择:
- 要简单存值、计数 → String
- 要顺序、队列、栈 → List
- 要唯一、找共同、抽奖 → Set
- 要排行榜、按范围查 → ZSet
- 要存对象、改属性 → Hash