目录

一.常见命令 

1.1.SET

1.2.GET

1.3.MGET  

1.4.MSET  

1.5.SETNX

二.计数命令

2.1.INCR  

2.2.INCRBY

2.3.DECR

2.4.DECYBY

2.5.INCRBYFLOAT

三 . 其他命令

3.1.APPEND

3.2.GETRANGE

3.3.SETRANGE

3.4.STRLEN  

四. 字符串类型内部编码

五. 典型使用场景

5.1.缓存(Cache)功能

5.2.计数(Counter)功能

5.3.共享会话(Session)

5.4.⼿机验证码


字符串(String)是 Redis 最基础、最核心的数据类型,理解其特性至关重要:

  1. 基础构建块:

    • 键的类型: Redis 中所有的键 (Key) 本质上都是字符串类型

    • 值的基石: 其他复杂数据结构(列表 List、集合 Set、有序集合 Sorted Set、哈希 Hash)的元素值 (Value) 也必须是字符串类型。这使得字符串成为学习和理解 Redis 整个数据模型的基础。

  2. 灵活的值类型 (二进制安全):

    • Redis 字符串类型存储的值 (Value) 具有极高的灵活性,可以是:

      • 文本数据: 普通字符串、JSON、XML、CSV 等格式的文本。

      • 数值数据: 整数或浮点数(Redis 提供专门的命令如 INCRDECRINCRBYFLOAT 来操作数值)。

      • 二进制数据: 如图片、音频、视频文件或任何序列化的字节流。

    • 关键特性:二进制安全 (Binary Safe)

      • Redis 内部存储和操作字符串值完全基于原始字节序列(二进制流)

      • 这意味着 Redis 本身不感知、也不处理任何字符集编码(如 UTF-8, GBK)问题

      • 客户端存入什么编码的字节流,Redis 就原样存储;客户端读取时,也将得到完全相同的字节流。字符集的解析完全由客户端负责。

    • 容量限制: 单个字符串值的最大容量为 512MB

核心价值与应用场景:

  • 缓存 (Cache): 存储 HTML 片段、用户会话信息、序列化对象等。

  • 计数器 (Counter): 利用 INCR/DECR 命令实现点击计数、库存计数等。

  • 分布式锁 (Distributed Lock): 配合 SET 命令的 NX/PX 等选项实现简单锁机制。

  • 配置存储: 存储应用配置项。

  • 二进制数据存储: 存储小型图片、验证码、序列化数据等(需注意 512MB 限制)。

总结: Redis 字符串类型不仅是键的载体,更是所有复杂数据结构元素的基石。其 二进制安全 的特性赋予了它存储任意数据的强大能力,而 512MB 的上限 和 丰富的操作命令 使其成为 Redis 最常用、最灵活的数据类型之一。

一.常见命令 

1.1.SET

功能: 将字符串类型的值设置到指定的键中。

  • 覆盖规则: 如果键已存在,无论其原先存储的数据类型是什么(字符串、列表、哈希等),都会被覆盖为新的字符串值。

  • TTL 处理: 原先与该键关联的任何生存时间(TTL)都会被清除失效

语法:

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

版本要求: 1.0.0 起可用

时间复杂度: O(1)

选项:

SET 命令提供多种选项来控制其行为:

  • EX seconds: 设置键的过期时间,单位是

  • PX milliseconds: 设置键的过期时间,单位是毫秒

  • NX: 仅当键不存在 (Not eXists) 时才执行设置操作。如果键已存在,则设置操作不会执行

  • XX: 仅当键存在 (eXists) 时才执行设置操作。如果键不存在,则设置操作不会执行

功能整合: 上面这些新选项使得单个 SET 命令能够完全覆盖 SETNXSETEXPSETEX 的功能:

  • SET key value NX 等价于 SETNX key value

  • SET key value EX seconds 等价于 SETEX key seconds value

  • SET key value PX milliseconds 等价于 PSETEX key milliseconds value

重要说明:

  1. 互斥性: NX 和 XX 选项是互斥的,同一命令中只能指定其中一个。

  2. 命令演变: 由于 SET 命令结合选项 (NXXXEXPX) 可以完全替代 SETNXSETEXPSETEX 等独立命令的功能,在 Redis 的后续版本中,这些独立命令的实现已被整合到 SET 命令中。虽然这些独立命令目前通常仍保留可用(以兼容旧脚本或习惯),但推荐优先使用带选项的 SET 命令,因为它功能更统一、强大。

返回值:

  • 如果设置操作成功执行,返回字符串 "OK"

  • 如果因为指定了 NX 或 XX 选项但条件不满足(即 NX 时键已存在,或 XX 时键不存在),导致设置操作未执行,则返回 (nil)

话不多说,我们看看例子

NX选项

可以看到啊!!上面这个SET mykey "World" NX并没有生效啊!!!这个是因为

NX: 仅当键不存在 (Not eXists) 时才执行设置操作。如果键已存在,则设置操作不会执行

 我们看看它生效的样子

XX选项

 

我们看到:上面这个SET mykey "World" XX并没有生效啊!!!这个是因为

