【Redis】Redis String 字符串

目录

1. 常见命令

SET

GET

MGET

MSET

SETNX

2. 计数命令

INCR

INCRBY

DECR

DECYBY

INCRBYFLOAT

3. 其他命令

APPEND

GETRANGE

SETRANGE

STRLEN

4. 命令小结

 5. 内部编码

6. 典型使用场景

缓存(Cache) 功能

计数(Counter)功能

手机验证码


字符串类型是 Redis 最基础的数据类型,关于字符串需要特别注意:

1) 首先 Redis 中所有的键的类型都是字符串类型,而且其他几种数据结构也都是在字符串类似基础上构建的,例如列表和集合的元素类型是字符串类型,所以字符串类型能为其他4种数据结构的学习奠定基础。

2) 其次,如下图所示,字符串类型的值实际可以是字符串,包含一般格式的字符串或者类似JSON、XML格式的字符串; 数字,可以是整型或者浮点型; 甚至是二进制流数据,例如图片、音频、视频等。不过一个字符串的最大值不能超过 512 MB。

1. 常见命令

SET

将 string 类型的 value 设置到 key 中。如果 key之前存在,则覆盖,无论原来的数据类型是什么。之前关于此 key 的 TTL 也全部失效。

语法:

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

时间复杂度:0(1) 

选项:

SET 命令支持多种选项来影响它的行为:

注意: 由于带选项的 SET命令可以被 SETNX、SETEX、PSETEX 等命令代替,所以之后的版本
中,Redis 可能进行合并。

返回值:

  • 如果设置成功,返回 OK。
  • 如果由于 SET指定了 NX 或者 XX但条件不满足,SET不会执行,并返回(nil)。

示例:

127.0.0.1:6379> exists mykey
(integer) 0
127.0.0.1:6379> set mykey "hello"
OK
127.0.0.1:6379> get mykey
"hello"
127.0.0.1:6379> set mykey "world" nx     # key不存在才设置
(nil)
127.0.0.1:6379> del mykey
(integer) 1
127.0.0.1:6379> exists mykey
(integer) 0
127.0.0.1:6379> set mykey "world" xx    # key存在才设置
(nil)
127.0.0.1:6379> get mykey
(nil)
127.0.0.1:6379> set mykey "world" nx    # key不存在才设置
OK
127.0.0.1:6379> get mykey
"world"
127.0.0.1:6379> set mykey "will expire in 10s" ex 10   # 设置过期时间
OK
127.0.0.1:6379> get mykey
"will expire in 10s"
127.0.0.1:6379> get mykey      # 10s之后
(nil)

GET

获取 key 对应的 value。如果 key不存在,返回 nil。如果 value 的数据类型不是 string,会报错。

语法:

时间复杂度:O(1)

返回值: key对应的 value,或者 nil 当 key 不存在。

示例:

127.0.0.1:6379> flushall
OK
127.0.0.1:6379> get mykey
(nil)
127.0.0.1:6379> set mykey "hello"
OK
127.0.0.1:6379> get mykey
"hello"
127.0.0.1:6379> del mykey
(integer) 1
127.0.0.1:6379> exists mykey
(integer) 0
127.0.0.1:6379> hset mykey name Bob
(integer) 1
127.0.0.1:6379> get mykey
(error) WRONGTYPE Operation against a key holding the wrong kind of value

MGET

一次性获取多个 key 的值。如果对应的 key 不存在或者对应的数据类型不是 string,返回 nil。

语法:

MGET key [key ...]

时间复杂度: O(N) N 是 key数量

返回值: 对应 value 的列表

示例:

127.0.0.1:6379> set key1 "hello"
OK
127.0.0.1:6379> set key2 "world"
OK
127.0.0.1:6379> mget key1 key2 noexisting
1) "hello"
2) "world"
3) (nil)

MSET

一次性设置多个 key 的值。

语法:

MSET key value [key value ...]

时间复杂度: O(N) N 是 key数量

返回值: 永远是 OK

示例:

127.0.0.1:6379> mset key1 "value1" key2 "value2"
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> get key2
"value2"

多次 get vs 单次 mget

使用 mget/mset 由于可以有效地减少了网络时间,所以性能相较更高。假设网络耗时 1毫秒,命令执行时间耗时 0.1毫秒,则执行时间如表所示。

1000次 get 和1次 mget  对比

学会使用批量操作,可以有效提高业务处理效率,但是要注意,每次批量操作所发送的键的数量也不是无节制的,否则可能造成单一命令执行时间过长,导致 Redis 阻塞。

SETNX

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

语法:

SETNX key value

时间复杂度: 0(1)

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

示例:

127.0.0.1:6379> setnx key "hello"
(integer) 1
127.0.0.1:6379> setnx key "world"
(integer) 0
127.0.0.1:6379> get key
"hello"

SET、SET NX和 SETXX 的执行流程如图所示。

