深刻剖析Redis系列(五) - Redis数据结构之字符串

前言

字符串类型Redis 最基础的数据结构。字符串类型 的值实际能够是 字符串简单复杂 的字符串,例如 JSONXML)、数字(整数、浮点数),甚至是 二进制(图片、音频、视频),可是值最大不能超过 512MBjava

其余文章

正文

1. 相关命令

1.1. 常见命令

1.1.1. 设置值

set key value [ex seconds] [px milliseconds] [nx|xx]服务器

set 命令有几个选项:

  1. ex seconds:为 设置 秒级过时时间
  2. px milliseconds:为 设置 毫秒级过时时间
  3. nx:键必须 不存在,才能够设置成功,用于 添加
  4. xx:与 nx 相反,键必须 存在,才能够设置成功,用于 更新

除了 set 选项,Redis 还提供了 setexsetnx 两个命令:

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 ...]

经过下面操做 批量获取abcd 的值:

127.0.0.1:6379> mget a b c d
1) "1"
2) "2"
3) "3"
4) "4"
复制代码

批量操做 命令,能够有效提升 开发效率,假如没有 mget 这样的命令,要执行 nget 命令的过程和 耗时 以下:

n次get时间 = n次网络时间 + n次命令时间

使用 mget 命令后,执行 nget 命令的过程和 耗时 以下:

n次get时间 = 1次网络时间 + n次命令时间

Redis 能够支撑 每秒数万读写操做,但这指的是 Redis 服务端 的处理能力,对于 客户端 来讲,一次命令除了 命令时间 仍是有 网络时间

假设 网络时间1 毫秒,命令时间为 0.1 毫秒(按照每秒处理 1 万条命令算),那么执行 1000get 命令和 1mget 命令的区别如表所示:

操做 时间
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

getsetset 同样会 设置值,可是不一样的是,它同时会返回 键原来的值,例如:

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

startend 分别是 开始结束偏移量偏移量0 开始计算,例如获取值 best前两个字符 的命令以下:

127.0.0.1:6379> getrange redis 0 1
"be"
复制代码

最后给出 字符串 类型命令的 时间复杂度 说明:

2. 内部编码

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

  • int8 个字节的 长整型

  • 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 开发与运维》


欢迎关注技术公众号: 零壹技术栈

零壹技术栈

本账号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。

相关文章
相关标签/搜索