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 存在时才设置(仅更新)。
示例:
# 基础设置 SET mykey "Hello" # 设置 10 秒过期 SET mykey "World" EX 10 # 仅不存在时设置,实现分布式锁的基础用法 SET lock_key "1" NX EX 30
GET:获取键值
GET用于获取指定 key 对应的 value,如果 key 不存在或类型不是字符串,会返回nil。
语法:
GET key示例:
SET mykey "Hello" GET mykey # 返回 "Hello" GET non_exist_key # 返回 nil
DEL:删除键
DEL可以删除指定的 key,无论它是什么数据类型。
语法:
DEL key \[key \.\.\.\]示例:
DEL mykey DEL key1 key2 key3 # 批量删除多个键
2. 批量操作命令
当需要同时操作多个键值对时,批量命令能大幅减少网络往返次数,提升性能。
MSET:批量设置键值对
一次性设置多个 key-value 对,所有操作都是原子性的,要么全部成功,要么全部失败。
语法:
MSET key value \[key value \.\.\.\]示例:
MSET key1 "a" key2 "b" key3 "c"
MGET:批量获取键值
一次性获取多个 key 对应的 value,返回结果按传入 key 的顺序排列,不存在的 key 对应nil。
语法:
MGET key \[key \.\.\.\]示例:
MGET key1 key2 non_exist_key # 返回 ["a", "b", nil]
3. 原子计数命令
Redis 的单线程特性,让这些计数命令天生具备原子性,无需额外加锁,就能实现安全的计数场景。
INCR / INCRBY:整数自增
INCR会将 key 对应的数字值加 1;INCRBY可以指定步长,加指定的数值。如果 key 不存在,会先初始化为 0 再执行操作。
语法:
INCR key/INCRBY key increment示例:
SET counter "100" INCR counter # 返回 101 INCRBY counter 5 # 返回 106
DECR / DECRBY:整数自减
与INCR相反,DECR会将数字值减 1;DECRBY可以指定步长,减指定的数值。
语法:
DECR key/DECRBY key decrement示例:
DECR counter # 返回 105 DECRBY counter 3 # 返回 102
INCRBYFLOAT:浮点数自增
针对浮点型数字的自增操作,支持正负增量,解决了普通自增命令只能处理整数的问题。
语法:
INCRBYFLOAT key increment示例:
SET 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示例:
SET mykey "Hello" APPEND mykey " World" # 返回字符串长度 11,值为 "Hello World"
GETRANGE:截取子串
获取字符串中指定范围的子串,索引从 0 开始,也支持负数索引(-1 表示最后一个字符)。
语法:
GETRANGE key start end示例:
SET mykey "This is a string" GETRANGE mykey 0 3 # 返回 "This" GETRANGE mykey -6 -1 # 返回 "string"
SETRANGE:覆盖子串
从指定偏移量开始,用新的 value 覆盖原字符串的内容,如果偏移量超过原字符串长度,中间会用\\x00填充。
语法:
SETRANGE key offset value示例:
SET mykey "Hello Redis" SETRANGE mykey 6 "World" # 从索引 6 开始替换,值变为 "Hello World"
STRLEN:获取字符串长度
返回字符串的字节长度,如果 key 不存在则返回 0。
语法:
STRLEN key示例:
STRLEN 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类型存储,无需额外处理字符串,性能最高;SET key 6379 OBJECT ENCODING key # 返回 "int"embstr 编码:当字符串长度小于 39 字节时,Redis 会使用
embstr编码,将对象头和字符串数据存储在同一块连续内存中,减少内存碎片,提升访问效率;SET key "hello" OBJECT ENCODING key # 返回 "embstr"raw 编码:当字符串长度大于 39 字节时,Redis 会使用
raw编码,单独为字符串数据分配内存,以支持更大容量的存储;SET key "a string greater than 39 bytes ......" OBJECT ENCODING key # 返回 "raw"
四、String 典型业务场景
String 凭借灵活的特性,能覆盖绝大多数高频业务场景,以下是 4 个最经典的实战用法:
场景 1:缓存(Cache)—— 减轻数据库压力
Redis 最经典的用法之一,就是作为数据库的前置缓存,减少直接访问数据库的请求量,大幅提升系统响应速度。
以用户信息缓存为例:
业务流程:用户查询信息时,先从 Redis 中读取;如果缓存不存在,再从 MySQL 中查询,查询成功后写入 Redis,并设置过期时间;
核心代码示例:
publicUserInfogetUserInfo(longuserId){// 1. 拼接 Redis keyStringkey="user:info:"+userId;// 2. 从 Redis 中读取缓存Stringvalue=redis.get(key);if(value!=null){// 缓存命中,直接反序列化返回returnJSON.parseObject(value,UserInfo.class);}// 3. 缓存未命中,查询数据库UserInfouserInfo=mysql.query("select * from user where id = ?",userId);if(userInfo!=null){// 写入 Redis,设置 1 小时过期redis.setex(key,3600,JSON.toJSONString(userInfo));}returnuserInfo;}优势:理想情况下,每个用户信息 1 小时内只会触发一次数据库查询,极大降低了数据库压力;同时 Redis 的高性能也让接口响应速度提升数倍。
注意:缓存 key 的命名建议使用 “业务名:数据类型:主键” 的格式,比如
user:info:1001,方便维护和批量管理。
场景 2:计数器(Counter)—— 实现高频计数
String 的原子计数命令,是实现计数器的完美方案,常见场景包括视频播放量、商品库存、接口访问次数统计等。
以视频播放量统计为例:
业务流程:用户每次播放视频时,调用
INCR命令给视频对应的 key 加 1;后续可以定时将 Redis 中的计数同步到数据库;核心代码示例:
publicvoidaddVideoView(longvideoId){Stringkey="video:view:"+videoId;// 原子自增 1redis.incr(key);}publiclonggetVideoView(longvideoId){Stringkey="video:view:"+videoId;Stringvalue=redis.get(key);returnvalue!=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 次;
核心代码示例:
// 发送验证码publicbooleansendSmsCode(StringphoneNumber){StringcountKey="sms:count:"+phoneNumber;// 检查 1 分钟内发送次数Longcount=redis.incr(countKey);if(count==1){// 第一次发送,设置 1 分钟过期redis.expire(countKey,60);}if(count>5){// 超过 1 分钟 5 次限制,返回失败returnfalse;}// 生成验证码并写入 Redis,设置 5 分钟过期Stringcode=generateCode();StringcodeKey="sms:code:"+phoneNumber;redis.setex(codeKey,300,code);// 调用短信服务发送验证码smsService.send(phoneNumber,code);returntrue;}// 校验验证码publicbooleanverifySmsCode(StringphoneNumber,Stringcode){StringcodeKey="sms:code:"+phoneNumber;StringcorrectCode=redis.get(codeKey);if(correctCode==null){// 验证码已过期returnfalse;}returncorrectCode.equals(code);}优势:通过
INCR+ 过期时间实现防刷控制,通过SET+ 过期时间实现验证码的时效管理,逻辑清晰且性能高效。
五、总结
Redis String 字符串,看似简单,实则是 Redis 中功能最全面、应用最广泛的数据类型:
它是二进制安全的,能存储文本、数字、JSON、二进制数据;
丰富的命令覆盖了增删改查、批量操作、原子计数、字符串处理等所有场景;
自动适配的内部编码,在不同场景下都能保证性能与内存效率;
缓存、计数器、Session 共享、验证码存储等高频业务场景,都能通过 String 轻松实现。
理解 String 字符串的特性与用法,是掌握 Redis 的第一步,也是后续学习其他复杂数据类型的基础。