XX: 仅当键存在 (eXists) 时才执行设置操作。如果键不存在,则设置操作不会执行

我们来看看它执行的样子

EX选项

至于这个PX选项,由于时间太短了,我们就不演示了。

1.2.GET

获取 key 对应的 value。

如果 key 不存在,返回 nil。

如果 value 的数据类型不是 string,会报错。

语法:

GET key
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:key 对应的 value,或者 nil 当 key 不存在。

1.3.MGET  

一次性获取多个 key 的值。

如果对应的 key 不存在或者对应的数据类型不是 string,返回 nil。

语法:

MGET key [key ...]
  • 命令有效版本:1.0.0 之后  
  • 时间复杂度:O(N) ,N 是我们这个命令里要获取的 key 数量  ,不是redis里面所有key的数量
  • 返回值:对应 value 的列表

我们来对比一下这个get命令和mget命令啊!!!

很明显啊,聪明人都会选项mget来获取value。

我们看看怎么使用

1.4.MSET  

一次性设置多个 key 的值。  

覆盖规则:

  • 如果指定的 key 已存在,无论其原先存储的数据类型是什么(字符串、列表、哈希、集合、有序集合等),MSET 都会将其覆盖为新的字符串值

  • 这是由 MSET 的设计目标决定的:它是一个原子性的批量设置字符串值的命令。

TTL 处理:

  • 对于 MSET 命令中指定的、已经存在的 key,原先与该键关联的任何生存时间(TTL)都会被清除失效。执行 MSET 后,这些键将变成永久的(没有过期时间),除非后续显式地为它们设置新的 TTL(例如使用 EXPIRE 或 SET 的 EX/PX 选项)。

  • 对于 MSET 命令中指定的、之前不存在的 key,设置后会成为永久键(没有 TTL),除非后续显式设置。

语法:  

MSET key value [key value ...]  
  • 命令有效版本:1.0.1 之后  
  • 时间复杂度:O(N)
  • N 是 key 数量  
  • 返回值:永远是 OK  

多说无益,我们直接上手看例子

这就很好的说明了:如果指定的 key 已存在,无论其原先存储的数据类型是什么(字符串、列表、哈希、集合、有序集合等),MSET 都会将其覆盖为新的字符串值

接下来我们接着看

对于 MSET 命令中指定的、之前不存在的 key,设置后会成为永久键(没有 TTL),除非后续显式设置。

1.5.SETNX

设置key-value但只允许在key之前不存在的情况下。

语法:

SETNX key value

命令有效版本:1.0.0之后

时间复杂度:O(1)

返回值:1表⽰设置成功。0表⽰没有设置。

话不多说,我们直接看例子

这个就是没有设置成功的情况,我们接下来看看设置成功的情况

SET、SETNX、SETXX执⾏流程

二.计数命令

2.1.INCR  

功能:

将键 key 中存储的整数值增加 1。

关键行为:

  • 如果键 key 不存在,则在执行递增操作前,先将该键的值设置为 0

  • 如果键 key 存储的值不是整数类型(无法解析为整数),则命令返回错误。

  • 如果键 key 存储的值是整数,但递增后超出 64 位有符号整数的表示范围(即 C/C++ 中的 long long 类型,范围从 -9223372036854775808 到 9223372036854775807),则命令返回错误。

原子性:
此命令是原子操作。在分布式环境下,多个客户端同时对同一个键执行 INCR 时,Redis 的单线程执行模型确保该命令不会出现竞态条件,每个操作都会顺序执行,因此非常适合实现高并发场景下的计数器。

与浮点数的区别:
INCR 命令仅适用于整数值。若需操作浮点数,请使用 INCRBYFLOAT 命令。

语法:

INCR key
  • 命令有效版本:1.0.0 之后  
  • 时间复杂度:O(1)  
  • 返回值:integer 类型的加完后的数值。

错误情况:

  • 当 key 存储的不是字符串类型时,返回类型错误。

  • 当 key 存储的字符串不能表示为整数,或操作后导致整数溢出时,返回错误。

话不多说,我们直接看例子

将键 key 中存储的整数值增加 1。

如果键 key 不存在,则在执行递增操作前,先将该键的值设置为 0

当 key 存储的字符串不能表示为整数,返回错误

当 key 存储的字符串操作后导致整数溢出时,返回错误

2.2.INCRBY

将 key 对应的 string 表示的数字加上对应的值。

如果 key 不存在,则视为 key 对应的 value 是 0。

如果 key 对应的 string 不是一个整型或者范围超过了 64 位有符号整型,则报错。

语法:

INCRBY key decrement
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:integer 类型的加完后的数值。

错误情况:

  1. decrement不是整数时

  2. 当 key 存储的不是字符串类型时

  3. 当 key 存储的字符串不能表示为整数时

  4. 当操作结果超出 64 位有符号整数范围时

话不多说,我们直接看例子

2.3.DECR

将 key 对应的 string 表示的数字减一。

如果 key 不存在,则视为 key 对应的 value 是 0。

如果 key 对应的 string 不是一个整型或者范围超过了 64 位有符号整型,则报错。