图 SET、SET NX、SETXX执行流程

2. 计数命令

INCR

将 key 对应的 string表示的数字加一。如果 key不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是一个整型或者范围超过了 64 位有符号整型,则报错。

语法:

INCR key

时间复杂度:O(1)

返回值: integer类型的加完后的数值。

示例:

127.0.0.1:6379> setnx key "hello"
(integer) 1
127.0.0.1:6379> setnx key "world"
(integer) 0
127.0.0.1:6379> get key
"hello"
127.0.0.1:6379> exists mykey
(integer) 0
127.0.0.1:6379> incr mykey
(integer) 1
127.0.0.1:6379> set mykey "10"
OK
127.0.0.1:6379> incr mykey
(integer) 11
127.0.0.1:6379> set mykey "999999999999999999999999999999"
OK
127.0.0.1:6379> incr mykey
(error) ERR value is not an integer or out of range
127.0.0.1:6379> set mykey "not a number"
OK
127.0.0.1:6379> incr mykey
(error) ERR value is not an integer or out of range

INCRBY

将 key 对应的 string表示的数字加上对应的值。如果 key不存在,则视为 key 对应的 value是0。如
果 key 对应的 string不是一个整型或者范围超过了 64 位有符号整型,则报错。

语法:

INCRBY key decrement

时间复杂度:O(1)

返回值: integer类型的加完后的数值。

示例:

127.0.0.1:6379> exists mykey
(integer) 0
127.0.0.1:6379> incrby mykey 3
(integer) 3
127.0.0.1:6379> set mykey "10"
OK
127.0.0.1:6379> incrby mykey 4
(integer) 14
127.0.0.1:6379> incrby mykey "not a number"
(error) ERR value is not an integer or out of range
127.0.0.1:6379> set mykey "999999999999999999999999999"
OK
127.0.0.1:6379> incrby mykey 3
(error) ERR value is not an integer or out of range
127.0.0.1:6379> set mykey "not a number"
OK
127.0.0.1:6379> incrby mykey 3
(error) ERR value is not an integer or out of range

DECR

将 key 对应的 string表示的数字减一。如果 key不存在,则视为 key对应的 value 是 0。如果 key对
应的 string 不是一个整型或者范围超过了 64 位有符号整型,则报错。

语法:

DECR key

时间复杂度: O(1)

返回值: integer类型的减完后的数值。

示例:

127.0.0.1:6379> exists mykey
(integer) 0
127.0.0.1:6379> decr mykey
(integer) -1
127.0.0.1:6379> set mykey "10"
OK
127.0.0.1:6379> decr mykey
(integer) 9
127.0.0.1:6379> set mykey "999999999999999999999999"
OK
127.0.0.1:6379> decr mykey
(error) ERR value is not an integer or out of range
127.0.0.1:6379> set mykey "not a number"
OK
127.0.0.1:6379> decr mykey
(error) ERR value is not an integer or out of range

DECYBY

将 key 对应的 string表示的数字减去对应的值。如果 key不存在,则视为 key对应的 value是 0。如
果 key 对应的 string 不是一个整型或者范围超过了 64 位有符号整型,则报错。

语法:

DECRBY key decrement

时间复杂度:0(1)

返回值:integer类型的减完后的数值。

示例:

127.0.0.1:6379> exists mykey
(integer) 0
127.0.0.1:6379> decrby mykey 3
(integer) -3
127.0.0.1:6379> set mykey "10"
OK
127.0.0.1:6379> decrby mykey 3
(integer) 7
127.0.0.1:6379> decrby mykey "not a number"
(error) ERR value is not an integer or out of range
127.0.0.1:6379> set mykey "9999999999999999999999999"
OK
127.0.0.1:6379> decrby mykey 3
(error) ERR value is not an integer or out of range
127.0.0.1:6379> set mykey "not a number"
OK
127.0.0.1:6379> decrby mykey 3
(error) ERR value is not an integer or out of range

INCRBYFLOAT

将 key 对应的 string表示的浮点数加上对应的值。如果对应的值是负数,则视为减去对应的值。如果key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的不是 string,或者不是一个浮点数,则报错。允许采用科学计数法表示浮点数。

语法:

INCRBYFLOAT key increment

时间复杂度: O(1)

返回值: 加/减完后的数值。

示例:

127.0.0.1:6379> set mykey "10.50"
OK
127.0.0.1:6379> incrbyfloat mykey 0.1
"10.6"
127.0.0.1:6379> incrbyfloat mykey -5
"5.6"
127.0.0.1:6379> set mykey 1.0e3
OK
127.0.0.1:6379> incrbyfloat mykey 2.0e2
"1200"

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

3. 其他命令

APPEND

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

语法:

APPEND KEY VALUE

时间复杂度:0(1). 追加的字符串一般长度较短, 可以视为 0(1)

返回值: 追加完成之后 string 的长度。

示例:

