字符串 String
本教程共 40 篇 · 第 6 篇 · 更新于 2026-08-02
6. 字符串 String
本节目标
- 理解 String 是「二进制安全」的,能存文本、数字、JSON、图片字节
- 熟练使用
SET/GET/MSET/MGET/APPEND/STRLEN - 用
INCR/INCRBY/INCRBYFLOAT做原子计数器 - 用
SET的EX/NX/XX选项实现过期控制与分布式锁雏形 - 了解
GETSET、位操作,以及避免「大 key」
String 是 Redis 最基础的数据类型,也是我们用得最多的类型。它表面上像别的 KV 系统的 value,但 Redis 的 String 有几个重要特点。
6-1 什么是「二进制安全的 String」
Redis 的 String 本质是一段字节序列(byte array),可以存:
- 普通文本(UTF-8 中文、英文)
- 数字(整数、浮点数,Redis 会识别并支持原子增减)
- 序列化后的对象(JSON、Protocol Buffers、Java 对象等)
- 任意二进制数据(图片、音频片段)
「二进制安全」意味着 Redis 不会根据 \0(空字符)或编码去截断内容,存进去是什么、取出来就是什么。单个 String 的 value 上限是 512 MB。
6-2 基本读写:SET / GET / MSET / MGET
127.0.0.1:6379> SET name "码上学"
OK
127.0.0.1:6379> GET name
"码上学"
127.0.0.1:6379> STRLEN name
(integer) 9
STRLEN 返回字节长度(中文 UTF-8 每个字占 3 字节,所以「码上学」是 9 字节)。
一次设置/获取多个 key,减少往返(RTT):
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> MGET k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
APPEND 把内容追加到已有值的末尾(key 不存在则相当于 SET):
127.0.0.1:6379> APPEND greeting "Hello"
(integer) 5
127.0.0.1:6379> APPEND greeting " World"
(integer) 11
127.0.0.1:6379> GET greeting
"Hello World"
6-3 SET 的进阶选项(非常重要)
SET 支持一组修饰选项,让「设置值」这一个命令就能完成「条件写 + 过期」等组合动作:
SET key value [NX | XX] [GET] [EX seconds | PX ms | EXAT unix | PXAT unix | KEEPTTL]
EX 秒/PX 毫秒:设置过期时间。EXAT/PXAT:设置到某个 Unix 时间戳过期。NX:仅当 key 不存在时才设置(「set if not exists」)。XX:仅当 key 已存在时才设置。GET:返回设置前的旧值(8.0 起支持)。KEEPTTL:保留该 key 原有的 TTL。
示例——设置带 60 秒过期的缓存:
127.0.0.1:6379> SET session:1001 "token-abc" EX 3600
OK
127.0.0.1:6379> TTL session:1001
(integer) 3597
提示:老教程里常见
SETNX、SETEX、PSETEX三个独立命令,它们现在依然可用,但官方更推荐直接用SET+ 选项一步完成,语义更清晰。比如分布式锁经典写法就是SET lock:order 1 NX EX 30——「不存在才设、且 30 秒后自动释放」,一条命令原子搞定(详见缓存设计章节)。
6-4 原子计数器:INCR 家族
当 String 的值是「能被解析为整数」的数字时,Redis 提供原子增减命令,并发安全、无需应用层加锁:
127.0.0.1:6379> SET views 0
OK
127.0.0.1:6379> INCR views
(integer) 1
127.0.0.1:6379> INCR views
(integer) 2
127.0.0.1:6379> INCRBY views 10
(integer) 12
127.0.0.1:6379> DECR views
(integer) 11
127.0.0.1:6379> DECRBY views 5
(integer) 6
浮点数:
127.0.0.1:6379> SET price 2.71
OK
127.0.0.1:6379> INCRBYFLOAT price 0.43
"3.14"
INCR 系列命令即便 key 原本不存在,也会先初始化为 0 再自增,返回值是操作后的整数/浮点结果。
典型场景:文章阅读量、接口调用次数、库存扣减(注意超卖问题需配合其他机制)、限流器计数。
6-5 取旧值、改片段
GETSET key value:设置新值并返回旧值(常用于「取走当前计数并重置」)。GETRANGE key start end:取子串(类似 substring,支持负索引)。SETRANGE key offset value:从指定偏移量覆写。
127.0.0.1:6379> SET msg "Hello Redis"
OK
127.0.0.1:6379> GETRANGE msg 0 4
"Hello"
127.0.0.1:6379> SETRANGE msg 6 "World"
(integer) 11
127.0.0.1:6379> GET msg
"Hello World"
6-6 String 上的位操作(Bitmap 基础)
String 还能按「位」操作,实现超省内存的布尔统计(如用户每日签到):
127.0.0.1:6379> SETBIT sign:2026-08:user1 1 1
(integer) 0
127.0.0.1:6379> SETBIT sign:2026-08:user1 2 1
(integer) 0
127.0.0.1:6379> GETBIT sign:2026-08:user1 1
(integer) 1
127.0.0.1:6379> BITCOUNT sign:2026-08:user1
(integer) 2
SETBIT/GETBIT 操作单个位,BITCOUNT 统计置 1 的位数(即「签到了几天」)。关于 Bitmap / Bitfield 的细节,本教程有专门章节讲解。
6-7 String 的编码与「数字」语义
理解 String 的内部表示,能帮你写出更省内存、更高效的代码。Redis 会根据 value 的样子自动选择三种底层编码:
- int(整数编码):当 value 是能用 64 位有符号整数表示的纯数字(如
"100"、"42")时,Redis 直接以整数存储,INCR系列命令零成本自增,最省内存。 - embstr(短字符串):较短的字符串(通常不超过 44 字节)用一块连续内存存储,分配与访问都很快。
- raw(长字符串):更长的字符串走普通表示,适合大文本、序列化对象、二进制数据。
一个容易忽略的细节:String 里的「数字」只是看起来像数字。只有当你真正用 INCR/INCRBYFLOAT 这类命令时,Redis 才会按数值去运算;而 GET 出来的永远是字节序列,可以再参与字符串拼接。也正因如此,用 String 做计数器时务必全程走 INCR 家族,不要先 GET、在应用层加一、再 SET——那样在并发下会丢失更新,也失去了原子性。
提示:单个 String 的 value 上限是 512 MB,但别因为「能存这么大」就去塞大对象。一个几十 MB 的 String 在删除、迁移、持久化时都会拖慢实例,这就是所谓「大 key」问题。大文本或对象应拆分,或改用更合适的数据类型。
6-8 实战一:做缓存
最经典的 String 用法——把数据库查询结果缓存起来:
# 先查缓存
127.0.0.1:6379> GET user:1001:profile
(nil)
# 缓存未命中,回源数据库,再把结果写进 Redis,设 5 分钟过期
127.0.0.1:6379> SET user:1001:profile '{"id":1001,"name":"张三"}' EX 300
OK
(nil) 表示 key 不存在(与空字符串 "" 不同)。带 EX 过期可避免缓存永不失效、堆积旧数据。
6-9 实战二:分布式锁雏形
利用 SET ... NX 的原子性,多个进程抢同一把锁,只有一个能成功:
127.0.0.1:6379> SET lock:order:888 1 NX EX 30
OK # 抢到锁
# 另一个进程再来抢
127.0.0.1:6379> SET lock:order:888 1 NX EX 30
(nil) # 已被占用,抢不到
释放锁要用 Lua 脚本保证「只删自己的锁」(不能简单 DEL,否则可能误删别人的锁),完整做法在缓存设计章节展开。
6-10 常见误区
- 误区:INCR 只能用于已存在的数字 key。 不存在的 key 会被当成 0 自增,不需要先
SET 0。 - 误区:String 只能存字符串。 它能存任意二进制,数字只是被识别后可做原子运算。
- 误区:EXPIRE 后 INCR 不影响过期时间。
INCR等写命令不会重置 TTL;但注意RENAME、SET(不带 KEEPTTL)等会清除 TTL。 - 误区:缓存 value 越大越好。 单个 String 接近 512MB 上限会形成「大 key」,删除/迁移会卡顿,应拆分。
- 误区:
(nil)和""一样。GET不存在的 key 返回(nil)(空),GET一个值为空串的 key 返回"",两者含义不同。
6-11 小结
- String 是二进制安全的字节序列,能存文本/数字/对象/二进制,上限 512 MB。
- 读写:
SET/GET/MSET/MGET/APPEND/STRLEN;SET的EX/NX/XX/GET选项极常用。 - 计数:
INCR/INCRBY/DECR/INCRBYFLOAT,并发安全。 - 片段与旧值:
GETRANGE/SETRANGE/GETSET。 - 位操作:
SETBIT/GETBIT/BITCOUNT(Bitmap 基础)。 - 用武之地:缓存、计数器、分布式锁雏形、限流。
提示:版本基线说明。本教程正文统一以 Redis 8.x(最新稳定版) 表述。需要留意:官方 redis.io 下载页目前把
8.8标注为 “Latest stable”,而 GitHub 上 redis/redis 的最新发布标签为8.10.0;二者同属 8.x 大版本,命令与数据类型差异极小,本教程以8.10.0为准。
掌握了 String,下一章看 Hash——当你要存「对象」并频繁改其中某字段时,它比把整个对象塞进一个 String 更合适。