语法:

DECR key
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:integer 类型的减完后的数值。

话不多说,我们直接看例子

2.4.DECYBY

将 key 对应的 string 表示的数字减去对应的值。

如果 key 不存在,则视为 key 对应的 value 是 0。

如果 key 对应的 string 不是一个整型或者范围超过了 64 位有符号整型,则报错。

语法:

DECBBY key decrement
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:integer 类型的减完后的数值。

2.5.INCRBYFLOAT

功能:

对键 key 存储的浮点数值执行加减操作。

核心行为:

  1. 数值操作

    • 将键对应的字符串值解析为浮点数

    • 加上指定的增量值

    • 若增量为负数,则执行减法操作

  2. 键不存在时

    • 自动视为该键的值为 0.0

  3. 科学计数法

    • 支持使用科学计数法表示浮点数(如 1.23e4

  4. 错误条件

    • 键存在但非字符串类型 → 报错

    • 键值无法解析为浮点数 → 报错

语法:

INCRBYFLOAT key increment
  1. 命令有效版本:2.6.0 之后
  2. 时间复杂度:O(1)
  3. 返回值:加/减完后的数值。

话不多说,我们直接看例子

很多存储系统和编程语言内部使用 CAS 机制实现计数功能,会有一定的 CPU 开销,但在 Redis 中完全不存在这个问题,因为 Redis 是单线程架构,任何命令到了 Redis 服务端都要顺序执行。

Redis里面整数和小数的存储方式

Redis 在存储数值类型时,对整数和小数采取了不同的底层处理方式。

  • 整数: 直接使用高效的整型数据结构(在 Redis 内部通常表示为 long 类型,对应 C 语言的 long long 或 Java 的 long,即 64 位有符号整数)。这使得整数的算术运算(如 INCRDECRINCRBY)非常高效,因为它们直接在内存中的二进制数值上进行操作。

  • 小数: 则是以序列化后的字符串形式存储的(例如,"3.14")。这意味着每次需要对小数进行任何算术运算时(无论是 Redis 内置命令如 INCRBYFLOAT,还是客户端应用逻辑),都必须:

    1. 将存储的字符串解析为编程语言中的浮点数类型(如 double)。

    2. 在浮点数上执行运算。

    3. 将运算结果重新序列化为字符串。

    4. 最后再将字符串写回 Redis。

这种“字符串-数值-字符串”的转换过程,相比于整数的直接二进制运算,会带来显著的额外开销(CPU 用于解析/序列化,内存用于存储字符串)。选择这种设计主要是因为:

  • 保持精度和可移植性: 字符串表示可以精确地记录用户输入的小数值(尤其是涉及特定精度时),避免了二进制浮点数固有的精度损失问题(如 0.1 无法精确表示)。它也确保了不同客户端或系统之间传输时值的一致性和可读性。

  • 简化实现: 统一用字符串存储简化了 Redis 内部对值类型的处理逻辑。

因此,虽然 Redis 提供了 INCRBYFLOAT 等命令支持小数运算,但其底层实现机制决定了其性能开销远大于整数的等效操作。在设计需要频繁进行数值计算的数据结构时,应充分考虑这种差异。

三 . 其他命令

3.1.APPEND

如果 key 已经存在并且是一个 string,命令会将 value 追加到原有 string 的后边。

如果 key 不存在,则效果等同于 SET 命令。

语法:

APPEND KEY VALUE
  • 命令有效版本:2.0.0 之后
  • 时间复杂度:O(1). 追加的字符串一般长度较短, 可以视为 O(1).
  • 返回值:追加完成之后 string 的长度。返回的是字节数!!!

注意事项

append 返回值, 长度的单位是 字节!!

redis 的字符串, 不会对字符编码做任何处理. (redis 不认识字符, 只认识字节)

当前咱们的 xshell 终端, 默认的字符编码是 utf8.

在终端中输入汉字之后, 也就是按照 utf8 编码的~~

一个汉字在 utf8 字符集中, 通常是 3 个字节的~

3.2.GETRANGE

功能:

返回键 key 对应字符串值的子串,子串范围由 start 和 end 下标确定(包含两端字符)。

索引规则:

  1. 正数索引:从左向右计数(0 表示第一个字符)

  2. 负数索引:从右向左计数

    • -1:倒数第一个字符

    • -2:倒数第二个字符

    • 其他负数依此类推

  3. 范围处理

    • 若 start 超出字符串左边界,视为 0

    • 若 end 超出字符串右边界,视为最后一个字符

    • 若 start > end,返回空字符串

语法:

GETRANGE key start end
  • 命令有效版本:2.4.0 之后
  • 时间复杂度:O(N). N 为 [start, end] 区间的长度. 由于 string 通常比较短, 可以视为是 O(1)
  • 返回值:string 类型的子串、

闭区间包含

负数索引

越界处理

空键处理

完整实例

3.3.SETRANGE

功能:

从指定偏移量 offset 开始,覆盖键 key 存储的字符串值的部分内容。

注意这里的偏移量可是以字节为单位的。

核心行为:

  1. 覆盖规则

    • 从 offset 位置开始,用 value 覆盖原字符串

    • 若 offset 大于原字符串长度,自动用空字节(\x00)填充中间空缺

  2. 键不存在时

    • 视为空字符串 "" 处理

    • 等同于在指定位置创建新字符串

  3. 长度变化

    • 操作后字符串长度 = max(原长度, offset + value长度)

    • 可能扩展字符串长度

语法:

SETRANGE key offset value
  • 命令有效版本:2.2.0 之后
  • 时间复杂度:O(N),N 为 value 的长度。由于一般给的 value 比较短,通常视为 O(1)。
  • 返回值:替换后的 string 的长度。

话不多说,直接看例子:

基础覆盖:从指定偏移量 offset 开始,覆盖键 key 存储的字符串值的部分内容。

超出长度处理:若 offset 大于原字符串长度,自动用空字节(\x00)填充中间空缺

空键操作:键不存在时,视为空字符串 "" 处理,偏移量前面的全部自动用空字节(\x00)填充

注意这里的偏移量可是以字节为单位的。

如果我们当前的value是中文字符串,进行 SETRANGE操作的话可能会出现问题的。因为一个中文字符通常不止一个字节。

我们看看

3.4.STRLEN  

获取 key 对应的 string 的长度。也就是对应string的字节数。

当 key 存放的类似不是 string 时,报错。

语法:

STRLEN key
  • 命令有效版本:2.2.0 之后  
  • 时间复杂度:O(1)  
  • 返回值:string 的长度。或者当 key 不存在时,返回 0。

注意事项:

1.字节长度

无论字符串使用何种编码(如 ASCII、UTF-8、GBK),STRLEN 始终返回字符串占用的 字节数
例如:

  • 字符串 "hello"(ASCII)→ 5 字节 → 返回 5
  • 字符串 "你好"(UTF-8 编码)→ 每个汉字占 3 字节 → 总 6 字节 → 返回 6

2.非字符串类型报错

若 Key 存储的不是字符串类型(如 List/Hash/Set),Redis 会返回错误:(error) WRONGTYPE Operation against a key holding the wrong kind of value

3.Key 不存在:若 Key 不存在,返回 0(视为空字符串)。


话不多说,我们直接看例子

字符串 "你好"(注意我们现在是使用了UTF-8 编码)→ 每个汉字占 3 字节 → 总 6 字节 → 返回 6

在编程和数据库设计中,理解字符串长度和字符编码至关重要,不同环境下的处理方式存在显著差异:

长度单位:

  • C++: std::string 的 length() 或 size() 方法返回的是字节数

  • Java: String 的 length() 方法返回的是 字符数

  • MySQL: VARCHAR(N) 定义中的 N 指定的是字符数。一个字符可以是一个英文字母、一个汉字或其他语言符号。

四. 字符串类型内部编码

Redis 为了优化内存使用和性能,会根据字符串值的类型和长度动态选择三种不同的内部编码表示:

  • int:8个字节的⻓整型。
  • embstr:⼩于等于39个字节的字符串。
  • raw:⼤于39个字节的字符串。

Redis 会根据当前值的类型和⻓度动态决定使⽤哪种内部编码实现。

int (整型):

当字符串值可以表示为 8 字节长整型 (long) 时使用。

优势: 内存占用小,操作效率高(支持数值计算)。

示例:

值 '6379' 被识别为整数,使用 int 编码

embstr (嵌入式字符串):

用于存储长度 小于或等于 44 字节 的字符串值 (注意:Redis 版本演进中,这个阈值从早期的 39 字节提高到了 5.0 及以后版本的 44 字节)

特点: RedisObject 对象头和数据本身存储在一块连续的内存中。

优势: 内存分配和释放只需一次操作,缓存局部性好,访问效率高。

示例:



 短字符串 "hello" 使用 embstr 编码

raw (原始字符串):

用于存储长度 大于 44 字节 的字符串值。

特点: RedisObject 对象头和数据本身存储在两块独立分配的内存中。

原因: 对于长字符串,分配连续的大块内存代价更高且不灵活。

示例:

"raw"  # 长字符串使用 raw 编码

忠告

 不建议大家去记, 长度大于 39 这样的数字~~

给大家讲个情景吧:

对于某个业务场景, 有很多很多的 key , 类型都是 string

但是每个 value 的 string 长度都是 100 左右~~ 更关注与整体的内存空间.

因此的话, 这样的字符串使用 embstr 来存储也不是 不能考虑~~

上述效果具体怎么实现?

  1. 先看 redis 是否提供了对应的配置项, 可以修改 39 这个数字~~
  2. 如果没有提供配置型, 就需要针对 redis 源码进行魔 改~

为啥很多大厂, 往往是自己造轮子, 而不是直接使用业 界成熟的呢?

开源的组件, 往往考虑的是通用性~~ 但是大厂往往会遇到一些极端的业务场景~~ 往往就需要根据当前的极端业务, 针对上述的开源组件 进行定制化~~

五. 典型使用场景

5.1.缓存(Cache)功能

        缓存(Cache)功能是⽐较典型的缓存使⽤场景,其中Redis作为缓冲层,MySQL作为存储层,绝⼤部分请 求的数据都是从Redis中获取。

缓存读取的核心流程:

  1. 优先查询 Redis: 当应用服务器需要数据时,首先尝试从 Redis 缓存中读取。

  2. 缓存命中 (Cache Hit): 如果所需数据存在于 Redis 中,则直接将其返回给应用服务器。此时不会访问后端数据库,显著提升响应速度和降低数据库负载。

  3. 缓存未命中 (Cache Miss):

    • 如果 Redis 中不存在所需数据,则应用服务器转而查询后端数据库 (如 MySQL)。

    • 从数据库获取到结果后,将其返回给应用服务器。

    • 同时,将这个查询结果写入 Redis 缓存,以备后续相同的请求使用。

Redis 与“热点数据”:

  • Redis 缓存最典型的应用场景就是存储高频访问的数据,即“热点数据”。

  • 上述缓存策略(最近查询到的数据写入缓存)本质上是将最近被访问的数据视为潜在的热点数据。

  • 这隐含了一个假设:某个数据一旦被访问,它在近期内很可能被再次访问(时间局部性原理)。这种策略(如 LRU - Least Recently Used 的变体)能有效捕捉并加速访问这类热点数据。

潜在问题:缓存膨胀与解决方案

上述策略存在一个明显的问题:随着时间推移,越来越多的 Key 会因为首次访问而从数据库加载并写入 Redis。如果不加控制,Redis 中存储的数据量会持续增长,最终可能导致:

  • 内存耗尽: Redis 主要依赖内存存储,无限增长的数据必然耗尽内存资源。

  • 性能下降: 内存不足可能导致 Redis 自身操作变慢,甚至触发更严重的问题。

核心解决方案:

  1. 设置过期时间 (TTL - Time To Live):

    • 在将数据写入 Redis 时,必须为其设置一个合理的过期时间。例如:SET key value EX 3600 (设置 key 在 3600 秒后过期)。

    • 这确保了即使数据不再被访问,也会在过期后被自动删除,释放内存空间。

    • 过期时间的选择需要结合业务场景(数据更新频率、重要性、容忍的陈旧度等)。

  2. 配置内存淘汰策略 (Eviction Policies):

    • 当 Redis 内存使用达到配置的最大限制 (maxmemory) 时,它会根据设定的淘汰策略自动删除部分 Key 以释放空间,保证新数据可以写入。

    • 常见的淘汰策略包括:

      • volatile-lru / allkeys-lru: 淘汰最近最少使用的 Key(有过期时间的 / 所有Key)。

      • volatile-lfu / allkeys-lfu: 淘汰最不经常使用的 Key(有过期时间的 / 所有Key)。

      • volatile-ttl: 淘汰即将过期的 Key。

      • volatile-random / allkeys-random: 随机淘汰 Key(有过期时间的 / 所有Key)。

      • noeviction: 不淘汰,新写入操作会报错(通常不推荐用于缓存场景)。

    • 选择合适的淘汰策略对于平衡内存使用和缓存命中率至关重要。


由于Redis具有⽀撑⾼并发的特性,所以缓存通常能起到加速读写和降低后端压⼒的作⽤。

下⾯的伪代码模拟了业务数据访问过程:

1)假设业务是根据⽤⼾uid获取⽤⼾信息

