string类型
Redis中的string,直接就是按照二进制数据的方式存储的(也就是不会做任何的编码转换,存的是什么,取出来还是什么)。Redis不仅仅可以存储文本数据,还可以存整数、普通的文本字符串、JSON、XML、二进制数据(图片,视频,音频)等等,不过对于视频和音频来说,只是理想的状态下能存储。因为这两个体积可能会比较大,Redis对于string类型,限制了大小最大是512M。
设置和获取
SET
将 string 类型的 value 设置到 key 中。如果 key 之前存在,则覆盖,无论原来的数据类型是什么。之前关于此 key 的 TTL 也全部失效。
redis 文档给出的语法格式说明:
[]
相当于一个独立的单元,表示可选项,其中|
表示“或者”的意思,多个只能出现一个。[]
和[]
之间,是可以同时存在的。
语法:SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
介绍一下其中的选项;
- EX seconds:以秒为单位,设置超时时间。
- PX milliseconds:以毫秒为单位,设置超时时间。
- NX:如果key不存在,才设置;如果key存在,则不设置(返回nil)。
- XX:如果key存在,才设置(相当于更新key的value);如果key不存在,则不设置(返回nil)。
例如set key1 value1 ex 10
,相当于set key1 value1
和expire key1 10
。两条命令分开写。
注意:由于带选项的 SET 命令可以被 SETNX 、 SETEX 、 PSETEX 等命令代替,所以之后的版本中,Redis 可能进行合并。
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:
- 如果设置成功,返回 OK。
- 如果由于 SET 指定了 NX 或者 XX 但条件不满足,SET 不会执行,并返回 (nil)。
示例:
127.0.0.1:6379> set key1 111 ex 10 nx //key1设置为10后过期,nx表示不存在才设置
OK
127.0.0.1:6379> get key1
"111"
127.0.0.1:6379> ttl key1
(integer) 3
127.0.0.1:6379> ttl key1
(integer) 1
127.0.0.1:6379> ttl key1
(integer) -2127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 111 ex 10 xx // 由于没有key1,所以xx选项会返回nil
(nil)
GET
获取 key 对应的 value。如果 key 不存在,返回 nil。如果 value 的数据类型不是 string,会报错。
语法:get key
。
示例:
127.0.0.1:6379> set key1 111
OK
127.0.0.1:6379> lpush key2 222 333 444
(integer) 3
127.0.0.1:6379> get key2
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> type key2
list
命令有效版本:1.0.0 之后。
时间复杂度:O(1)。
返回值:key 对应的 value,或者 nil 当 key 不存在。
MGET
⼀次性获取多个 key 的值。如果对应的 key 不存在或者对应的数据类型不是 string,返回 nil。
语法:mget key [key ...]
示例:
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 someOtherKey
1) "Hello"
2) "World"
3) (nil)
命令有效版本:1.0.0 之后。
时间复杂度:O(N) N 是 key 数量。
返回值:对应 value 的列表。
MSET
Mset 命令用于同时设置一个或多个 key-value 对。
语法:MSET key1 value1 key2 value2 .. keyN valueN
示例:
127.0.0.1:6379> mset key1 hello key2 world
OK
127.0.0.1:6379> mget key1 key2
1) "hello"
2) "world"
命令有效版本:1.0.1 之后。
时间复杂度:O(N) N 是 key 数量。
返回值:永远是 OK。
SETNX
命令在指定的 key 不存在时,为 key 设置指定的值。
语法:SETNX KEY_NAME VALUE
示例:
127.0.0.1:6379> EXISTS job # job 不存在
(integer) 0127.0.0.1:6379> SETNX job "programmer" # job 设置成功
(integer) 1127.0.0.1:6379> SETNX job "code-farmer" # 尝试覆盖 job ,失败
(integer) 0127.0.0.1:6379> GET job # 没有被覆盖
"programmer"
SETEX
为指定的 key 设置值及其过期时间。如果 key 已经存在, SETEX 命令将会替换旧的值。
语法:SETEX KEY_NAME TIMEOUT VALUE
示例:
127.0.0.1:6379> set key1 111
OK
127.0.0.1:6379> setex key1 10 222
OK
127.0.0.1:6379> get key1
"222"
命令有效版本:大于等于 2.0.0
返回值:设置成功时返回 OK 。
PSETEX
以毫秒为单位设置 key 的生存时间。
语法:PSETEX key1 EXPIRY_IN_MILLISECONDS value1
示例:
127.0.0.1:6379> PSETEX mykey 1000 "Hello"
OK
127.0.0.1:6379> PTTL mykey
999
127.0.0.1:6379> GET mykey
1) "Hello"
计数命令
INCR
将 key 对应的 string 表示的数字加⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
语法:INCR KEY_NAME
示例:
127.0.0.1:6379> set key2 100
OK
127.0.0.1:6379> incr key2
(integer) 101
127.0.0.1:6379> set key3 abc
OK
127.0.0.1:6379> incr key3
(error) ERR value is not an integer or out of range
命令有效版本:1.0.0 之后。
时间复杂度:O(1)。
返回值:integer 类型的加完后的数值。
INCRBY
将 key 中储存的数字加上指定的增量值。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。本操作的值限制在 64 位(bit)有符号数字表示之内。
语法:INCRBY KEY_NAME INCR_AMOUNT
示例:
127.0.0.1:6379> get key2
"101"
127.0.0.1:6379> incrby key2 30
(integer) 131
命令有效版本:1.0.0 之后。
时间复杂度:O(1)。
返回值:integer 类型的加完后的数值。
DECR
将 key 对应的 string 表⽰的数字减⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
语法:DECR KEY_NAME
示例:
# 对存在的数字值 key 进行 DECR
127.0.0.1:6379> SET failure_times 10
OK
127.0.0.1:6379> DECR failure_times
(integer) 9# 对不存在的 key 值进行 DECR
127.0.0.1:6379> EXISTS count
(integer) 0
127.0.0.1:6379> DECR count
(integer) -1# 对存在但不是数值的 key 进行 DECR
127.0.0.1:6379> SET company YOUR_CODE_SUCKS.LLC
OK
127.0.0.1:6379> DECR company
(error) ERR value is not an integer or out of range
命令有效版本:1.0.0 之后。
时间复杂度:O(1)。
返回值:integer 类型的减完后的数值。
DECRBY
将 key 对应的 string 表⽰的数字减去对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
语法:DECRBY KEY_NAME DECREMENT_AMOUNT
示例:
# 对已存在的 key 进行 DECRBY
127.0.0.1:6379> SET count 100
OK
127.0.0.1:6379> DECRBY count 20
(integer) 80# 对不存在的 key 进行DECRBY
127.0.0.1:6379> EXISTS pages
(integer) 0
127.0.0.1:6379> DECRBY pages 10
(integer) -10
命令有效版本:1.0.0 之后。
时间复杂度:O(1)。
返回值:integer 类型的减完后的数值。
INCRBYFLOAT
为 key 中所储存的值加上指定的浮点数增量值。如果 key 不存在,那么 INCRBYFLOAT 会先将 key 的值设为 0 ,再执行加法操作。
语法:INCRBYFLOAT KEY_NAME INCR_AMOUNT
示例:
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 5.0e3
"OK"
127.0.0.1:6379> INCRBYFLOAT mykey 2.0e2
"5200"
命令有效版本:2.6.0 之后。
时间复杂度:O(1)。
返回值:加/减完后的数值。
其它命令
APPEND
用于为指定的 key 追加值。
如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。
如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。
语法:APPEND KEY VALUE
示例:
127.0.0.1:6379> exists key1 // 确保key1不存在
(integer) 0
127.0.0.1:6379> append key1 hello // 相当于set key1 hello
(integer) 5 // 字符串长度
127.0.0.1:6379> append key1 world //在hello后追加world
(integer) 10
127.0.0.1:6379> get key1
"helloworld"
命令有效版本:2.0.0 之后。
时间复杂度:O(1)。追加的字符串⼀般⻓度较短, 可以视为 O(1)。
返回值:追加完成之后 string 的⻓度。
GETRANGE
用于获取存储在指定 key 中字符串的子字符串。字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)。
语法:GETRANGE KEY_NAME start end
示例:
127.0.0.1:6379> set key2 "Hello World"
OK
127.0.0.1:6379> getrange key2 0 4
"Hello"127.0.0.1:6379> getrange key2 -5 -1 // 倒数第5 ~ 倒数第一
"World"
命令有效版本:2.4.0 之后。
时间复杂度:O(N)。N 为 [start, end] 区间的⻓度. 由于 string 通常⽐较短, 可以视为是 O(1)
返回值:string 类型的子串。
SETRANGR
用指定的字符串覆盖给定 key 所储存的字符串值,覆盖的位置从偏移量 offset 开始。
语法:SETRANGE KEY_NAME OFFSET VALUE
示例:
127.0.0.1:6379> get key2
"Hello World"
127.0.0.1:6379> setrange key2 6 Redis
(integer) 11
127.0.0.1:6379> get key2
"Hello Redis"
命令有效版本:2.2.0 之后。
时间复杂度:O(N), N 为 value 的⻓度. 由于⼀般给的 value ⽐较短, 通常视为 O(1)。
返回值:替换后的 string 的⻓度。
STRLEN
用于获取指定 key 所储存的字符串值的长度。当 key 储存的不是字符串值时,返回一个错误。
语法:STRLEN KEY_NAME
示例:
127.0.0.1:6379> get key2
"Hello Redis"
127.0.0.1:6379> strlen key2
(integer) 11
命令有效版本:2.2.0 之后。
时间复杂度:O(1)。
返回值:string 的⻓度。或者当 key 不存在时,返回 0。
String的使用场景
缓存功能
其中Redis作为缓存层,MySQL作为存储层,绝大部分请求的数据都是从Reid’s中获取。由于R额滴神具有高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。
如图所示的整体思路是:应用服务器访问数据的时候,先查询Redis,如果Redis上有要查询的数据,就直接从Redis取数据交给应用服务器,不继续访问数据库了。如果Redis上没有要查询的数据,再读取MySQL,把读取到的结果返回给应用服务器,同时,把这个数据也写入到Redis中。
Reids中常存储的是热点数据,但是这样的策略存在一个明显的问题:随着时间的推移,肯定会有许多的key在Redis中访问不到,从而在MySQL中读取并写入Redis中,此时Redis中的数据不是就越来越多了吗?面对这种问题,解决的方法有两种:
- 在把数据写给Redis的同时,给这个key设置一个过期时间。
- Redis在内存不足的时候,提供了淘汰策略。淘汰策略我们在后面的时候会讲到。
计数功能
许多应用都会使用Redis作为计数的基础工具,它可以实现快速计数、查询缓存的功能,同时数据还可以异步处理或者落地到其他数据源。下图所示的视频播放次数可以使用Redis来完成:用户没有播放一次视频,相应的视频播放数就会自增1。
现在有许多公司都喜欢收集用户的数据,统计数据是为了进一步明确用户的需求,根据需求改进和迭代产品。
但是Redis并不擅长统计数据,比如,想要在上述的Redis中,统计播放量前100的视频有哪些,基于Redis搞就很麻烦;相反,使用MySQL来存储上述的数据,一个sql语句就能搞定。
下面用伪代码来实现上面的要求
// 在 Redis 中统计某视频的播放次数
long incrVideoCounter(long vid)
{key = "video:" + vid;long count = Redis 执⾏命令:incr keyreturn counter;
}
验证码
很多应用出于安全考虑,会在每次进行登录时,让用户输⼊⼿机号并且配合给⼿机发送验证码,然后让用户再次输⼊收到的验证码并进行验证,从而确定是否是用户本人。为了短信接口不会频繁访问,会限制用户每分钟获取验证码的频率,例如⼀分钟不能超过 5 次。
手机验证码的有两步:
- 生成验证码
用户输入手机号,点击获取验证码。但是一般都会有时间限制,比如说1分钟之后才能再次重新获取验证码,验证码5分钟之内有效。这样做的原因是怕用户频繁的获取验证码,对于服务器端压力就会增大。 - 检查验证码
把短信收到的验证码这一串数字,提交到系统中,然后系统在进行判断验证码是否正确。
此功能可以⽤以下伪代码说明基本实现思路:
String 发送验证码(phoneNumber)
{key = "shortMsg:limit:" + phoneNumber;// 设置过期时间为 1 分钟(60 秒)// 使⽤ NX,只在不存在 key 时才能设置成功bool r = Redis 执⾏命令:set key 1 ex 60 nxif (r == false) {// 说明之前设置过该⼿机的验证码了long c = Redis 执⾏命令:incr keyif (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;}
}