别被“数据结构”三个字吓到,String 就是你熟悉的字符串,只不过它能自动做加减法。
本次导航:
- String 在 Redis 里长什么样(内部编码偷偷帮你省内存)
- 最常用的命令:
SET、GET、INCR、MSET、SETNX…… - 实战场景:计数器、缓存 Session、分布式锁的基础
- 几个容易踩的坑
发车前温馨提醒:
如果你还没把 Redis 跑起来,建议先看上一篇:
有了一个能敲命令的 Redis,咱们就可以进行实操了。
一、String:最不起眼但用得最多的类型
String 是 Redis 最基础的数据类型。基础到什么程度呢?你随便用一个缓存,八成存的就是 String。
几个特点:
- 二进制安全:可以存图片、序列化对象、JSON 字符串,随便什么字节流。
- 最大容量 512 MB——够用了,别真往里塞视频就行。
- 内部会根据值的类型和长度,自动选用三种编码之一来省内存。
内部编码
| 编码 | 条件 | 说明 |
|---|---|---|
| int | 值是 64 位有符号整数 | 直接用 long 存储,不额外分配内存 |
| embstr | 字符串长度 ≤ 44 字节 | 一次分配连续内存,读取快 |
| raw | 字符串长度 > 44 字节 | 两次分配,适合大字符串 |
你可以用 OBJECT ENCODING key 看一眼当前 key 用的什么编码:
SET counter 100
OBJECT ENCODING counter # 输出 "int"
SET short "hello"
OBJECT ENCODING short # 输出 "embstr"
SET long "这里写一个超过44个字符的字符串看看会发生什么..."
OBJECT ENCODING long # 输出 "raw"
Redis 自己会帮你选最优解,你只需要 SET 和 GET 就行。
二、常用命令:从存读到加减乘除
基础三件套
SET name "zhangsan" # 存
GET name # 取 "zhangsan"
DEL name # 删
批量操作(少跑几次网络,省时间)
MSET a 1 b 2 c 3
MGET a b c # 返回 ["1","2","3"]
数字专用:原子加减
SET views 0
INCR views # 1
INCR views # 2
INCRBY views 10 # 12
DECR views # 11
DECRBY views 5 # 6
原子性:多个客户端同时 INCR 同一个 key,Redis 会排队一个个执行,不会出现“加了一次只加了半次”的情况。这事很重要,后面做计数器全靠它。
带过期时间的写入(缓存标配)
# 写法一:SETEX
SETEX session:1001 3600 "user_data_json"
# 写法二:SET 带 EX 参数(推荐,更统一)
SET session:1001 "user_data_json" EX 3600
# 查看剩余存活时间(秒)
TTL session:1001
不存在才写入(分布式锁的基础)
SET lock:order_001 "locked" NX
# 如果 key 已存在,返回 nil,写入失败
NX 的意思是 "Not eXists"。
从 Redis 2.6.12 开始,SET 命令已经集成了 NX、EX、PX 等参数,不用再分开写 SETNX + EXPIRE 了。
三、实战场景:String 能帮你干什么
场景1:计数器(文章阅读量、限流)
# 用户点开一篇文章
INCR article:10086:views
# 获取阅读量
GET article:10086:views # 输出 "12345"
限流:比如限制每个 IP 每分钟最多访问 10 次
# 伪代码:每次请求执行下面两步
current = INCR rate_limit:192.168.1.1
if current == 1:
EXPIRE rate_limit:192.168.1.1 60
if current > 10:
return "请求太频繁"
场景2:缓存 Session(存用户信息)
用户登录后,把用户对象序列化成 JSON 字符串存进去,设置过期时间:
SET user:session:abc123 '{"id":1001,"name":"zhangsan","role":"admin"}' EX 7200
下次请求来了,GET 出来反序列化,不用查数据库。
场景3:分布式锁(基础版)
多个服务同时抢一个锁,谁 SET NX 成功谁就拿到:
# 抢锁(5秒自动释放,防止死锁)
SET lock:order_pay "node1" NX EX 5
# 返回 OK 表示抢到,返回 nil 表示被别人占了
释放锁的时候直接 DEL。
注意:这个基础版有个小问题——可能误删别人的锁(比如锁超时了,A 刚拿到,B 删了)。生产环境要用更严谨的 Lua 脚本版本,后面会有专门的文章讲。
四、几个容易踩的坑
坑1:KEYS * 别在生产环境用
KEYS * 会遍历所有 key,数据量大的时候 Redis 直接卡住几秒。
想看有哪些 key,用 SCAN 游标迭代:
SCAN 0 MATCH user:* COUNT 100
坑2:String 存大 JSON 要谨慎
一个 key 塞几十 KB 甚至 MB 的 JSON,会拖慢整个 Redis(读写大对象耗 CPU,网络传输也慢)。
解决办法:拆成 Hash 存,或者压缩后再存。
坑3:数字不要超过 2^63-1
Redis 的 int 编码只支持 64 位有符号整数(-9223372036854775808 到 9223372036854775807)。
超过这个范围,会转成字符串存储,但 INCR 会报错。
127.0.0.1:6379> SET big 9223372036854775807
OK
127.0.0.1:6379> INCR big
(error) ERR increment or decrement would overflow
坑4:忘了设过期时间,内存爆炸
缓存场景一定要给 key 加 EX 或 EXPIRE,否则 Redis 会变成“永不删除的数据库”,直到内存撑爆。
五、小结
String 是 Redis 的“万金油”:
- 存文本、数字、序列化对象都行
- 数字操作用
INCR/DECR,原子性保证不错乱 - 批量操作用
MSET/MGET省网络 - 分布式锁靠
SET NX EX - 缓存记得设过期时间
你不需要记住所有命令,记住 SET、GET、INCR、MSET、SET NX EX 这五个,就能应付 80% 的场景。
OK,本次导航结束,下次继续。