Redis 核心数据类型之 String 详解

Redis 核心数据类型:String 字符串详解

Redis 中所有的数据类型,本质上都是基于字符串类型构建的。作为最基础、最常用的类型,String 字符串不仅能存储文本、数字,还能直接保存 JSON、二进制数据,是 Redis 中功能最灵活的 "基石"。今天我们就从基础特性、常用命令、内部编码到实战场景,把 Redis String 彻底讲透。


一、String 基础特性:不止是 "字符串"

Redis 中的字符串,本质上是二进制安全的字节序列,这意味着它不会对存储的数据做任何编码层面的修改,你可以放心存储:

  • 普通文本,比如用户昵称、配置信息;

  • 序列化后的 JSON/XML 数据,比如用户信息、商品详情;

  • 数字,既可以是普通整数,也可以是可自增 / 自减的计数器;

  • 二进制数据,比如图片、音频、视频的字节流。

关键限制与细节

  1. 最大长度 :单个字符串的最大容量为 512MB,这个限制足以应对绝大多数业务场景;

  2. 编码无关性:Redis 内部以原始二进制形式存储字符串,不会主动转换字符集。客户端发送数据时使用的编码(如 UTF-8、GBK),会直接决定 Redis 中存储的内容;

  3. 底层通用性:列表、集合、哈希等复杂数据类型,它们的元素最终都是以字符串形式存储的,因此 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

  • 示例

    redis 复制代码
    SET mykey "Hello"
    GET mykey  # 返回 "Hello"
    GET non_exist_key  # 返回 nil
DEL:删除键

DEL 可以删除指定的 key,无论它是什么数据类型。

  • 语法DEL key \[key \.\.\.\]

  • 示例

    redis 复制代码
    DEL mykey
    DEL key1 key2 key3  # 批量删除多个键

2. 批量操作命令

当需要同时操作多个键值对时,批量命令能大幅减少网络往返次数,提升性能。

MSET:批量设置键值对

一次性设置多个 key-value 对,所有操作都是原子性的,要么全部成功,要么全部失败。

  • 语法MSET key value \[key value \.\.\.\]

  • 示例

    redis 复制代码
    MSET key1 "a" key2 "b" key3 "c"
MGET:批量获取键值

一次性获取多个 key 对应的 value,返回结果按传入 key 的顺序排列,不存在的 key 对应 nil

  • 语法MGET key \[key \.\.\.\]

  • 示例

    redis 复制代码
    MGET key1 key2 non_exist_key  # 返回 ["a", "b", nil]

3. 原子计数命令

Redis 的单线程特性,让这些计数命令天生具备原子性,无需额外加锁,就能实现安全的计数场景。

INCR / INCRBY:整数自增

INCR 会将 key 对应的数字值加 1;INCRBY 可以指定步长,加指定的数值。如果 key 不存在,会先初始化为 0 再执行操作。

  • 语法INCR key / INCRBY key increment

  • 示例

    redis 复制代码
    SET counter "100"
    INCR counter       # 返回 101
    INCRBY counter 5   # 返回 106
DECR / DECRBY:整数自减

INCR 相反,DECR 会将数字值减 1;DECRBY 可以指定步长,减指定的数值。

  • 语法DECR key / DECRBY key decrement

  • 示例

    redis 复制代码
    DECR counter       # 返回 105
    DECRBY counter 3   # 返回 102
INCRBYFLOAT:浮点数自增

针对浮点型数字的自增操作,支持正负增量,解决了普通自增命令只能处理整数的问题。

  • 语法INCRBYFLOAT key increment

  • 示例

    redis 复制代码
    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

  • 示例

    redis 复制代码
    SET mykey "Hello"
    APPEND mykey " World"  # 返回字符串长度 11,值为 "Hello World"
GETRANGE:截取子串

获取字符串中指定范围的子串,索引从 0 开始,也支持负数索引(-1 表示最后一个字符)。

  • 语法GETRANGE key start end

  • 示例

    redis 复制代码
    SET mykey "This is a string"
    GETRANGE mykey 0 3   # 返回 "This"
    GETRANGE mykey -6 -1 # 返回 "string"
SETRANGE:覆盖子串

从指定偏移量开始,用新的 value 覆盖原字符串的内容,如果偏移量超过原字符串长度,中间会用 \\x00 填充。

  • 语法SETRANGE key offset value

  • 示例

    redis 复制代码
    SET mykey "Hello Redis"
    SETRANGE mykey 6 "World"  # 从索引 6 开始替换,值变为 "Hello World"
STRLEN:获取字符串长度