127.0.0.1:6379> exists mykey
(integer) 0
127.0.0.1:6379> append mykey "hello"
(integer) 5
127.0.0.1:6379> get mykey
"hello"
127.0.0.1:6379> append mykey " world"
(integer) 11
127.0.0.1:6379> get mykey
"hello world"

GETRANGE

返回 key 对应的 string的子串,由 start和end确定(左闭右闭)。可以使用负数表示倒数。-1代表
倒数第一个字符,-2代表倒数第二个,其他的与此类似。超过范围的偏移量会根据 string 的长度调整成正确的值。

语法:

GETRANGE key start end

命令有效版本:2.4.0 之后

时间复杂度:O(N). N为[start,end]区间的长度. 由于 string通常比较短, 可以视为是 O(1)

返回值: string 类型的子串

示例:

127.0.0.1:6379> set mykey "This is a string"
OK
127.0.0.1:6379> getrange mykey 0 3
"This"
127.0.0.1:6379> getrange mykey -3 -1
"ing"
127.0.0.1:6379> getrange mykey 0 -1
"This is a string"
127.0.0.1:6379> getrange mykey 10 100
"string"

SETRANGE

覆盖字符串的一部分,从指定的偏移开始。

语法:

SETRANGE key offset value

时间复杂度: O(N), N为 value 的长度. 由于一般给的 value 比较短, 通常视为 O(1).

返回值: 替换后的 string 的长度。

示例:

127.0.0.1:6379> set key1 "hello world"
OK
127.0.0.1:6379> setrange key1 6 "redis"
(integer) 11
127.0.0.1:6379> get key1
"hello redis"

STRLEN

获取 key 对应的 string 的长度。当 key 存放的类似不是 string 时,报错。

语法:

时间复杂度: O(1)

返回值: string的长度。或者当 key 不存在时,返回 0。

示例:

127.0.0.1:6379> set mykey "hello world"
OK
127.0.0.1:6379> strlen mykey
(integer) 11
127.0.0.1:6379> strlen noexisting
(integer) 0

4. 命令小结

下表是字符串类型命令的效果、时间复杂度,开发人员可以参考此表,结合自身业务需求和数
据大小选择合适的命令。

字符串类型命令小结

 5. 内部编码

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

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

整型类型示例如下:

127.0.0.1:6379> set key 666
OK
127.0.0.1:6379> object encoding key
"int"

短字符串示例如下:

# 小于等于 39 个字节的字符串
127.0.0.1:6379> set key "hello"
OK
127.0.0.1:6379> object encoding key
"embstr"

长字符串示例如下:

# 大于 39 个字节的字符串

127.0.0.1:6379> set key "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
OK
127.0.0.1:6379> object encoding key
"raw"


6. 典型使用场景

缓存(Cache) 功能

下图是比较典型的缓存使用场景,其中 Redis 作为缓冲层,MySQL作为存储层,绝大部分请
求的数据都是从 Redis 中获取。由于 Redis 具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。

Redis+MySQL组成的缓存存储架构

1) 假设业务是根据用户 uid 获取用户信息

UserInfo getUserInfo(long uid) {
 ...
}

2) 首先从 Redis 获取用户信息,我们假设用户信息保存在"user:info:<uid>"对应的键中:

// 根据 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) {
     响应 404
     return null;
 }
 
 // 将⽤⼾信息序列化成 JSON 格式
 String value = JSON 序列化(userInfo);
 
 // 写⼊缓存,为了防⽌数据腐烂(rot),设置过期时间为 1 ⼩时(3600 秒)
 Redis 执⾏命令:set key value ex 3600
 
 // 返回⽤⼾信息
 return userInfo;
}

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

计数(Counter)功能

许多应用都会使用 Redis 作为计数的基础工具,它可以实现快速计数、查询缓存的功能,同时数
据可以异步处理或者落地到其他数据源。如下图所示,例如视频网站的视频播放次数可以使用
Redis 来完成:用户每播放一次视频,相应的视频播放数就会自增 1。

记录视频播放次数

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

共享会话(Session)

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

Session 分散存储

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

Redis 集中管理 Session

手机验证码

很多应用出于安全考虑,会在每次进行登录时,让用户输入手机号并且配合给手机发送验证码,
然后让用户再次输入收到的验证码并进行验证,从而确定是否是用户本人。为了短信接口不会频繁访问,会限制用户每分钟获取验证码的频率,例如一分钟不能超过5次,如下图所示。

短信验证码

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

String 发送验证码(phoneNumber) {
     key = "shortMsg:limit:" + phoneNumber;
     // 设置过期时间为 1 分钟(60 秒)
     // 使⽤ NX,只在不存在 key 时才能设置成功
     bool r = Redis 执⾏命令:set key 1 ex 60 nx
     if (r == false) {
     // 说明之前设置过该⼿机的验证码了
     long c = Redis 执⾏命令:incr key
     if (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 的字符串类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏微凉.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值