UserInfo getUserInfo(long uid) {...}

2)⾸先从Redis获取⽤⼾信息,我们假设⽤⼾信息保存在"user:info:"对应的键中:

// 根据 uid 得到 Redis 的键
String key = "user:info:" + uid;// 尝试从 Redis 中获取对应的值
String value = Redis 执行命令: get key;// 如果缓存命中 (hit)
if (value != null) {// 假设我们的用户信息按照 JSON 格式存储UserInfo userInfo = JSON 反序列化(value);return userInfo;
}

3)如果没有从 Redis 中得到用户信息,及缓存 miss,则进一步从 MySQL 中获取对应的信息,随后写入缓存并返回:

// 如果缓存未命中(miss)
if (value == null) {// 从数据库中,根据 uid 获取用户信息UserInfo userInfo = MySQL 执行 SQL: select * from user_info where uid = <uid>}// 如果表中没有 uid 对应的用户信息
if (userInfo == null) {响应 404return null;
}// 将用户信息序列化成 JSON 格式
String value = JSON 序列化(userInfo);// 写入缓存,为了防止数据腐烂(rot),设置过期时间为 1 小时(3600 秒)
Redis 执行命令: set key value ex 3600// 返回用户信息
return userInfo;
}

通过增加缓存功能,在理想情况下,每个用户信息,一个小时期间只会有一次 MySQL 查询,极大地提升了查询效率,也降低了 MySQL 的访问数。