返回字符串的字节长度,如果 key 不存在则返回 0。

  • 语法STRLEN key

  • 示例

    redis 复制代码
    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 种不同的内部编码方式,对开发者透明:

  1. int 编码 :当字符串存储的是 8 字节以内的整数时,Redis 会直接用 long 类型存储,无需额外处理字符串,性能最高;

    redis 复制代码
    SET key 6379
    OBJECT ENCODING key  # 返回 "int"
  2. embstr 编码 :当字符串长度小于 39 字节时,Redis 会使用 embstr 编码,将对象头和字符串数据存储在同一块连续内存中,减少内存碎片,提升访问效率;

    redis 复制代码
    SET key "hello"
    OBJECT ENCODING key  # 返回 "embstr"
  3. raw 编码 :当字符串长度大于 39 字节时,Redis 会使用 raw 编码,单独为字符串数据分配内存,以支持更大容量的存储;

    redis 复制代码
    SET key "a string greater than 39 bytes ......"
    OBJECT ENCODING key  # 返回 "raw"

四、String 典型业务场景

String 凭借灵活的特性,能覆盖绝大多数高频业务场景,以下是 4 个最经典的实战用法:

场景 1:缓存(Cache)------ 减轻数据库压力

Redis 最经典的用法之一,就是作为数据库的前置缓存,减少直接访问数据库的请求量,大幅提升系统响应速度。

以用户信息缓存为例:

  1. 业务流程:用户查询信息时,先从 Redis 中读取;如果缓存不存在,再从 MySQL 中查询,查询成功后写入 Redis,并设置过期时间;

  2. 核心代码示例:

    java 复制代码
    public 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;
    }
  3. 优势:理想情况下,每个用户信息 1 小时内只会触发一次数据库查询,极大降低了数据库压力;同时 Redis 的高性能也让接口响应速度提升数倍。

注意:缓存 key 的命名建议使用 "业务名:数据类型:主键" 的格式,比如 user:info:1001,方便维护和批量管理。

场景 2:计数器(Counter)------ 实现高频计数

String 的原子计数命令,是实现计数器的完美方案,常见场景包括视频播放量、商品库存、接口访问次数统计等。

以视频播放量统计为例:

  1. 业务流程:用户每次播放视频时,调用 INCR 命令给视频对应的 key 加 1;后续可以定时将 Redis 中的计数同步到数据库;

  2. 核心代码示例:

    java 复制代码
    public 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;
    }
  3. 优势:INCR 命令是原子性的,无需额外加锁,就能应对高并发场景,不会出现计数错误。

场景 3:Session 共享 ------ 分布式会话存储

在分布式 Web 架构中,用户的 Session 信息如果只存在单台服务器上,会导致负载均衡切换服务器时用户会话丢失。用 Redis 存储 Session 信息,就能实现多服务器间的会话共享。

  1. 业务流程:用户登录成功后,将 Session 信息写入 Redis;后续所有服务器节点都从 Redis 中读取 Session 信息,实现会话统一管理;

  2. 核心优势:

    • 所有服务器节点共享同一份 Session 数据,用户会话不会因服务器切换而失效;

    • Redis 可以设置 Session 过期时间,自动清理无效会话,无需手动维护;

    • 支持集群部署,扩展性远优于单机 Session 存储。

场景 4:短信验证码 ------ 防刷与时效控制

用户登录 / 注册场景中,短信验证码的存储和防刷,是 String 类型的高频用法:

  1. 业务需求:验证码有效期 5 分钟,且 1 分钟内只能发送 5 次;

  2. 核心代码示例:

    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);
    }
  3. 优势:通过 INCR + 过期时间实现防刷控制,通过 SET + 过期时间实现验证码的时效管理,逻辑清晰且性能高效。


五、总结

Redis String 字符串,看似简单,实则是 Redis 中功能最全面、应用最广泛的数据类型:

  • 它是二进制安全的,能存储文本、数字、JSON、二进制数据;

  • 丰富的命令覆盖了增删改查、批量操作、原子计数、字符串处理等所有场景;

  • 自动适配的内部编码,在不同场景下都能保证性能与内存效率;

  • 缓存、计数器、Session 共享、验证码存储等高频业务场景,都能通过 String 轻松实现。

理解 String 字符串的特性与用法,是掌握 Redis 的第一步,也是后续学习其他复杂数据类型的基础。

相关推荐
老纪2 小时前
Redis怎样利用Lua为多个Key同步续期
jvm·数据库·python
2403_883261092 小时前
C#怎么计算两个日期的差值_C#如何处理时间跨度【笔记】
jvm·数据库·python
m0_740653222 小时前
Golang切片底层原理是怎样的_Golang切片实现原理教程【简明】
jvm·数据库·python
yexuhgu2 小时前
CSS如何处理CSS逻辑属性兼容性_通过PostCSS转译为物理属性
jvm·数据库·python
m0_624578592 小时前
CSS如何给Bootstrap背景添加半透明层_使用rgba颜色模式与定位
jvm·数据库·python
m0_470857642 小时前
CSS如何实现等宽表格布局_利用table-layout与盒模型
jvm·数据库·python
kexnjdcncnxjs2 小时前
HTML 中使用 EXIF.js 读取图片元数据失败的常见原因与解决方案
jvm·数据库·python
iuvtsrt2 小时前
Python如何实现定时异步任务_结合asyncio与loop.call_later调用
jvm·数据库·python
m0_463672202 小时前
HTML怎么标注成就连续打卡中断_HTML“断连,重新开始”提示【方法】
jvm·数据库·python