前言
字符串类型 是 Redis
最基础的数据结构。字符串类型 的值实际可以是 字符串(简单 和 复杂 的字符串,例如 JSON
、XML
)、数字(整数、浮点数),甚至是 二进制(图片、音频、视频),但是值最大不能超过 512MB
。
正文
1. 相关命令
1.1. 常见命令
1.1.1. 设置值
set key value [ex seconds] [px milliseconds] [nx|xx]
set
命令有几个选项:
- ex seconds:为 键 设置 秒级过期时间。
- px milliseconds:为 键 设置 毫秒级过期时间。
- nx:键必须 不存在,才可以设置成功,用于 添加。
-
xx:与
nx
相反,键必须 存在,才可以设置成功,用于 更新。
除了 set
选项,Redis
还提供了 setex
和 setnx
两个命令:
setex key seconds value
setnx key value
- setex:设定键的值,并指定此键值对应的 有效时间。
127.0.0.1:6379> setex key1 5 value1
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> get key1
(nil)
-
setnx:键必须 不存在,才可以设置成功。如果键已经存在,返回
0
。
127.0.0.1:6379> set key2 value1
OK
127.0.0.1:6379> setnx key2 value2
(integer) 1
127.0.0.1:6379> get key2
"value1"
1.1.2. 获取值
get key
如果要获取的 键不存在,则返回 nil
(空)。
127.0.0.1:6379> get not_exist_key
(nil)
1.1.3. 批量设置值
mset key value [key value ...]
下面操作通过 mset
命令一次性设置 4
个 键值对:
127.0.0.1:6379> mset a 1 b 2 c 3 d 4
OK
1.1.4. 批量获取值
mget key [key ...]
通过下面操作 批量获取 键 a
、b
、c
、d
的值:
127.0.0.1:6379> mget a b c d
1) "1"
2) "2"
3) "3"
4) "4"
批量操作 命令,可以有效提高 开发效率,假如没有 mget
这样的命令,要执行 n
次 get
命令的过程和 耗时 如下:
imagen次get时间 = n次网络时间 + n次命令时间
使用 mget
命令后,执行 n
次 get
命令的过程和 耗时 如下:
imagen次get时间 = 1次网络时间 + n次命令时间
Redis
可以支撑 每秒数万 的 读写操作,但这指的是 Redis
服务端 的处理能力,对于 客户端 来说,一次命令除了 命令时间 还是有 网络时间。
假设 网络时间 为 1
毫秒,命令时间为 0.1
毫秒(按照每秒处理 1
万条命令算),那么执行 1000
次 get
命令和 1
次 mget
命令的区别如表所示:
操作 | 时间 |
---|---|
1000次get操作 | 1000 * 1 + 1000 * 0.1 = 1100ms = 1.1s |
1次mget操作 | 1 * 1 + 1000 * 0.1 = 101ms = 0.101s |
1.1.5. 计数
incr key
incr
命令用于对值做 自增操作,返回结果分为三种情况:
- 值不是 整数,返回 错误。
- 值是 整数,返回 自增 后的结果。
- 键不存在,按照值为
0
自增,返回结果为1
。
127.0.0.1:6379> exists key
(integer) 0
127.0.0.1:6379> incr key
(integer) 1
除了 incr
命令,Redis
还提供了 decr
(自减)、incrby
(自增指定数字)、decrby
(自减指定数字)、incrbyfloat
(自增浮点数)等命令操作:
decr key
incrby key increment
decrby key decrement
incrbyfloat key increment
很多 存储系统 和 编程语言 内部使用 CAS
机制实现 计数功能,会有一定的 CPU
开销。但在 Redis
中完全不存在这个问题,因为 Redis
是 单线程架构,任何命令到了 Redis
服务端 都要 顺序执行。
1.2. 不常用命令
1.2.1. 追加值
append key value
append
可以向 字符串尾部 追加值。
127.0.0.1:6379> get key
"redis"
127.0.0.1:6379> append key world
(integer) 10
127.0.0.1:6379> get key
"redisworld"
1.2.2. 字符串长度
strlen key
比如说,当前值为 redisworld
,所以返回值为 10
:
127.0.0.1:6379> get key
"redisworld"
127.0.0.1:6379> strlen key
(integer) 10
1.2.3. 设置并返回原值
getset key value
getset
和 set
一样会 设置值,但是不同的是,它同时会返回 键原来的值,例如:
127.0.0.1:6379> getset hello world
(nil)
127.0.0.1:6379> getset hello redis
"world"
1.2.4. 设置指定位置的字符
setrange key offeset value
下面操作将值由 pest
变为了 best
:
127.0.0.1:6379> set redis pest
OK
127.0.0.1:6379> setrange redis 0 b
(integer) 4
127.0.0.1:6379> get redis
"best"
1.2.5. 获取部分字符串
getrange key start end
start
和 end
分别是 开始 和 结束 的 偏移量,偏移量 从 0
开始计算,例如获取值 best
的 前两个字符 的命令如下:
127.0.0.1:6379> getrange redis 0 1
"be"
最后给出 字符串 类型命令的 时间复杂度 说明:
image2. 内部编码
字符串 类型的 内部编码 有 3
种:
-
int:
8
个字节的 长整型。 -
embstr:小于等于
39
个字节的字符串。 -
raw:大于
39
个字节的字符串。
Redis
会根据当前值的 类型 和 长度 决定使用哪种 内部编码实现。
- 整数类型
127.0.0.1:6379> set key 8653
OK
127.0.0.1:6379> object encoding key
"int"
- 短字符串
#小于等于39个字节的字符串:embstr
127.0.0.1:6379> set key "hello,world"
OK
127.0.0.1:6379> object encoding key
"embstr"
- 长字符串
#大于39个字节的字符串:raw
127.0.0.1:6379> set key "one string greater than 39 byte........."
OK
127.0.0.1:6379> object encoding key
"raw"
127.0.0.1:6379> strlen key
(integer) 40
3. 典型使用场景
3.1. 缓存功能
下面是一种比较典型的 缓存 使用场景,其中 Redis
作为 缓存层,MySQL
作为 存储层,绝大部分请求的数据都是从 Redis
中获取。由于 Redis
具有支撑 高并发 的特性,所以缓存通常能起到 加速读写 和 降低后端压力 的作用。
整个功能的伪代码如下:
public UserInfo getUserInfo(long id) {
String userRedisKey = "user:info:" + id;
String value = redis.get(userRedisKey);
UserInfo userInfo;
if (value != null) {
userInfo = deserialize(value);
} else {
userInfo = mysql.get(id); if (userInfo != null) {
redis.setex(userRedisKey, 3600, serialize(userInfo));
}
return userInfo;
}
}
3.2. 计数
许多应用都会使用 Redis
作为 计数 的基础工具,它可以实现 快速计数、查询缓存 的功能,同时数据可以 异步落地 到其他 数据源。一般来说,视频播放数系统,就是使用 Redis
作为 视频播放数计数 的基础组件,用户每播放一次视频,相应的视频播放数就会自增 1
。
public long incrVideoCounter (long id) {
String key = "video:playCount:" + id;
return redis.incr(key);
}
实际上,一个真实的 计数系统 要考虑的问题会很多:防作弊、按照 不同维度 计数,数据持久化 到 底层数据源等。
3.3. 共享Session
一个 分布式 Web
服务将用户的 Session
信息(例如 用户登录信息)保存在 各自 的服务器中。这样会造成一个问题,出于 负载均衡 的考虑,分布式服务 会将用户的访问 均衡 到不同服务器上,用户 刷新一次访问 可能会发现需要 重新登录,这个问题是用户无法容忍的。
为了解决这个问题,可以使用 Redis
将用户的 Session
进行 集中管理。在这种模式下,只要保证 Redis
是 高可用 和 扩展性的,每次用户 更新 或者 查询 登录信息都直接从 Redis
中集中获取。
3.4. 限速
很多应用出于安全的考虑,会在每次进行登录时,让用户输入 手机验证码,从而确定是否是用户本人。但是为了 短信接口 不被 频繁访问,会 限制 用户每分钟获取 验证码 的频率。例如一分钟不能超过 5
次,如图所示:
此功能可以使用 Redis
来实现,伪代码如下:
String phoneNum = "138xxxxxxxx";
String key = "shortMsg:limit:" + phoneNum;
// SET key value EX 60 NX
boolean isExists = redis.set(key, 1, "EX 60", "NX");
if (isExists != null || redis.incr(key) <= 5) {
// 通过
} else {
// 限速
}
上述就是利用 Redis
实现了 限速功能,例如 一些网站 限制一个 IP
地址不能在 一秒钟之内 访问超过 n
次也可以采用 类似 的思路。
小结
本文简单的介绍了 Redis
的 字符串数据结构 的 基本命令,内部编码 和 相关应用场景。
参考
《Redis 开发与运维》
欢迎关注技术公众号: 零壹技术栈
零壹技术栈本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。
网友评论