与 MySQL 等关系型数据库不同的是,Redis 没有表、字段这种命名空间,而且也没有对键名有强制要求(除了不能使用一些特殊字符)。

但设计合理的键名,有利于防止键冲突和项目的可维护性,比较推荐的方式是使用 "业务名:对象名:唯一标识:属性" 作为键名。

例如 MySQL 的数据库名为 vs,用户表名为 user_info,那么对应的键可以使用 "vs:user_info:6379"、"vs:user_info:6379:name" 来表示,如果当前 Redis 只会被一个业务

使用,可以省略业务名 "vs:"。

如果键名过程,则可以使用团队内部都认同的缩写替代,例如 "user:6379:friends:messages:5217" 可以被 "u:6379:fr:m:5217" 代替。

毕竟键名过长,还是会导致 Redis 的性能明显下降的。

5.2.计数(Counter)功能

许多应用都会使用 Redis 作为计数的基础工具,它可以实现快速计数、查询缓存的功能,同时数据可以异步处理或者落地到其他数据源。

如图所示,例如视频网站的视频播放次数可以使用 Redis 来完成:用户每播放一次视频,相应的视频播放数就会自增 1。

// 在 Redis 中统计某视频的播放次数
long incrVideoCounter(long vid) {key = "video:" + vid;long count = Redis 执行命令:incr keyreturn counter;
}

