
目录一、String字符串1.2 常见命令1.2.1 SET1.2.2 GET1.2.3 MGET1.2.4 MSET1.2.5 SETNX 、SETEX1.3 计数命令1.3.1 INCR1.3.2 INCRBY1.3.3 DECR1.3.4 DECRBY1.3.5 INCRYBYFLOAT1.3.6 计数命令小结1.4 其它命令1.4.1 APPEND1.4.2 GETRANGE1.4.3 SETRANGE编辑1.4.4 STRLEN1.5 命令小结二、内部编码三、string的应用场景3.1 缓存功能编辑3.2 计数功能3.3 共享会话3.4 手机验证码一、String字符串字符串类型是 Redis 最基础的数据类型关于字符串需要特别注意1⾸先 Redis 中所有的键的类型都是字符串类型⽽且其他⼏种数据结构也都是在字符串类似基础上构建的例如列表和集合的元素类型是字符串类型所以字符串类型能为其他 4 种数据结构的学习奠定基础。2其次字符串类型的值实际可以是字符串包含⼀般格式的字符串或者类似 JSON、XML 格式的字符串数字可以是整型或者浮点型甚⾄是⼆进制流数据例如图⽚、⾳频、视频等。不过⼀个字符串的最⼤值不能超过 512MB。由于 Redis 内部存储字符串完全是按照⼆进制流的形式保存的所以 Redis 是不处理字符集编码问题的客⼾端传⼊的命令中使⽤的是什么字符集编码就存储什么字符集编码。1.2 常见命令1.2.1 SET将 string 类型的 value 设置到 key 中。如果 key 之前存在则覆盖⽆论原来的数据类型是什么。之前关于此 key 的 TTL 也全部失效。SET key value [expiration EX seconds|PX milliseconds] [NX|XX]时间复杂度O(1)选项SET 命令⽀持多种选项来影响它的⾏为EXseconds⸺使⽤秒作为单位设置 key 的过期时间。PXmilliseconds⸺使⽤毫秒作为单位设置 key 的过期时间。NX⸺只在 key 不存在时才进⾏设置即如果 key 之前已经存在设置不执⾏。XX⸺只在 key 存在时才进⾏设置即如果 key 之前不存在设置不执⾏。注意由于带选项的 SET 命令可以被SETNX、SETEX、PSETEX等命令代替所以之后的版本中Redis 可能进⾏合并。返回值如果设置成功返回 OK。如果由于 SET 指定了 NX 或者 XX 但条件不满⾜SET 不会执⾏并返回 (nil)。[]相当于一个独立的单元表示可选选项可有可无其中 | 表示“或者”的意思多个只能出现一个。[]和[]之间可用同时存在的。示例redis EXISTS mykey (integer) 0 redis SET mykey Hello OK redis GET mykey Hello redis SET mykey World NX (nil) redis DEL mykey (integer) 1 redis EXISTS mykey (integer) 0 redis SET mykey World XX (nil) redis GET mykey (nil) redis SET mykey World NX OK redis GET mykey World redis SET mykey Will expire in 10s EX 10 OK redis GET mykey Will expire in 10s redis GET mykey # 10秒之后 (nil)FLUSHALL是清空所有key value。1.2.2 GET获取 key 对应的 value。如果 key 不存在返回 nil。如果 value 的数据类型不是 string会报错。语法GET key时间复杂度O(1)返回值key 对应的 value或者 nil 当 key 不存在。redis GET nonexisting (nil) redis SET mykey Hello OK redis GET mykey Hello redis DEL mykey (integer) 1 redis EXISTS mykey (integer) 0 redis HSET mykey name Bob (integer) 1 redis GET mykey (error) WRONGTYPE Operation against a key holding the wrong kind of value1.2.3 MGET⼀次性获取多个 key 的值。如果对应的 key 不存在或者对应的数据类型不是 string返回 nil。 语法MGET key [key ...]时间复杂度O(N) N 是 key 数量返回值对应 value 的列表1.2.4 MSET⼀次性设置多个 key 的值。语法MSET key value [key value ...]时间复杂度O(N) N 是 key 数量返回值永远是 OKredis MSET key1 Hello key2 World OK redis GET key1 Hello redis GET key2 World1.2.5 SETNX 、SETEX设置 key-value 但只允许在 key 之前不存在的情况下。语法SETNX key value时间复杂度O(1)返回值1 表⽰设置成功。0 表⽰没有设置。redis SETNX mykey Hello (integer) 1 redis SETNX mykey World (integer) 0 redis GET mykey Hello其实就是把set的参数组合起来。1.3 计数命令1.3.1 INCR将 key 对应的 string 表⽰的数字加⼀。如果 key 不存在则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型则报错。语法INCR key时间复杂度O(1)返回值integer 类型的加完后的数值。1.3.2 INCRBY将 key 对应的 string 表⽰的数字加上对应的值。如果 key 不存在则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型则报错。语法INCRBY key decrement时间复杂度O(1)返回值integer 类型的加完后的数值。redis EXISTS mykey (integer) 0 redis INCRBY mykey 3 (integer) 3 redis SET mykey 10 OK redis INCRBY mykey 3 (integer) 13 redis INCRBY mykey not a number (error) ERR value is not an integer or out of range redis SET mykey 234293482390480948029348230948 OK redis INCRBY mykey 3 (error) value is not an integer or out of range redis SET mykey not a number OK redis INCRBY mykey 3 (error) value is not an integer or out of range1.3.3 DECR将 key 对应的 string 表⽰的数字减⼀。如果 key 不存在则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型则报错。语法DECR key时间复杂度O(1)返回值integer 类型的减完后的数值。示例redis EXISTS mykey (integer) 0 redis DECR mykey (integer) -1 redis SET mykey 10 OK redis DECR mykey (integer) 9 redis SET mykey 234293482390480948029348230948 OK redis DECR mykey (error) value is not an integer or out of range redis SET mykey not a number OK redis DECR mykey (error) value is not an integer or out of range1.3.4 DECRBY将 key 对应的 string 表⽰的数字减去对应的值。如果 key 不存在则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型则报错。语法DECRBY key decrement时间复杂度O(1)返回值integer 类型的减完后的数值。redis EXISTS mykey (integer) 0 redis DECRBY mykey 3 (integer) -3 redis SET mykey 10 OK redis DECRBY mykey 3 (integer) 7 redis DECRBY mykey not a number (error) ERR value is not an integer or out of range redis SET mykey 234293482390480948029348230948 OK redis DECRBY mykey 3 (error) value is not an integer or out of range redis SET mykey not a number OK redis DECRBY mykey 3 (error) value is not an integer or out of range1.3.5 INCRYBYFLOAT将 key 对应的 string 表⽰的浮点数加上对应的值。如果对应的值是负数则视为减去对应的值。如果key 不存在则视为 key 对应的 value 是 0。如果 key 对应的不是 string或者不是⼀个浮点数则报错。允许采⽤科学计数法表⽰浮点数。语法INCRBYFLOAT key increment时间复杂度O(1)返回值加/减完后的数值。redis SET mykey 10.50 OK redis INCRBYFLOAT mykey 0.1 10.6 redis INCRBYFLOAT mykey -5 5.6 redis SET mykey 5.0e3 OK redis INCRBYFLOAT mykey 2.0e2 5200很多存储系统和编程语⾔内部使⽤ CAS 机制实现计数功能会有⼀定的 CPU 开销但在 Redis 中完全不存在这个问题因为 Redis 是单线程架构任何命令到了 Redis 服务端都要顺序执⾏。1.3.6 计数命令小结1.4 其它命令1.4.1 APPEND如果 key 已经存在并且是⼀个 string命令会将 value 追加到原有 string 的后边。如果 key 不存在则效果等同于 SET 命令。语法APPEND KEY VALUE时间复杂度O(1). 追加的字符串⼀般⻓度较短, 可以视为 O(1).返回值追加完成之后 string 的⻓度。redis EXISTS mykey (integer) 0 redis APPEND mykey Hello (integer) 5 redis GET mykey Hello redis APPEND mykey World (integer) 11 redis GET mykey Hello World1.4.2 GETRANGE返回 key 对应的 string 的⼦串由 start 和 end 确定左闭右闭。可以使⽤负数表⽰倒数。-1 代表倒数第⼀个字符-2 代表倒数第⼆个其他的与此类似。超过范围的偏移量会根据 string 的⻓度调整成正确的值。语法GETRANGE key start end时间复杂度O(N). N 为 [start, end] 区间的⻓度. 由于 string 通常⽐较短, 可以视为是 O(1)返回值string 类型的⼦串redis SET mykey This is a string OK redis GETRANGE mykey 0 3 This redis GETRANGE mykey -3 -1 ing redis GETRANGE mykey 0 -1 This is a string redis GETRANGE mykey 10 100 string1.4.3 SETRANGE覆盖字符串的⼀部分从指定的偏移开始。语法SETRANGE key offset value时间复杂度O(N), N 为 value 的⻓度. 由于⼀般给的 value ⽐较短, 通常视为 O(1).返回值替换后的 string 的⻓度。redis SET key1 Hello World OK redis SETRANGE key1 6 Redis (integer) 11 redis GET key1 Hello Redis1.4.4 STRLEN获取 key 对应的 string 的⻓度。当 key 存放的类似不是 string 时报错。语法STRLEN key时间复杂度O(1)返回值string 的⻓度。或者当 key 不存在时返回 0。redis SET mykey Hello world OK redis STRLEN mykey (integer) 11 redis STRLEN nonexisting (integer) 01.5 命令小结命令执⾏效果时间复杂度set key value [key value...]设置 key 的值是 valueO(k), k 是键个数get key获取 key 的值O(1)del key [key ...]删除指定的 keyO(k), k 是键个数mset key value [key value...]批量设置指定的 key 和 valueO(k), k 是键个数mget key [key ...]批量获取 key 的值O(k), k 是键个数incr key指定的 key 的值 1O(1)decr key指定的key的值-1O(1)incrby key n指定的key的值nO(1)decrby key n指定的 key 的值 -nO(1)incrbyfloat key n指定的 key 的值 nO(1)append key value指定的 key 的值追加 valueO(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)二、内部编码字符串类型的内部编码有 3 种int8 个字节的⻓整型。embstr⼩于等于 39 个字节的字符串。raw⼤于 39 个字节的字符串。Redis 会根据当前值的类型和⻓度动态决定使⽤哪种内部编码实现。整型类型⽰例如下短字符串示例如下⻓字符串⽰例如下长度超过39字节小数存储示例其实是字符串三、string的应用场景3.1 缓存功能缓存Cache功能下图是⽐较典型的缓存使⽤场景其中 Redis 作为缓冲层MySQL 作为存储层绝⼤部分请求的数据都是从 Redis 中获取。由于 Redis 具有⽀撑⾼并发的特性所以缓存通常能起到加速读写和降低后端压⼒的作⽤。伪代码模拟实现过程假设业务是根据⽤⼾ uid 获取⽤⼾信息UserInfo getUserInfo(long uid) { ... }⾸先从 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; }如果没有从 Redis 中得到⽤⼾信息及缓存 miss则进⼀步从 MySQL 中获取对应的信息随后写⼊缓存并返回// 如果缓存未命中miss if (value null) { // 从数据库中根据 uid 获取⽤⼾信息 UserInfo userInfo MySQL 执⾏ SQLselect * 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 的访问数。与 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 的性能明显下降的。3.2 计数功能许多应⽤都会使⽤ Redis 作为计数的基础⼯具它可以实现快速计数、查询缓存的功能同时数据可以异步处理或者落地到其他数据源。如下图所⽰例如视频⽹站的视频播放次数可以使⽤Redis 来完成⽤⼾每播放⼀次视频相应的视频播放数就会⾃增 1。// 在 Redis 中统计某视频的播放次数 long incrVideoCounter(long vid) { key video: vid; long count Redis 执⾏命令incr key return counter; }实际中要开发⼀个成熟、稳定的真实计数系统要⾯临的挑战远不⽌如此简单防作弊、按照不同维度计数、避免单点问题、数据持久化到底层数据源等。3.3 共享会话⼀个分布式 Web 服务将⽤⼾的 Session 信息例如⽤⼾登录信息保存在各⾃的服务器中但这样会造成⼀个问题出于负载均衡的考虑分布式服务会将⽤⼾的访问请求均衡到不同的服务器上并且通常⽆法保证⽤⼾每次请求都会被均衡到同⼀台服务器上这样当⽤⼾刷新⼀次访问是可能会发现需要重新登录这个问题是⽤⼾⽆法容忍的。举个例子方源前些天生病了声带发炎了完全说不出话就到医院挂个号挂了一个专家号说这个病需要观察一周一周之后再来看看。很快一周过去之后方源发现医生变了一个人从谢含沫变成了商心慈。商心慈并不知道房源的病例然后她就拿着方源的就诊卡刷了一下就在电脑上出现了他的病例。3.4 手机验证码很多应⽤出于安全考虑会在每次进⾏登录时让⽤⼾输⼊⼿机号并且配合给⼿机发送验证码然后让⽤⼾再次输⼊收到的验证码并进⾏验证从⽽确定是否是⽤⼾本⼈。为了短信接⼝不会频繁访 问会限制⽤⼾每分钟获取验证码的频率例如⼀分钟不能超过 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 的字符串类型。像这种发短信的操作都有专门的SDK来实现的第三方只不过需要米就是了。