> 技术文档 > Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度


前言

Redis 作为一个高性能的键值数据库,其核心在于提供了丰富的数据类型来满足各种业务场景的需求。在所有数据类型中,String(字符串)是最基础也是最核心的一种。无论是简单的键值缓存,还是复杂的计数器、分布式锁,都离不开 String 类型的支持。本文将深入探讨 Redis 中 String 类型的底层原理、常用命令以及多样化的应用场景,帮助你彻底掌握这一关键数据结构,为高效使用 Redis 打下坚实的基础。

redis所有的key都是字符串
value的类型都是存在差异的

redis中的字符串,直接就是按照二进制的方式存储的
(不会做任何的编码转换,存的是啥,取出来的就是啥)

JSON、xml、二进制数据(音频、视频、图片)

音视频的大小体积可能比较大
redis对于string类型限制了最大的大小是512MB

redis单线程模型,希望进行的操作都能比较快速

Mysql默认的字符集是拉丁文,插入中文就会失败的

但是redis不会出现这种情况的,存啥形式取出来的就是啥

一般来说redis遇到乱码问题的概率较小

set 和get

set格式

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

通过一个命令创建键值对并且设置超时时间

set key value ex 10转换为set key value 创建键值对expire key 10 设置过期时间为10

除了上面使用ex设置超时时间为秒
还可以使用px设置超时时间为毫秒

set key value px 10

后面的

NX和XXNX表示的是如果Key不存在的话,才设置如果key存在的话,则不设置(返回nil)XX则相反如果key存在才设置(相当于更新key的value,可能会更改原来的数据类型,原来的key的ttl(生存时间)也会失效的)如果key不存在的话,则不设置(返回nil)这里的两个选项是互斥的,所以二者只能存在一个

redis文档说明
[]相当于一个独立的单元,表示选项(可有可无)
其中的 | 表示或者的意思,多个只能出现一个
一次性将命令完成,这样可以减少网络开销

删库命令,将redis上所有的键值对都删除

FLUSHALL

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

在后面加上NX,如果不存在的话才进行设置,如果存在的话就返回nil
这里我们的key2已经消失了,因为我们上面设置了过期时间了
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
这里在后面加上XX,如果key存在的话就进行value的更新
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

对于GET来说,只是支持字符串类型的value
如果value是其他的类型的话,使用GET获取就会出错了Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
我们这里的key类型是list,所以我们这里使用GET是不能获取value的
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

mset 和mget

一次可以操作多组键值对的

执行N次get命令会进行多次网络传输的
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
执行一次get命令的话只进行一次get传输的Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

MSET key value [key value ...]

一次性创建多个键值对
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
虽然可以设置多个键值对,但是不要太多了,如果这个命令执行时间太长了的话,会造成阻塞其他的命令

使用mget可以一次查询多个key的value的值
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
时间复杂度是O(N)
这个N是key的数量

SETNX 、SETEX、PSETEX

SETNX 不存在才能设置,存在则设置失败

被框出来的这个命令没有生效,因为key1的值是没有改变的
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
setex的使用方法

setex key time value

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

setex和psetex都是设置key的过期时间,前者的单位是秒,后者的单位是毫秒

上面都是针对set的一些常见用法,进行了缩写

这样使用者的门槛就越低了

incr和incrby

incr 针对value+1#此时key对应的value必须是整数,进行这个命令后的返回值是+1后的值incrby 针对value+n#此时key对应的value必须是整数,进行这个命令后的返回值是+n后的值

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
如果value不是整数的话,这里进行incr的话会出现报错的
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
小数也是不行的,也是会出现报错的
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
并且如果值过大的话也是会报错的
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
我们这里原本是没有key3的,然后使用incr key3
incr操作的Key如果是不存在的话,就会将这个key的value当做0
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

针对某个key的value加上n,返回加上后的值
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
incrby加上正数和负数都是可以的

decr 、decrby 和incrbyfloat

decr 针对value-1decrby 针对value-nincrbyfloat 针对value +/- 小数

