Redis String 数据类型详解
String 是 Redis 中最基础、使用最广泛的数据类型,其余 4 种核心数据结构(Hash、List、Set、ZSet)均构建于 String 类型之上。
它以 key-value 键值对形式存储,value 层对外表现为字符串,底层支持整数、浮点数、普通文本、二进制数据等多种形态,
具备高性能、原子性、二进制安全等核心优势。
一、核心基础特性
1. 二进制安全
Redis String 不依赖 C 语言的\0作为字符串结束标志,存储与读取完全按照字节长度处理,
因此,可以安全存储图片、序列化对象、加密串等任意二进制数据,不会因特殊字符出现内容截断。
2. 容量上限
单个 key 对应的 value 最大支持512MB存储,但工程实践中强烈不建议存储过大的 value,
通常,控制在 10KB 以内,避免阻塞 Redis 主线程。
3. 多语义自动适配
同一个 String 类型会根据 value 内容自动适配存储语义:
- 纯整数(64位有符号范围内):支持原子加减运算
- 浮点数:支持浮点原子增量
- 普通字符串/二进制:支持拼接、截取、位操作
4. 三种编码模式(内存优化核心)
Redis 会根据 value 的内容和长度,自动选择三种编码方式存储,最大化节省内存,可通过OBJECT ENCODING <key>命令查看当前编码。
| 编码类型 | 触发条件 | 存储特点 |
|---|---|---|
| int | value 为 64 位有符号整数范围内的数字 | 直接将数值存储在 redisObject 的ptr字段,无需额外 SDS 内存,内存占用最小、访问最快 |
| embstr(嵌入式字符串) | 字符串长度 ≤ 44 字节(Redis 3.2+,旧版为 39 字节) | redisObject 与 SDS 结构体在同一块连续内存中分配,仅需一次内存申请,减少内存碎片,访问速度快;属于只读优化,修改时会自动转为 raw 编码 |
| raw(原始字符串) | 字符串长度 > 44 字节 | redisObject 与 SDS 分离存储,两次内存分配,支持灵活修改 |
编码转换规则
- int 编码的 key,若追加字符串、修改为非整数,会转为 raw/embstr;
- embstr 编码的 key,执行任何修改操作(如 APPEND、SETRANGE),都会转为 raw 编码;
- 数值超过 long 范围(如超长数字字符串),会自动转为 embstr/raw 编码。
二、核心命令分类详解
1. 基础读写命令
SET:设置键值对(最核心命令)
SETkey value[EXseconds][PXmilliseconds][EXATtimestamp][PXATms-timestamp][NX|XX][KEEPTTL]- 常用参数说明:
EX seconds:设置过期时间(秒)PX milliseconds:设置过期时间(毫秒)NX:仅当 key不存在时才设置成功(原子性创建,常用于分布式锁)XX:仅当 key已存在时才设置成功(原子性更新)KEEPTTL:保留该 key 原有的过期时间,不被新 SET 覆盖
- 示例:
# 普通设置SETusername"zhangsan"# 设置值并指定1小时过期SETuser:1001'{"id":1001,"age":20}'EX3600# 原子加锁:key不存在才创建,30秒自动过期SETlock:order:1001"random_uuid"NXEX30
GET:获取值
GET key- GET 是 String 类型专属的读取命令,只能读取类型为 String 的 key。
- key 不存在时返回
nil;若 value 非字符串类型会报错。 nil是 Redis 的空值标记,代表这个 key 不存在,没有对应的数据。
GETSET:设置新值并返回旧值
语法:GETSETkey newValue 例子:127.0.0.1:6379>getset test"hello"(nil)`因为从未设置过 test,所以返回结果是:nil`,但是,此时已经设置了 test 值为 hello127.0.0.1:6379>get test"hello"127.0.0.1:6379>getset test"hellw""hello"127.0.0.1:6379>get test"hellw"127.0.0.1:6379>- 原子操作,常用于「获取当前值并重置」场景,例如计数器清零。
APPEND:字符串追加
APPENDkey appendValue- 在字符串末尾追加内容,返回追加后的总字节长度;
- key 不存在时等同于 SET;
- String 类型专属命令,非 String 类型的 key 执行会报类型错误;
STRLEN:获取字符串长度
STRLENkey- 返回字节数,注意 UTF-8 中文每个字符占 3 字节。
GETRANGE / SETRANGE:子串操作
# 获取[start,end]闭区间子串,支持负数索引(-1表示最后一个字节) 语法:GETRANGEkey start end"补充":end 超过最大索引时,Redis会自动截断到字符串末尾,不会报错。 例子:SETdemo"HelloRedis"GETRANGEdemo-5-1-1:最后一个字节(末尾的 s)-5:代表倒数第5个字节(开头的R)-----------------------------------------# 从 offset 位置开始覆盖写入 value,返回修改后总长度 语法:SETRANGEkey offset value 例子:SETdemo"Hello"SETRANGEdemo3"123456"返回结果:(integer)9GETdemo 返回结果:"Hel123456"注意:SETname"你好"GETRANGEname03会返回乱码,因为 “你” 占0~2字节,第3字节是 “好” 的第一个字节,被截断了。2. 数值原子操作(单线程保证原子性)
Redis 单线程模型保证所有数值命令都是原子操作,并发场景下不会出现竞态问题,是实现计数器的核心方案。
| 命令 | 作用 | 示例 |
|---|---|---|
INCR key | 值自增 1,key 不存在则从 0 开始自增,返回自增后结果 | INCR article:100:views |
DECR key | 值自减 1 | DECR stock:1001 |
INCRBY key increment | 增加指定整数增量 | INCRBY score:user:1001 10 |
DECRBY key decrement | 减少指定整数减量 | DECRBY stock:1001 5 |
INCRBYFLOAT key increment | 增加浮点数增量,支持正负值 | INCRBYFLOAT price 0.5 |
注意:不存在对应
DECRBYFLOAT,可通过传入负增量实现减法。
3. 批量操作命令(减少网络IO)
批量命令可一次网络往返完成多个 key 的操作,大幅降低 RTT 开销,提升性能。
MSET:批量设置
MSETkey1 value1 key2 value2 key3 value3...- 原子操作,所有 key 要么全部设置成功,要么全部失败。
MGET:批量获取
MGETkey1 key2 key3...- 按顺序返回对应 value,不存在的 key 返回
nil。
MSETNX:批量不存在时设置
MSETNXkey1 value1 key2 value2...- 仅当所有 key 都不存在时才全部设置成功,具备原子性,可用于批量分布式锁抢占。
4. 位图 Bitmap 操作
String 类型支持按位操作,即 Bitmap(位图),将 value 的每个字节拆分为 8 个二进制位,可实现海量布尔状态的极低内存存储(1 亿个布尔状态仅需约 12MB 内存)。
| 命令 | 作用 |
|---|---|
SETBIT key offset value | 设置第 offset 位的值(0 或 1) |
GETBIT key offset | 获取第 offset 位的值 |
BITCOUNT key [start end] | 统计指定字节范围内值为 1 的位总数 |
BITOP operation destkey key1 key2... | 对多个 key 执行位运算(AND/OR/XOR/NOT),结果存入 destkey |
BITPOS key bit [start end] | 查找第一个值为 0/1 的位的偏移量 |
四、典型业务应用场景
1. 热点数据缓存
最主流的用法,缓存用户信息、商品详情、配置参数、页面片段等热点数据,降低数据库访问压力。
SETproduct:2001'{"id":2001,"name":"手机","price":3999}'EX18002. 计数器与统计
利用 INCR 原子性实现文章阅读量、点赞数、接口调用量、库存计数等场景,天然支持高并发。
# 文章阅读量+1INCRarticle:500:views3. 分布式锁
基于SET key value NX EX timeout实现原子加锁,配合随机 value 实现安全解锁,是轻量级分布式锁的标准实现方案。
4. 会话共享(Session 存储)
分布式系统中,将用户登录态、Token 存入 Redis String,设置过期时间,实现多服务实例共享会话。
SETtoken:abc123xyz user:1001EX72005. 接口限流
以「用户ID + 接口 + 时间窗口」为 key,通过 INCR 累加访问次数,超过阈值则拒绝访问,配合过期时间实现固定窗口限流。
6. 海量布尔状态统计
利用 Bitmap 实现用户签到、在线状态、日活/月活统计、用户标签等场景,内存占用极低。例如按用户 ID 作为偏移量,记录每日签到:
# 用户ID=1001在2026年8月3日签到SETBITsign:20260810011# 统计8月全月签到天数BITCOUNTsign:202608五、最佳实践与注意事项
1. 避免大 Key 问题
- 单个 String value 建议控制在10KB 以内,过大的 value 会导致网络传输慢、Redis 命令阻塞、内存碎片严重。
- 若需存储大对象,优先考虑拆分到 Hash 结构,或进行压缩后存储。
2. 利用编码优化内存
- 数值类型直接存储为数字,不要包裹引号或冗余前缀,确保触发 int 编码,最大化节省内存。
- 短字符串场景尽量避免频繁修改,防止 embstr 转 raw 带来的内存开销。
3. 保障操作原子性
- 计数场景必须使用 INCR/DECR 系列命令,禁止使用「GET + SET」的读写分离模式,并发下会出现数据不一致。
- 设置带过期时间的 key,优先使用
SET ... EX一条命令完成,而非 SET + EXPIRE 两条命令,避免中间异常导致永久不过期。
4. 批量操作注意边界
- 多 key 读写优先使用 MSET/MGET 减少网络开销,但单次批量 key 数量建议控制在 500 以内,避免单条命令过大阻塞 Redis。
- 批量操作仅适用于同类型 key,不支持跨数据结构。
5. Bitmap 使用注意
SETBIT的 offset 是位偏移量,过大的 offset 会触发一次性内存分配,可能导致短时阻塞;避免使用超大偏移量,必要时提前分桶拆分。- Bitmap 适合海量稀疏度低的场景,稀疏度过高时内存收益反而下降。
下面逐个命令给你跑一遍实际执行流程,能直观看到命令输入、返回结果和最终效果:
例子
1. INCR:值自增 1
场景:统计文章浏览量,每有一次访问就计数 +1。
- 初始状态:Redis 中没有
article:100:views这个 key。 - 第一次执行:
INCRarticle:100:views返回结果:
(integer) 1说明:key 不存在时,默认从 0 开始自增,自增后结果为 1。
- 第二次执行(再来一次访问):
INCRarticle:100:views返回结果:
(integer) 2- 验证当前值:
GETarticle:100:views返回:
"2"2. DECR:值自减 1
场景:商品库存扣减,每卖出 1 件库存就 -1。
- 先设置初始库存 100 件:
SETstock:1001100- 卖出 1 件,执行自减:
DECRstock:1001返回结果:
(integer) 99说明:在 100 的基础上减 1,剩余库存 99。
补充:如果 key 不存在,会自动从 0 开始减:
DECRtest:notexist返回:
(integer) -13. INCRBY:增加指定整数增量
场景:给用户增加积分,一次加 10 分。
- 初始用户积分为 0(key 不存在):
INCRBYscore:user:100110返回结果:
(integer) 10说明:从 0 开始加 10,当前积分为 10。
- 再增加 20 积分:
INCRBYscore:user:100120返回结果:
(integer) 30说明:10 + 20 = 30。
4. DECRBY:减少指定整数减量
场景:一次卖出 5 件商品,批量扣减库存。
- 先重置库存为 100:
SETstock:1001100- 一次性扣减 5 件:
DECRBYstock:10015返回结果:
(integer) 95说明:100 - 5 = 95,剩余库存 95。
5. INCRBYFLOAT:增加浮点数增量
场景:调整商品价格,支持小数金额的增减。
- 设置初始价格为 10.0:
SETprice10.0- 涨价 0.5 元:
INCRBYFLOATprice0.5返回结果:
"10.5"说明:10.0 + 0.5 = 10.5,注意返回的是字符串格式的浮点数。
- 降价 1.2 元(传负数即可实现减法):
INCRBYFLOATprice-1.2返回结果:
"9.3"说明:10.5 - 1.2 = 9.3。Redis 没有专门的浮点数减法命令,用负增量就能实现。
补充注意
- 以上所有命令都是原子操作,高并发场景下不会出现计算错误;
- 如果 key 里存的不是数字(比如存了
"abc"),执行这些命令会直接报错; - INCR/DECR/INCRBY/DECRBY 只能操作整数,INCRBYFLOAT 可以操作整数和浮点数。