Redis 核心数据类型:String 字符串详解
Redis 中所有的数据类型,本质上都是基于字符串类型构建的。作为最基础、最常用的类型,String 字符串不仅能存储文本、数字,还能直接保存 JSON、二进制数据,是 Redis 中功能最灵活的 "基石"。今天我们就从基础特性、常用命令、内部编码到实战场景,把 Redis String 彻底讲透。
一、String 基础特性:不止是 "字符串"
Redis 中的字符串,本质上是二进制安全的字节序列,这意味着它不会对存储的数据做任何编码层面的修改,你可以放心存储:
-
普通文本,比如用户昵称、配置信息;
-
序列化后的 JSON/XML 数据,比如用户信息、商品详情;
-
数字,既可以是普通整数,也可以是可自增 / 自减的计数器;
-
二进制数据,比如图片、音频、视频的字节流。
关键限制与细节
-
最大长度 :单个字符串的最大容量为 512MB,这个限制足以应对绝大多数业务场景;
-
编码无关性:Redis 内部以原始二进制形式存储字符串,不会主动转换字符集。客户端发送数据时使用的编码(如 UTF-8、GBK),会直接决定 Redis 中存储的内容;
-
底层通用性:列表、集合、哈希等复杂数据类型,它们的元素最终都是以字符串形式存储的,因此 String 是所有数据结构的基础。
不同场景下,String 的存储形式可以灵活变化:
-
普通字符串:直接存储文本,比如
hello; -
JSON 字符串:存储结构化数据,比如
\{\&\#34;id\&\#34;:1,\&\#34;name\&\#34;:\&\#34;James\&\#34;,\&\#34;age\&\#34;:19\}; -
数字字符串:存储可计算的数字,比如
103; -
二进制数据:存储图片、文件的原始字节流。
二、String 常用命令:从基础到进阶
Redis 为 String 提供了丰富的命令,覆盖了增删改查、批量操作、原子计数等场景,我们按功能分类梳理:
1. 基础操作命令
SET:设置键值对
SET 是最核心的写入命令,用于为指定 key 设置 value,如果 key 已存在则会直接覆盖,无论原数据是什么类型。
-
语法 :
SET key value \[EX seconds\] \[PX milliseconds\] \[NX\|XX\] -
关键选项:
-
EX seconds:设置键的过期时间,单位为秒; -
PX milliseconds:设置键的过期时间,单位为毫秒; -
NX:仅当 key 不存在时才设置(防覆盖); -
XX:仅当 key 存在时才设置(仅更新)。
-
-
示例:
redis# 基础设置 SET mykey "Hello" # 设置 10 秒过期 SET mykey "World" EX 10 # 仅不存在时设置,实现分布式锁的基础用法 SET lock_key "1" NX EX 30
GET:获取键值
GET 用于获取指定 key 对应的 value,如果 key 不存在或类型不是字符串,会返回 nil。
-
语法 :
GET key -
示例:
redisSET mykey "Hello" GET mykey # 返回 "Hello" GET non_exist_key # 返回 nil
DEL:删除键
DEL 可以删除指定的 key,无论它是什么数据类型。
-
语法 :
DEL key \[key \.\.\.\] -
示例:
redisDEL mykey DEL key1 key2 key3 # 批量删除多个键
2. 批量操作命令
当需要同时操作多个键值对时,批量命令能大幅减少网络往返次数,提升性能。
MSET:批量设置键值对
一次性设置多个 key-value 对,所有操作都是原子性的,要么全部成功,要么全部失败。
-
语法 :
MSET key value \[key value \.\.\.\] -
示例:
redisMSET key1 "a" key2 "b" key3 "c"
MGET:批量获取键值
一次性获取多个 key 对应的 value,返回结果按传入 key 的顺序排列,不存在的 key 对应 nil。
-
语法 :
MGET key \[key \.\.\.\] -
示例:
redisMGET key1 key2 non_exist_key # 返回 ["a", "b", nil]
3. 原子计数命令
Redis 的单线程特性,让这些计数命令天生具备原子性,无需额外加锁,就能实现安全的计数场景。
INCR / INCRBY:整数自增
INCR 会将 key 对应的数字值加 1;INCRBY 可以指定步长,加指定的数值。如果 key 不存在,会先初始化为 0 再执行操作。
-
语法 :
INCR key/INCRBY key increment -
示例:
redisSET counter "100" INCR counter # 返回 101 INCRBY counter 5 # 返回 106
DECR / DECRBY:整数自减
与 INCR 相反,DECR 会将数字值减 1;DECRBY 可以指定步长,减指定的数值。
-
语法 :
DECR key/DECRBY key decrement -
示例:
redisDECR counter # 返回 105 DECRBY counter 3 # 返回 102
INCRBYFLOAT:浮点数自增
针对浮点型数字的自增操作,支持正负增量,解决了普通自增命令只能处理整数的问题。
-
语法 :
INCRBYFLOAT key increment -
示例:
redisSET float_key "10.5" INCRBYFLOAT float_key 0.5 # 返回 11.0 INCRBYFLOAT float_key -2.0 # 返回 9.0
4. 字符串操作命令
APPEND:追加内容
将指定的 value 追加到已有字符串的末尾,如果 key 不存在,会自动创建一个新的键值对。
-
语法 :
APPEND key value -
示例:
redisSET mykey "Hello" APPEND mykey " World" # 返回字符串长度 11,值为 "Hello World"
GETRANGE:截取子串
获取字符串中指定范围的子串,索引从 0 开始,也支持负数索引(-1 表示最后一个字符)。
-
语法 :
GETRANGE key start end -
示例:
redisSET mykey "This is a string" GETRANGE mykey 0 3 # 返回 "This" GETRANGE mykey -6 -1 # 返回 "string"
SETRANGE:覆盖子串
从指定偏移量开始,用新的 value 覆盖原字符串的内容,如果偏移量超过原字符串长度,中间会用 \\x00 填充。
-
语法 :
SETRANGE key offset value -
示例:
redisSET mykey "Hello Redis" SETRANGE mykey 6 "World" # 从索引 6 开始替换,值变为 "Hello World"
STRLEN:获取字符串长度
返回字符串的字节长度,如果 key 不存在则返回 0。
-
语法 :
STRLEN key -
示例:
redisSTRLEN mykey # 返回 11("Hello World" 的字节长度)
命令小结:时间复杂度速查表
| 命令 | 执行效果 | 时间复杂度 |
|---|---|---|
SET key value |
设置键值对 | O(1) |
GET key |
获取键值 | O(1) |
DEL key \[key\.\.\.\] |
删除键 | O (k),k 为键的个数 |
MSET key1 v1 key2 v2\.\.\. |
批量设置键值对 | O (k),k 为键的个数 |
MGET key1 key2\.\.\. |
批量获取键值 | O (k),k 为键的个数 |
INCR/DECR key |
整数自增 / 自减 1 | O(1) |
INCRBY/DECRBY key n |
整数自增 / 自减 n | O(1) |
INCRBYFLOAT key n |
浮点数自增 / 自减 n | O(1) |
APPEND key value |
追加内容 | O (n),n 为追加内容的长度 |
STRLEN key |
获取字符串长度 | O(1) |
GETRANGE key start end |
截取子串 | O (n),n 为截取子串的长度 |
SETRANGE key offset value |
覆盖子串 | O (n),n 为 value 的长度 |
三、String 内部编码:优化存储效率
Redis 为了兼顾性能与内存占用,会根据字符串的内容和长度,自动选择 3 种不同的内部编码方式,对开发者透明:
-
int 编码 :当字符串存储的是 8 字节以内的整数时,Redis 会直接用
long类型存储,无需额外处理字符串,性能最高;redisSET key 6379 OBJECT ENCODING key # 返回 "int" -
embstr 编码 :当字符串长度小于 39 字节时,Redis 会使用
embstr编码,将对象头和字符串数据存储在同一块连续内存中,减少内存碎片,提升访问效率;redisSET key "hello" OBJECT ENCODING key # 返回 "embstr" -
raw 编码 :当字符串长度大于 39 字节时,Redis 会使用
raw编码,单独为字符串数据分配内存,以支持更大容量的存储;redisSET key "a string greater than 39 bytes ......" OBJECT ENCODING key # 返回 "raw"
四、String 典型业务场景
String 凭借灵活的特性,能覆盖绝大多数高频业务场景,以下是 4 个最经典的实战用法:
场景 1:缓存(Cache)------ 减轻数据库压力
Redis 最经典的用法之一,就是作为数据库的前置缓存,减少直接访问数据库的请求量,大幅提升系统响应速度。
以用户信息缓存为例:
-
业务流程:用户查询信息时,先从 Redis 中读取;如果缓存不存在,再从 MySQL 中查询,查询成功后写入 Redis,并设置过期时间;
-
核心代码示例:
javapublic UserInfo getUserInfo(long userId) { // 1. 拼接 Redis key String key = "user:info:" + userId; // 2. 从 Redis 中读取缓存 String value = redis.get(key); if (value != null) { // 缓存命中,直接反序列化返回 return JSON.parseObject(value, UserInfo.class); } // 3. 缓存未命中,查询数据库 UserInfo userInfo = mysql.query("select * from user where id = ?", userId); if (userInfo != null) { // 写入 Redis,设置 1 小时过期 redis.setex(key, 3600, JSON.toJSONString(userInfo)); } return userInfo; } -
优势:理想情况下,每个用户信息 1 小时内只会触发一次数据库查询,极大降低了数据库压力;同时 Redis 的高性能也让接口响应速度提升数倍。
注意:缓存 key 的命名建议使用 "业务名:数据类型:主键" 的格式,比如
user:info:1001,方便维护和批量管理。
场景 2:计数器(Counter)------ 实现高频计数
String 的原子计数命令,是实现计数器的完美方案,常见场景包括视频播放量、商品库存、接口访问次数统计等。
以视频播放量统计为例:
-
业务流程:用户每次播放视频时,调用
INCR命令给视频对应的 key 加 1;后续可以定时将 Redis 中的计数同步到数据库; -
核心代码示例:
javapublic void addVideoView(long videoId) { String key = "video:view:" + videoId; // 原子自增 1 redis.incr(key); } public long getVideoView(long videoId) { String key = "video:view:" + videoId; String value = redis.get(key); return value != null ? Long.parseLong(value) : 0; } -
优势:
INCR命令是原子性的,无需额外加锁,就能应对高并发场景,不会出现计数错误。
场景 3:Session 共享 ------ 分布式会话存储
在分布式 Web 架构中,用户的 Session 信息如果只存在单台服务器上,会导致负载均衡切换服务器时用户会话丢失。用 Redis 存储 Session 信息,就能实现多服务器间的会话共享。
-
业务流程:用户登录成功后,将 Session 信息写入 Redis;后续所有服务器节点都从 Redis 中读取 Session 信息,实现会话统一管理;
-
核心优势:
-
所有服务器节点共享同一份 Session 数据,用户会话不会因服务器切换而失效;
-
Redis 可以设置 Session 过期时间,自动清理无效会话,无需手动维护;
-
支持集群部署,扩展性远优于单机 Session 存储。
-
场景 4:短信验证码 ------ 防刷与时效控制
用户登录 / 注册场景中,短信验证码的存储和防刷,是 String 类型的高频用法:
-
业务需求:验证码有效期 5 分钟,且 1 分钟内只能发送 5 次;
-
核心代码示例:
java// 发送验证码 public boolean sendSmsCode(String phoneNumber) { String countKey = "sms:count:" + phoneNumber; // 检查 1 分钟内发送次数 Long count = redis.incr(countKey); if (count == 1) { // 第一次发送,设置 1 分钟过期 redis.expire(countKey, 60); } if (count > 5) { // 超过 1 分钟 5 次限制,返回失败 return false; } // 生成验证码并写入 Redis,设置 5 分钟过期 String code = generateCode(); String codeKey = "sms:code:" + phoneNumber; redis.setex(codeKey, 300, code); // 调用短信服务发送验证码 smsService.send(phoneNumber, code); return true; } // 校验验证码 public boolean verifySmsCode(String phoneNumber, String code) { String codeKey = "sms:code:" + phoneNumber; String correctCode = redis.get(codeKey); if (correctCode == null) { // 验证码已过期 return false; } return correctCode.equals(code); } -
优势:通过
INCR+ 过期时间实现防刷控制,通过SET+ 过期时间实现验证码的时效管理,逻辑清晰且性能高效。
五、总结
Redis String 字符串,看似简单,实则是 Redis 中功能最全面、应用最广泛的数据类型:
-
它是二进制安全的,能存储文本、数字、JSON、二进制数据;
-
丰富的命令覆盖了增删改查、批量操作、原子计数、字符串处理等所有场景;
-
自动适配的内部编码,在不同场景下都能保证性能与内存效率;
-
缓存、计数器、Session 共享、验证码存储等高频业务场景,都能通过 String 轻松实现。
理解 String 字符串的特性与用法,是掌握 Redis 的第一步,也是后续学习其他复杂数据类型的基础。