key对应的value必须是整数,在64位的范围内,如果这个Key对应的value不存在,都是当做0的

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
这个操作方法和incr一样的

incrbyfloat针对浮点数Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

append

如果Key已经存在并且是一个string,命令会将value追加到原有string的后边,如果Key不存在,则效果等同于SET命令创建一个新的键值对

append key value

返回值是追加完字符串的长度

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
在已经存在的Key后面追加一个字符串
如果不存在对应的key的话就会新建一个键值对
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
这里返回的是6个字节
append返回值,长度的单位是字节

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

当前xshell终端,默认的字符编码是utf8
在终端输入汉字之后,也是按照utf8编码的

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

这里我们使用get查看key3的内容
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

  • 返回 \"\\xe4\\xbd\\xa0\\xe5\\xa5\\xbd\" → 这是 你好 的 UTF-8 字节序列的转义表示
    那么我们如何在使用get查看的时候,显示的是中文呢?

那么我们得在启动redis客户端的时候,加上一个 --raw这样的选项

就可以使redis客户端能够自动的把二进制数据尝试翻译了Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

getrange

在C++中的std::string substr
返回key对应的子串,由start和end确定(左闭右闭)

getrange key start end

redis中指定的区间是闭区间

redis的下标是可以支持负数的
-1倒数第一个元素
下标为len-1的元素
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

但是如果是汉字的话,那么此时进行子串切分,我们查询到的可能就不准了
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

setrange

将范围中的子串进行修改

setrange key offset value

offset表示的是偏移量,从第几个字节开始进行替换的
value表示的是替换的字符

结束就看value的长度了
从第4个字符开始进行更新字符串,更新为qq
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
返回值是替换之后新的字符串的长度Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
\\x表示当前是一个16进制的数字
凭空生成一个字节,这个字节里面的内容就是0x00
aaa就被追加到了0x00的后面了

setrange针对不存在的key也是可以操作的,不过会把offset之前的内容填充乘0x00的

strlen

获取字符串的长度的,单位是字节

strlen key

如果key中存放的类型不是key的话就会报错的
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
返回的是key的长度,如果key不存在的话,返回0

命令总结

好的,这是根据图片内容生成的表格:

命令 执行效果 时间复杂度 set key value [key value…] 设置 key 的值是 value O(k), k 是键个数 get key 获取 key 的值 O(1) del key [key …] 删除指定的 key O(k), k 是键个数 mset key value [key value …] 批量设置指定的 key 和 value O(k), k 是键个数 mget key [key …] 批量获取 key 的值 O(k), k 是键个数 incr key 指定的 key 的值 +1 O(1) decr key 指定的 key 的值 -1 O(1) incrby key n 指定的 key 的值 +n O(1) decrby key n 指定的 key 的值 -n O(1) incrbyfloat key n 指定的 key 的值 +n O(1) append key value 指定的 key 的值追加 value O(1) strlen key 获取指定 key 的值的长度 O(1) setrange key offset value 覆盖指定 key 的从 offset 开始的部分值 O(n), n 是字符串长度, 通常视为 O(1) getrange key start end 获取指定 key 的从 start 到 end 的部分值 O(n), n 是字符串长度, 通常视为 O(1)

string内部编码方式

字符串类型的内部编码有3种

  • int:8个字节的长整形
  • embstr:小于等于39个字节的字符串(压缩字符串)
  • raw:大于39个字节的字符串(普通字符串)
    redis会根据当前值的类型和长度动态决定使用哪种内部编码实现

我们可以使用命令查看对应key的编码方式

object encoding key

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

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

使用redis存储小数的时候,显示的编码格式是embstr,本质上还是当做字符串来存储的

string类型应用场景

作为缓存

整体的思路:应用服务器访问数据的时候,先查询redis,如果redis存在该数据,就直接取数据交给应用服务器,不继续访问数据库了
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
如果redis上数据不存在的话,再去去读mysql,把读到的结构,返回给应用服务器,同时,把这个数据也写到redis中

redis这样的缓存,经常来存储热点数据
高频被使用的数据,这个定义方式结合业务场景有很多种方式
这就相当于将最近使用到的数据作为热点数据

