Skip to content

Redis - 字符串

Redis 字符串是最简单的数据类型,但它们功能强大且用途广泛。它们是二进制安全的,这意味着一个字符串值可以容纳任何类型的数据,从 UTF-8 字符串到 JPEG 图像或序列化对象,最大可达 512MB。本节将介绍 Redis 中管理字符串值的现代命令和最佳实践。

1. 核心命令:SET、GET 和过期设置

Section titled “1. 核心命令:SET、GET 和过期设置”

基本操作包括设置值、检索值以及管理其生命周期。现代 Redis 为了提高效率,将许多选项集中到 SET 命令中。

与 SETEX 或 SETNX 等独立命令不同,现代 SET 命令包含的选项可以在一个单独的、原子性的操作中处理这些情况,从而减少网络延迟。

SET key value [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds] [NX | XX] [GET]
  • EX seconds:设置以秒为单位的过期时间。
  • PX milliseconds:设置以毫秒为单位的过期时间。
  • NX:仅在键不存在时设置键。
  • XX:仅在键已存在时设置键。
  • GET:返回键的旧值,如果键不存在则返回 nil。
# 设置用户会话数据,1 小时(3600 秒)后过期
> SET session:user:123 "{...user data...}" EX 3600
OK
# 尝试仅在用户名未被占用时设置
> SET username:jane_doe "unique_user_id_123" NX
OK # 成功,因为键是新的
> SET username:jane_doe "another_id_456" NX
(nil) # 失败,因为键已存在
# 检索值
> GET session:user:123
"{...user data...}"

Redis 提供了用于递增或递减整数和浮点值的原子命令。‘原子性’意味着操作在不中断的情况下完全完成,这对于防止并发应用中的竞态条件(race conditions)至关重要(例如,多个客户端尝试更新同一个计数器)。

  • INCR key:将键的整数值递增 1。
  • DECR key:将键的整数值递减 1。
  • INCRBY key increment:将整数值递增给定数量。
  • DECRBY key decrement:将整数值递减给定数量。
  • INCRBYFLOAT key increment:将浮点值递增给定数量。
# 用户发起请求。增加其请求计数器。
> INCR api_limit:user:123:minute
(integer) 1
# 首次对键设置过期时间,以便在一分钟后重置计数
> EXPIRE api_limit:user:123:minute 60
(integer) 1
# 更好的原子性实现方式是在事务或 Lua 脚本中使用 SET 和 INCR。
# 同一用户的另一个请求
> INCR api_limit:user:123:minute
(integer) 2
# 检查当前计数
> GET api_limit:user:123:minute
"2"

为减少网络往返(network round-trips),请使用 MSET 和 MGET 一次性设置或获取多个键。MSETNX 是一个变体,仅当所有键都不存在时才设置它们。

> MSET user:1:name "Alice" user:2:name "Bob" user:1:plan "premium"
OK
> MGET user:1:name user:2:name user:3:name
1) "Alice"
2) "Bob"
3) (nil) # user:3:name 未设置

位图允许你将字符串视为一个位数组。这对于追踪大量实体(entity)的布尔信息(例如,活跃/非活跃)来说,内存效率极高。

  • SETBIT key offset value:在 offset 处设置或清除位(0 或 1)。
  • GETBIT key offset:返回 offset 处的位。
  • BITCOUNT key [start end]:计算设置位(1)的数量。
  • BITPOS key bit [start end]:查找第一个设置为 0 或 1 的位。
# ID 为 5 的用户在 2023-10-27 登录
> SETBIT daily_active_users:2023-10-27 5 1
(integer) 0 # 该位之前为 0
# 用户 100 也登录
> SETBIT daily_active_users:2023-10-27 100 1
(integer) 0
# 用户 5 今天是否登录?
> GETBIT daily_active_users:2023-10-27 5
(integer) 1
# 今天有多少独立用户登录?
> BITCOUNT daily_active_users:2023-10-27
(integer) 2

这些命令在一个原子步骤中执行操作并检索值。

  • GETSET key value:设置新值并返回旧值。适用于轮换密钥(secrets)或令牌(tokens)等场景。
  • GETDEL key:检索键的值,然后将其删除。非常适合实现可靠的、一次性使用的消息队列或锁。
  • APPEND key value:将值追加到字符串的末尾。返回字符串的新长度。
# 任务存储在一个临时键中
> SET task:job:987 "Process video upload"
OK
# 一个工作进程原子性地获取任务详情并删除该键
# 这确保没有其他工作进程能获取到相同的任务。
> GETDEL task:job:987
"Process video upload"
  • 键命名: 使用清晰、分层的命名约定,例如 object:id:field(如 user:1001:profile_pic)。这能提高可读性并防止键冲突(key collisions)。
  • 缓存: 存储 API 响应、渲染后的 HTML 片段或复杂的计算结果。始终使用 EX 设置过期时间,以防止数据过期或陈旧(stale data)。
  • 会话存储: 存储用户会话数据。EX 选项非常适合在用户不活跃一段时间后自动登出用户。
  • 限流: 在带有 EX 的键上使用 INCR 来跟踪时间窗口内的操作。
  • 功能开关: 一个简单的 GET feature:new_dashboard:enabled 命令可以判断某个功能对用户或用户组是否启用。
  • 避免大型负载: 尽管字符串可以容纳 512MB,但这通常是一种反模式(anti-pattern)。存储大型 JSON 文档最好通过 Redis 的原生 JSON 数据类型(RedisJSON 模块)或将数据拆分为 Redis 散列(Hash)来处理。