实际上要开发一个成熟、稳定的真实计数系统,要面临的挑战远不止如此简单:防作弊、按照不同维度计数、避免单点问题、数据持久化到底层数据源等。

Redis 并不擅长数据统计!

为什么呢? Redis 的核心优势在于快速的键值存取和丰富的数据结构操作,但它缺乏内置的复杂聚合计算能力,特别是像排序、分组、多键关联统计这类操作。

典型例子: 比如,你想在上面提到的 Redis 缓存(或存储)中,统计出播放量排名前 100 的视频有哪些。要实现这个需求:

  • 基于 Redis 搞就很麻烦! 你可能需要:

    1. 遍历所有相关的视频 Key(如果数据量大,KEYS 命令是灾难性的,SCAN 也慢)。

    2. 逐个获取它们的播放量(多次 GET/HGET 命令,网络开销大)。

    3. 在客户端(应用服务器)内存中对所有视频的播放量进行排序。

    4. 最后取出前 100 名。
      这个过程不仅性能开销巨大(O(N) 复杂度),容易阻塞 Redis(如果遍历不当),而且实现逻辑复杂,代码量多。

相比之下,MySQL 的优势就显现了:

  • 如果上述播放量数据是存储在 MySQL 数据库中的(比如存在一个 videos 表里,有 video_id 和 play_count 字段)。

  • 那么,一个简单的 SQL 查询就搞定了! 例如:

    SELECT video_id, play_count FROM videos ORDER BY play_count DESC LIMIT 100;
  • MySQL 强大的查询优化引擎会高效地处理排序 (ORDER BY) 和限制结果集 (LIMIT),通常利用索引就能快速完成,复杂度远低于 Redis 的 O(N) 遍历,而且代码极其简洁。

5.3.共享会话(Session)

如图所示,一个分布式 Web 服务将用户的 Session 信息(例如用户登录信息)保存在各自的服务器中,但这样会造成一个问题:出于负载均衡的考虑,分布式服务会将用户的访问请求均衡到不同的服务器上,并且通常无法保证用户每次请求都会被均衡到同一台服务器上,这样当用户刷新一次访问是可能会发现需要重新登录,这个问题是用户无法容忍的。 

为了解决这个问题,可以使⽤Redis将⽤⼾的Session信息进⾏集中管理,如图2-13所⽰,在这种模 式下,只要保证Redis是⾼可⽤和可扩展性的,⽆论⽤⼾被均衡到哪台Web服务器上,都集中从 Redis 中查询、更新Session信息。 

总结一下,就是下面这样子:

分布式 Session 问题
在分布式 Web 服务中,若用户 Session 信息(如登录状态)存储在各自服务器内存中,当负载均衡将用户请求分配到不同服务器时,用户可能因访问不同服务器而丢失 Session 状态(例如刷新页面后需重新登录)。这种体验是用户无法容忍的。

解决方案:Redis 集中管理

  1. 统一存储:所有服务器将 Session 数据(键值对结构)集中存储到 Redis。

  2. 高可用保障:通过 Redis 集群(主从复制+哨兵)或 Redis Cluster 确保服务连续性。

  3. 全局访问:用户请求携带 Session ID(通常存于浏览器 Cookie),无论分配到哪台服务器,均从 Redis 查询/更新状态。
    → 实现效果:用户登录状态、购物车等会话数据在分布式环境下保持全局一致


医疗场景类比

原始案例

"我前段时间生病,声带发炎到完全说不出话~~
挂号看专家,做喉镜、雾化理疗,开了一周药量。医生嘱托:'先搞一周看看,一周后复查~~'
一周后复查,发现首诊医生不在!!! 新医生未接触过我的病例,不了解情况~~
硬着头皮就诊,新医生刷我的就诊卡,电脑上立即显示完整病史~~"

技术映射

  • 病人=客户端:需要连续医疗服务

  • 医生=Web服务器:多实例并行服务

  • 医生凭个人记忆=服务器本地存储Session:导致切换服务器时状态丢失

  • 就诊卡=Session ID:唯一标识患者身份(通过Cookie传递)

  • 医院病例系统=Redis:集中存储病史(会话状态),实现医生(服务器)间数据共享

