一、String 类型概述
String 是 Redis 最基础的数据类型:
- 所有 key 都是字符串;
- value 本质是一段连续的、二进制安全的字节序列(SDS),不关心内容是文本、整数还是图片/视频的二进制;
- 单个 value 最大 512MB。
虽然理论上能存图片、视频,但实践中不这么做:内存昂贵、大 value 会阻塞单线程、还有 512MB 上限。大文件放对象存储(OSS/MinIO/硬盘),Redis 只存 URL 或小缩略图。
二、String 的内部编码
| 内部编码 | 触发条件 |
|---|---|
| int | 值是 long 范围内的整数(64 位有符号:-2⁶³ ~ 2⁶³-1) |
| embstr | 字符串长度 ≤ 44 字节 |
| raw | 字符串长度 > 44 字节 |
127.0.0.1:6379> SET k1 12345
127.0.0.1:6379> OBJECT ENCODING k1 # "int"
127.0.0.1:6379> SET k2 hello
127.0.0.1:6379> OBJECT ENCODING k2 # "embstr"
127.0.0.1:6379> SET k3 aaaaa...(45个字符)
127.0.0.1:6379> OBJECT ENCODING k3 # "raw"
注意:int 编码一旦执行 APPEND 等字符串操作,会退化成 raw;embstr 被修改也会转 raw。
三、基础读写命令(回顾)
| 命令 | 作用 |
|---|---|
SET key value |
设置(可加 EX/PX/NX/XX 等选项) |
GET key |
获取 |
MSET k1 v1 k2 v2 ... |
批量设置,原子 |
MGET k1 k2 ... |
批量获取 |
SETEX key s v / SETNX key v |
设值+过期 / 不存在才设置 |
EXISTS / DEL / EXPIRE / TTL |
通用键命令 |
四、数值增减命令(计数器核心)
4.1 INCR / DECR
INCR key # 原子 +1,返回自增后的值
DECR key # 原子 -1
- 要求 value 是 long 范围内的整数 ,否则报错
value is not an integer or out of range; - key 不存在时,自动从 0 开始:对不存在的 key 执行 INCR,结果是 1。
4.2 INCRBY / DECRBY(指定步长)
INCRBY key 5 # +5
INCRBY key -5 # 加负数 = 减
DECRBY key 5 # -5
DECRBY key -5 # 减负数 = 加
步长也必须是整数。
4.3 INCRBYFLOAT(浮点增减)
INCRBYFLOAT key 1.5
INCRBYFLOAT key -1.5
INCRBYFLOAT key +1.5
- 参数是浮点数,支持正负号;
- 注意两点:
- 内部用 IEEE754 double 计算,有精度误差 (典型如
0.1+0.2 ≠ 0.3),金额场景要用整数(存"分")或字符串精确值,别用它; - 浮点执行后,值按字符串保存,编码变为 embstr/raw;若结果恰好是整数形式(如 113),后续仍可 INCR,是
111.5则不能 INCR。
- 内部用 IEEE754 double 计算,有精度误差 (典型如
4.4 思考题:两个连接同时发 INCR,最终自增几次?
答案:自增 2 次,结果准确(假设初值 113,最终 115),不会丢更新。
原因在于 Redis 命令执行是单线程的,两条 INCR 排队串行执行,每条 INCR 在 Redis 内部是一个不可分割的"读---改---写"原子操作:
连接A的 INCR:读取113 → 加1 → 写回114 (整条命令执行期间,不会被连接B打断)
连接B的 INCR:读取114 → 加1 → 写回115
对比你在 C/C++ 里写的 i++,它在 CPU 层面不是原子的,对应三条指令:
mov rax, [i] ; ① 从内存读到寄存器
inc rax ; ② 寄存器 +1
mov [i], rax ; ③ 写回内存
多线程下两个线程可能在①②之间交错(都读到旧值、各自 +1、互相覆盖),导致"加了两次结果只 +1"。要在多核上保证正确,必须加锁或用带 LOCK 前缀的原子指令(如 lock xadd)。
而 Redis 单线程天然把整个 INCR 串行化,无需加锁就是原子的------这正是 Redis 适合做计数器、秒杀扣库存、限流的根本原因。
五、字符串操作命令
5.1 APPEND:尾部追加
APPEND key value # 返回追加后的字符串长度
-
key 不存在时,等同于 SET;
-
对整数 int 编码追加,会先转成字符串再追加:
SET key1 113
APPEND key1 22 # 返回 5,值变成 "11322"(编码变 raw)
5.2 STRLEN:字节长度
STRLEN key # 返回字节数,不存在返回 0
⚠️ 注意是字节长度不是字符数 :
STRLEN "你好"在 UTF-8 下返回 6(每个汉字 3 字节)。
5.3 GETSET:设置新值并返回旧值
GETSET key value
- 返回旧值;key 不存在返回 nil(并设置新值);
- 典型用途:重置计数器同时取旧值;
- 6.2+ 可用
SET key value GET替代。
5.4 GETRANGE:按字节截取
GETRANGE key start end
- 截取范围是闭区间 start, end ,单位是字节(不是字符、也不是长度);
- 支持负数下标:
-1表示最后一个字节,0 -1表示全部; - 早期命令名叫 SUBSTR。
5.5 SETRANGE:按字节覆盖
SETRANGE key offset value # 从 offset 字节处开始覆盖,返回修改后的总长度
-
你的示例:
key3="value3"(位置 0~5),从位置 2 覆盖 5 个 b:v a l u e 3 v a b b b b b → "vabbbbb",长度 7 -
如果 offset 超过当前字符串长度,中间会用零字节
\x00填充:SET k ab SETRANGE k 5 xy # 结果是 "ab\x00\x00\x00xy",长度 7
GETRANGE/SETRANGE 都按字节操作,对中文等多字节字符可能切到半个字符,产生乱码,处理文本时慎用。
六、二进制安全与字节视角(重点)
6.1 Redis 存字符串不做任何字符集转换
客户端发过来什么字节,服务端就原样存什么字节,不存在"解码---转码---重新编码"的过程。这和 MySQL 形成对比:
- MySQL 有字符集概念(utf8mb3/utf8mb4 等),文本类按字符处理,读写有字符集转换;
- Redis 只把值当字节流,按字节处理,不认识字符,也就没有服务端转码。
6.2 Linux默认编码和utf8mb3
utf8mb3/utf8mb4是 MySQL 的字符集名称 ;Linux 系统的字符编码叫 locale ,现代发行版一般是UTF-8(en_US.UTF-8/C.UTF-8);- UTF-8 本身是变长 1~4 字节编码。常用汉字在 BMP 平面内,所以每个汉字占 3 字节,这和 MySQL 的 utf8mb3 只是"恰好都是 3 字节",不是一回事。
6.3 为什么 GET 中文显示成 \xHH
127.0.0.1:6379> SET key2 你好
127.0.0.1:6379> GET key2
"\xe4\xbd\xa0\xe5\xa5\xbd"
- 内存里就是 6 个字节:
你=E4 BD A0、好=E5 A5 BD; - redis-cli 默认对可打印 ASCII(0x20~0x7E)原样显示 ,其余字节用
\x加两位十六进制转义显示,所以看到的是字节的十六进制形式,不是真乱码。
6.4 --raw 选项(注意拼写)
redis-cli --raw
127.0.0.1:6379> GET key2
你好
加了之后 redis-cli 不做转义,直接输出原始字节,由你的终端按它自己的编码渲染。
6.5 按字节切中文的后果
127.0.0.1:6379> GETRANGE key2 0 -1 # 全部 → 你好
127.0.0.1:6379> GETRANGE key2 1 -2 # 切出乱码
字节序列是 E4 BD A0 E5 A5 BD(下标 0~5),1 -2 截取的是 BD A0 E5 A5,这不是合法的 UTF-8 序列,终端只能渲染成乱码------这直观说明了 Redis 按字节处理,不按字符处理。
6.6 乱码的真正来源
因为 Redis 服务端不转码,乱码只可能出现在两端编码不一致:A 终端用 GBK 写入,B 终端用 UTF-8 读取,字节按不同字符集解释就会乱。统一客户端编码(都用 UTF-8)即可。
七、危险命令:FLUSHALL / FLUSHDB
FLUSHDB # 清空当前逻辑库
FLUSHALL # 清空所有库(16 个库全删)
- 生产环境严禁使用,误执行基本等于"删库跑路",原文的警告完全正确;
- 8.0 支持后台异步清空 避免长时间阻塞:
FLUSHALL ASYNC(由后台线程释放内存,效果依然是清空,只是不阻塞); - 权限上应通过 rename-command 在配置中禁用或改名。
八、String 典型应用场景
| 场景 | 用法 |
|---|---|
| 缓存对象 | 整条 JSON 存 String,或用 Hash |
| 计数器 | INCR/DECR:文章阅读量、点赞数、库存、接口限流 |
| 秒杀扣库存 | DECR 原子扣减,配合判断返回值 |
| 分布式锁 | SET key value NX PX 毫秒 |
| 全局唯一 ID | INCR 生成全局递增整数 |
| 在线/签到统计 | Bitmap(SETBIT/BITCOUNT,String 的位操作) |
| 验证码 | SETEX code 60 验证码 |