上述策略存在一个明显的问题,随着时间的推移,肯定会有越来越多的Key在redis上访问不到,从而从mysql中去读并写入redis了,此时redis中的数据不是越来越多了么

解决方案
1、在把数据写给redis的同时,给这个key设置一个过期时间
2、redis也在内存不足的时候,提供了淘汰策略

计数功能

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
通过redis统计到数据,然后给其他统计数据仓库进行数据的统计

共享对话

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度
Redis String全方位指南:命令、编码、时间复杂度与应用场景_redis 命令 复杂度

手机验证码

在当今的互联网应用中,手机验证码是验证用户身份、确保账户安全的重要环节。利用 Redis 的 String 类型,我们可以轻松、高效地实现这一功能。

其核心思路是:

  1. 当用户请求发送验证码时,后端服务器会生成一个随机的数字(例如6位数)。

  2. 将用户的手机号作为 key,生成的随机验证码作为 value,存入 Redis 中。

  3. 同时,为这个 key 设置一个较短的过期时间,例如 5 分钟。这可以通过 SETEX 命令或者 SET 命令配合 EX 选项一步完成,确保了操作的原子性。

示例命令:

codeBash

# 设置手机号为 188xxxxxxxx 的验证码为 123456,并设置5分钟(300秒)后过期SETEX 188xxxxxxxx 300 123456

当用户输入验证码进行校验时,服务器只需从 Redis 中根据手机号这个 key 来获取 value,并与用户提交的验证码进行比对。

  • 如果能获取到值,并且与用户输入的一致,则验证通过。

  • 如果获取不到值(返回 nil),则说明验证码已经过期或不存在,验证失败。

使用 Redis 存储手机验证码的优势:

  • 高性能:Redis 基于内存操作,读写速度极快,可以轻松应对高并发的验证码请求。

  • 自带过期策略:使用 SETEX 或 EX 选项可以非常方便地管理验证码的生命周期,无需手动删除,有效节约了存储空间并简化了业务逻辑。

  • 简化数据库压力:将验证码这类临时性、高频读写的数据从关系型数据库(如 MySQL)中剥离出来,可以显著降低主数据库的负载。

总结

本文详细介绍了 Redis 中最基础、最核心的 String 数据类型。我们了解到,Redis 的 key 均为字符串,而 String 类型的 value 以二进制安全的方式存储,不会进行任何编码转换,这使其能够存储包括 JSON、图片、音视频在内的任意二进制数据,最大支持 512MB。

核心命令回顾:

  • 基础操作:SET 和 GET 是最基本的读写命令,而 MSET 和 MGET 提供了批量操作的能力,能有效减少网络开销。

  • 原子操作与选项:SET 命令的 NX(不存在才设置)和 XX(存在才设置)选项,以及 SETNX、SETEX 等命令,为实现分布式锁、缓存管理等功能提供了原子性保障。

  • 计数器功能:INCR、DECR、INCRBY 等一系列命令使 String 类型可以作为高性能的原子计数器使用,且能处理整数和浮点数。

  • 字符串处理:APPEND、GETRANGE、SETRANGE 和 STRLEN 等命令提供了对字符串值进行追加、截取、覆盖和长度计算等细粒度操作的能力。

内部编码:
Redis 会根据存储内容和长度,动态选择 int、embstr 或 raw 三种内部编码方式,以优化内存使用和执行效率。

典型应用场景:
最后,我们探讨了 String 类型的四大典型应用场景:

  1. 数据缓存:作为热点数据的缓存层,降低后端数据库压力。

  2. 计数功能:适用于文章阅读量、用户点赞数等统计场景。

  3. 共享会话(Session):在分布式系统中存储和共享用户登录信息。

  4. 手机验证码:利用其高性能和过期策略,安全、高效地处理临时验证信息。

总而言之,深入理解并熟练运用 String 类型是精通 Redis 的基石,其灵活性和高性能使其在各种技术方案中都扮演着不可或缺的角色。