核心结论
会话(Session)本质是客户端与服务器交互中产生的专属中间状态,分布式系统必须通过集中存储(如Redis)解决状态一致性问题,如同医院依赖共享病例系统保障连续诊疗。

5.4.⼿机验证码

        很多应⽤出于安全考虑,会在每次进⾏登录时,让⽤⼾输⼊⼿机号并且配合给⼿机发送验证码, 然后让⽤⼾再次输⼊收到的验证码并进⾏验证,从⽽确定是否是⽤⼾本⼈。

为了短信接⼝不会频繁访 问,会限制⽤⼾每分钟获取验证码的频率,例如⼀分钟不能超过5次,如图所⽰。

此功能可以用以下伪代码说明基本实现思路:

String 发送验证码(phoneNumber) {key = "shortMsg:limit:" + phoneNumber;// 设置过期时间为 1 分钟 (60 秒)// 使用 NX,只在不存在 key 时才能设置成功bool r = Redis 执行命令: set key 1 ex 60 nxif (r == false) {// 说明之前设置过该手机的验证码了long c = Redis 执行命令: incr keyif (c > 5) {// 说明超过了一分钟 5 次的限制了// 限制发送return null;}}// 说明要么之前没有设置过手机的验证码;要么次数没有超过 5 次String validationCode = 生成随机的 6 位数的验证码();validationKey = "validation:" + phoneNumber;// 验证码 5 分钟 (300 秒) 内有效Redis 执行命令: set validationKey validationCode ex 300;// 返回验证码,随后通过手机短信发送给用户return validationCode ;
}// 验证用户输入的验证码是否正确
bool 验证验证码(phoneNumber, validationCode) {validationKey = "validation:" + phoneNumber;String value = Redis 执行命令: get validationKey;if (value == null) {// 说明没有这个手机的验证码记录,验证失败return false;}if (value == validationCode) {return true;} else {return false;}
}

以上介绍了使用 Redis 的字符串数据类型可以使用的几个场景,但其适用场景远不止于此,开发人员可以结合字符串类型的特点以及提供的命令,充分发挥自己的想象力,在自己的业务中去找到合适的场景去使用 Redis 的字符串类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94485.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94485.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/94485.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nginx 学习

通过网盘分享的文件&#xff1a;Nginx 链接: https://pan.baidu.com/s/1dCc7FoND90H_x7rvRUXJqg 提取码: yyds 通过网盘分享的文件&#xff1a;Tomcat 链接: https://pan.baidu.com/s/1nj_5j_66gS_YHUAX1C25jg 提取码: yyds Nginx安装、启动 安装依赖库 #安装C编译器 yum insta…

Java、Android及计算机基础面试题总结

1. String、StringBuffer、StringBuilder区别特性StringStringBufferStringBuilder可变性不可变可变可变线程安全是是(synchronized)否性能低(频繁操作时)中等高场景字符串常量多线程字符串操作单线程字符串操作2. 接口和抽象类的区别特性接口(Interface)抽象类(Abstract Class…

数据集相关类代码回顾理解 | sns.distplot\%matplotlib inline\sns.scatterplot

【PyTorch】单目标检测项目 目录 os.path.join sns.distplot adjust_brightness os.path.join fullPath2imgos.path.join(path2data,"Training400",prefix,imgName[id_]) 使用os.path.join函数&#xff0c;智能地处理不同操作系统中的路径分隔符问题&#xff0…

JavaScript:链式调用

概念 链式调用&#xff08;Method Chaining&#xff09;是 JavaScript 中一种常见的编程模式&#xff0c;允许通过连续调用对象的方法来简化代码。这种模式的核心在于每个方法返回调用对象本身&#xff08;通常是 this&#xff09;&#xff0c;从而可以继续调用其他方法。 链式…

龙芯(loongson) ls2k1000 openwrt

PC环境&#xff1a;Linux Mint 21.3安装依赖sudo apt install build-essential clang flex bison g gawk gcc-multilib g-multilib gettext git libncurses-dev libssl-dev python3-distutils rsync unzip zlib1g-dev file wget下载源码&#xff1a;git clone https://gitee.co…

算法438. 找到字符串中所有字母异位词

给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。示例 1:输入: s "cbaebabacd", p "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "abc&…

Go语言中的闭包详解

闭包在Go语言中是一个能够访问并操作其外部作用域变量的函数&#xff0c;即使外部函数已经执行完毕。闭包由函数体和其引用的环境&#xff08;外部变量&#xff09;组成&#xff0c;及&#xff1a;闭包 函数 环境。闭包的特性&#xff1a;捕获外部变量&#xff1a;内部函数可…

【DL学习笔记】Dataset类功能以及自定义

文章目录一、Dataset 与 DataLoader 功能介绍抽象类Dataset的作用DataLoader 作用两者关系二、自定义Dataset类Dataset的三个重要方法__len__()方法_getitem__()方法__init__ 方法三、现成的torchvision.datasets模块MNIST举例COCODetection举例torchvision.datasets.MNIST使用…

Python爬虫实战:研究python_reference库,构建技术研究数据系统

1. 引言 1.1 研究背景与意义 在大数据时代,数据已成为重要的生产要素。互联网作为全球最大的信息库,蕴含着海量有价值的数据。如何从纷繁复杂的网络信息中快速、准确地提取所需数据,成为各行各业面临的重要课题。网络爬虫技术作为数据获取的关键手段,能够模拟人类浏览网页…

Web开发系列-第15章 项目部署-Docker

第15章 项目部署-Docker Docker技术能够避免部署对服务器环境的依赖&#xff0c;减少复杂的部署流程。 轻松部署各种常见软件、Java项目 参考文档&#xff1a;‌&#xfeff;‬‌&#xfeff;‍‍‌‍⁠⁠&#xfeff;‍‍&#xfeff;‬‌‍‌‬⁠‌‬第十五章&#xff1a;…

微软无界鼠标(Mouse without Borders)安装及使用:多台电脑共用鼠标键盘

文章目录一、写在前面二、下载安装1、两台电脑都下载安装2、被控端3、控制端主机三、使用一、写在前面 在办公中&#xff0c;我们经常会遇到这种场景&#xff0c;自己带着笔记本电脑外加公司配置的台式机。由于两台电脑&#xff0c;所以就需要搭配两套键盘鼠标。对于有限的办公…

nodejs 编程基础01-NPM包管理

1:npm 包管理介绍 npm 是nodejs 的包管理工具&#xff0c;类似于java 的maven 和 gradle 等&#xff0c;用来解决nodejs 的依赖包问题 使用场景&#xff1a;1. 从NPM 服务骑上下载或拉去别人编写好的第三方包到本地进行使用2. 将自己编写代码或软件包发布到npm 服务器供他人使用…

基于Mediapipe_Unity_Plugin实现手势识别

GitHub - homuler/MediaPipeUnityPlugin: Unity plugin to run MediaPipehttps://github.com/homuler/MediaPipeUnityPlugin 实现了以下&#xff1a; public enum HandGesture { None, Stop, ThumbsUp, Victory, OK, OpenHand } 核心脚本&#xff1a…

Android 项目构建编译概述

主要内容是Android AOSP源码的管理方式&#xff0c;项目源码的构建和编译&#xff0c;用到比如git、repo、gerrit一些命令工具&#xff0c;以及使用Soong编译系统&#xff0c;编写Android.bp文件的格式样式。 1. Android操作系统堆栈概述 Android 是一个针对多种不同设备类型打…

Python爬虫08_Requests聚焦批量爬取图片

一、Requests聚焦批量爬取图片 import re import requests import os import timeurl https://www.douban.com/ userAgent {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0}#获取整个浏览页面 page_text requests.get(urlur…

Spring Cloud系列—简介

目录 1 单体架构 2 集群与分布式 3 微服务架构 4 Spring Cloud 5 Spring Cloud环境和工程搭建 5.1 服务拆分 5.2 示例 5.2.1 数据库配置 5.2.2 父子项目创建 5.2.3 order_service子项目结构配置 5.2.4 product_service子项目结构配置 5.2.5 服务之间的远程调用 5.…

【普中STM32精灵开发攻略】--第 1 章 如何使用本攻略

学习本开发攻略主要参考的文档有《STM32F1xx 中文参考手册》和《Cortex M3权威指南(中文)》&#xff0c;这两本都是 ST 官方手册&#xff0c;尤其是《STM32F1xx 中文参考手册》&#xff0c;里面包含了 STM32F1 内部所有外设介绍&#xff0c;非常详细。大家在学习 STM32F103的时…

【Docker】RK3576-Debian上使用Docker安装Ubuntu22.04+ROS2

1、简述 RK3576自带Debian12系统,如果要使用ROS2,可以在Debian上直接安装ROS2,缺点是有的ROS包需要源码编译;当然最好是使用Ubuntu系统,可以使用Docker安装,或者构建Ubuntu系统,替换Debian系统。 推荐使用Docker来安装Ubuntu22.04,这里会有个疑问,是否可以直接使用Do…

解决docker load加载tar镜像报json no such file or directory的错误

在使用docker加载离线镜像文件时&#xff0c;出现了json no such file or directory的错误&#xff0c;刚开始以为是压缩包拷贝坏了&#xff0c;重新拷贝了以后还是出现了问题。经过网上查找方案&#xff0c;并且自己实践&#xff0c;采用下面的简单方法就可以搞定。 归结为一句…

《协作画布的深层架构:React与TypeScript构建多人实时绘图应用的核心逻辑》

多人在线协作绘图应用的构建不仅是技术栈的简单组合,更是对实时性、一致性与用户体验的多维挑战。基于React与TypeScript开发这类应用,需要在图形绘制的基础功能之外,解决多用户并发操作的同步难题、状态回溯的逻辑冲突以及大规模协作的性能瓶颈。每一层架构的设计,都需兼顾…