文章目录
Redis重要总结
它提供了五种数据结构:
字符串、哈希、列表、集合、有序集合,一个数据结构内部有几种编码方式。
一、特性
- 数据放在内存中
- C语言实现的
- 单线程架构,预防了多线程可能产生的竞争问题
二、全局命令
Redis有5种数据结构,他们是键值对中的值,对于键来说有一些通用的命令。
1、查看所有键
keys *
2、键总数
dbsize
注意:
dbsize命令在计算键总数时不会遍历所有键,而是直接获取Redis内置的键总数变量,所以dbsize命令的时间复杂度是O(1)。而keys命令会遍历所有键,所以它的时间复杂度是O(n),当Redis保存了大量键时,线上环境禁止使用。
3、检查键是否存在
exists key:如果键存在则返回1,不存在则返回0
4、删除键
del key [key ...]
返回结果为成功删除键的个数,假设删除一个不存在的键,就会返回0.
del是一个通用命令,无论值是什么数据结构类型,del命令都可以将其删除。
5、键过期expire 英[ɪkˈspaɪə(r)]终止
expire key seconds
expire:失效,终止
Redis支持对键添加过期时间,当超过过期时间后,会自动删除键,例如为键hello设置了10秒过期时间
expire hello 10
6、ttl命令会返回键的剩余过期时间,它有3种返回值:
1、大于等于0的整数:键剩余的过期时间
2、-1:键没设置过期时间
3、-2:键不存在
ttl hello
7、键的数据结构类型
type key
例如键hello是字符串类型,返回结果为string。键mylist是列表类型,返回结果为list,如果键不存在,返回null.
三、String(字符串)
常用命令
1、设置值
set key value [ex seconds] [px milliseconds] [nx|xx]
set命令有几个选项:
1、ex seconds:为键设置秒级过期时间。
2、px milliseconds:为键设置毫秒级过期时间。
3、nx:键必须不存在,才可以设置成功,用于添加。
4、xx:与nx相反,键必须存在,才可以设置成功,用于更新。
除了set选项,Redis还提供了setex和setnx两个命令:
它们的作用和ex和nx选项是一样的。下面的例子说明了set、setnx、set xx的区别。
SETNX key value
只有在 key 不存在时设置 key 的值。
SETEX key seconds value
将值 value 关联到 key ,并将 key 的过期时间设为 seconds 、(以秒为单位)。
setnx和setxx在实际使用中有什么应用场景吗?以setnx命令为例子,由于Redis的单线程命令处理机制,如果有多个客户端同时执
行setnx key value,根据setnx的特性只有一个客户端能设置成功,setnx可以作为分布式锁的一种 实现方案。
2、获取值
get key
如果要获取的键不存在,则返回nil(空)
3、批量设置值
mset key value [key value ...]
4、批量获取值
mget key [key ...]
mget a b c d
如果有些键不存在,那么它的值为nil(空),结果是按照传入键的顺序返回
说明:批量操作命令可以有效提高开发效率。
假如没有mget这样的命令,,要执行n次get命令,具体耗时如下:
n次get时间 = n次网络时间 + n次命令时间
使用mget命令后,具体耗时如下:
n次get时间 = 1次网络时间 + n次命令时间
5、计数
incr key
incr命令用于对值做自增操作,返回结果分为三种情况:
1、值不是整数,返回错误。 2、值是整数,返回自增后的结果。 3、键不存在,按照值为0自增,返回结果为1
除了incr命令,Redis提供了decr(自减)、incrby(自增指定数字)、decrby(自减指定数字)、incrbyfloat(自增浮点数):
decr key
incrby key increment
decrby key decrement
incrbyfloat key increment
计数:incr key
incr命令用于对值做自增操作,返回结果分为三种情况:
- 1、值不是整数,返回错误。
- 2、值是整数,返回自增后的结果。
- 3、键不存在,按照值为0自增,返回结果为1
SETEX key seconds value
将值 value 关联到 key ,并将 key 的过期时间设为 seconds 、(以秒为单位)。
应用场景(缓存、计数、限速)
- 1、缓存
- 2、计数
- 3、限速
很多应用出于安全的考虑,会在每次进行登录时,让用户输入手机验证码,从而确定是否是用户本人。但是为了短信接口不被频繁访问,会限制
用户每分钟获取验证码的频率。
phoneNum = "138xxxxxxxx";
key = "shortMsg:limit:" + phoneNum;
// SET key value EX 60 NX
isExists = redis.set(key,1,"EX 60","NX");
if(isExists != null || redis.incr(key) <=5)
{
// 通过
}
else
{
// 限速
}
四、哈希(用于保存对象,每个对象只用一个键保存)
1、命令
1、设置值
hset key field value
下面为user:1添加一对field-value:
hset user:1 name tom
如果设置成功会返回1,反之会返回0。
2、获取值
hget key field
3、删除field
hdel key field [field ...]
hdel会删除一个或多个field,返回结果为成功删除field的个数
4、计算field个数
hlen key
5、批量设置或获取field-value
hmget key field [field ...]
hmset key field value [field value ...]
6、判断field是否存在 hexists
hexists key field
7、获取所有field
hkeys key
8、获取所有value
hvals key
9、获取所有的field-value
hgetall key
10、hincrby hincrbyfloat
11、计算value的字符串长度
hstrlen key field
2、应用场景(缓存用户信息)
可用于缓存用户信息
每个用户属性使用一对field-value,但是只用一个键保存。
hmset user:1 name tom age 23 city beijing
五、列表list(一个键,装的不是一个数字)
在redis中,可以对列表两端进行插入和弹出操作,也有阻塞操作,还可以获得指定范围的元素列表,获取指定索引下表的元素等。
它可以充当栈和队列的角色,在实际开发上有很多应用场景。
命令
1、添加
从右边插入元素
rpush
从左边插入元素
lpush key value [value ...]
向某个元素前或者后插入元素
linsert key before|after pivot value
linsert命令会从列表中找到等于pivot的元素,在其前(before)或者后(after)插入一个新的元素value。
2、查找
获取指定范围内的元素列表
lrange key start end
索引下表有两个特点
第一,索引下标从左到右分别是0到N-1,但是从右到左分别是-1到-N。
第二,lrange中的end选项包含了自身,这个和很多编程语言不包含end不太 相同,例如想获取列表的第2到第4个元素
获取列表指定索引下标的元素
lindex key index
获取列表的长度
llen key
3、删除
从列表左侧弹出元素
lpop key
从列表右侧弹出
rpop key
删除指定元素
lrem key count value
lrem命令会从列表中找到等于value的元素进行删除,根据count的不同
分为三种情况:
1、count>0,从左到右,删除最多count个元素。
2、count<0,从右到左,删除最多count绝对值个元素。
3、count=0,删除所有。
按照索引范围修剪列表
ltrim key start end
4、修改
修改指定索引下标的元素:
lset key index newValue
5、阻塞操作
blpop key [key ...] timeout
brpop key [key ...] timeout
blpop和brpop是lpop和rpop的阻塞版本,它们除了弹出方向不同,使用方法基本相同,所以下面以brpop命令进行说明,brpop命令包含两个参数:
1、key[key...]:多个列表的键。
2、timeout:阻塞时间(单位:秒)。
列表不为空:客户端会立即返回。
列表为空:
如果timeout=3,那么客户端要等到3秒后返回,如果timeout=0,那么客户端一直阻塞等下去:
如果阻塞添加了数据element1,客户端立即返回。
注意点:
1、第一点,如果是多个键,那么brpop会从左至右遍历键,一旦有一个键能弹出元素,客户端立即返回。
2、第二点,如果多个客户端对同一个键执行brpop,那么最先执行brpop命令的客户端可以获取到弹出的值。
应用场景(消息队列(使用阻塞))
消息队列
如图2-21所示,Redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端
使用brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用 性。
列表选择口诀
- 1、lpush+lpop=Stack(栈)
- 2、lpush+rpop=Queue(队列)
- 3、lpsh+ltrim=Capped Collection(有限集合)
- 4、lpush+brpop=Message Queue(消息队列)
六、集合Set
集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。
Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集,合理地使用好集合类型,能在实际开发中解决很多实际问题。
命令
集合内操作
1、添加元素
sadd key element [element ...]
2、删除元素
srem key element [element ...]
3、计算元素个数
scard key
scard的时间复杂度为O(1),它不会遍历集合所有元素,而是直接用 Redis内部的变量
4、判断元素是否在集合中
sismember key element
5、随机从集合返回指定个数元素
srandmember key [count]
6、从集合随机弹出元素
spop key
srandmember和spop都是随机从集合选出元素,两者不同的是spop命令执行后,元素会从集合中删除,而srandmember不会。
7、获取所有元素
smembers key
集合间操作
1、求多个集合的交集
sinter key [key ...]
例如下面代码是求user:1:follow和user:2:follow两个集合的交集:
sinter user:1:follow user:2:follow
2、求多个集合的并集
suinon key [key ...]
3、求多个集合的差集
sdiff key [key ...]
4、将交集、并集、差集的结果保存
sinterstore destination key [key ...]
suionstore destination key [key ...]
sdiffstore destination key [key ...]
集合间的运算在元素较多的情况下会比较耗时,所以Redis提供了上面三个命令(原命令+store)将集合间交集、并集、差集的结果保存在 destination key中,destination还是set。
使用场景(给用户添加标签(不可以重复))
集合类型比较典型的使用场景是标签(tag)。例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣 点就是标签。
1、给用户添加标签
sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
2、给标签添加用户
sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
七、有序集合(按分数score)
集合内
1、添加元素
zadd key score member [score member ...]
·Redis3.2为zadd命令添加了nx、xx、ch、incr四个选项:
1、nx:member必须不存在,才可以设置成功,用于添加。
2、xx:member必须存在,才可以设置成功,用于更新。
3、ch:返回此次操作后,有序集合元素和分数发生变化的个数
4、incr:对score做增加,相当于后面介绍的zincrby。
2、计算成员个数
zcard key
3、计算某个成员的分数
zscore key member
4、计算成员的排名
zrank key member
zrevrank key member
zrank是从分数从低到高返回排名,zrevrank反之。(排名从0开始计算)
5、删除成员
zrem key member [member ...]
6、增加成员的分数
zincrby key increment member
下面操作给tom增加了9分,分数变为了260分:
zincrby user:ranking 9 tom
7、返回指定排名范围的成员
zrange key start end [withscores]
zrevrange key start end [withscores]
8、返回指定分数范围的成员
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores] [limit offset count]
9、返回指定分数范围成员个数
zcount key min max
10、删除指定排名内的升序元素
zremrangebyrank key start end
11、删除指定分数范围的成员
zremrangebyscore key min max
集合间操作
1、交集
zinterstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
这个命令参数较多,下面分别进行说明:
·destination:交集计算结果保存到这个键。
·numkeys:需要做交集计算键的个数。
·key[key...]:需要做交集计算的键。
·weights
weight[weight...]:每个键的权重,在做交集计算时,每个键中的每个member会将自己分数乘以这个权重,每个键的权重默认是1。
·aggregate sum|min|max:计算成员交集后,分值可以按照sum(和)、min(最小值)、max(最大值)做汇总,默认值是sum。
2、并集
zunionstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
该命令的所有参数和zinterstore是一致的,只不过是做并集计算
应用场景(排行榜)
有序集合比较典型的使用场景就是排行榜系统。例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间、按照播
放数量、按照获得的赞数。本节使用赞数这个维度,记录每天用户上传视频 的排行榜。
展示获取赞数最多的十个用户
zrevrangebyrank user:ranking:2016_03_15 0 9
八、键管理
针对单个键的命令,前面几节已经介绍过一部分了,例如type、del、object、exists、expire等,下面将介绍剩余的几个重要命令。
1.键重命名
rename key newkey
为了防止被强行rename,Redis提供了renamenx命令,确保只有newKey不存在时候才被覆盖
2.随机返回一个键
randomkey
3.键过期
expire:终止,失效
·expire key seconds:键在seconds秒后过期。
·expireat key timestamp:键在秒级时间戳timestamp后过期。
Redis2.6版本后提供了毫秒级的过期方案: ·pexpire key milliseconds:键在milliseconds毫秒后过期。
·pexpireat key milliseconds-timestamp键在毫秒级时间戳timestamp后过期。
ttl命令和pttl都可以查询键的剩余过期时间,但是pttl精度更高可以达到毫秒级别,有3种返回值:
·大于等于0的整数:键剩余的过期时间(ttl是秒,pttl是毫秒)。
·-1:键没有设置过期时间。
·-2:键不存在。
persist命令可以将键的过期时间清除
Persist:持续存在
persist key
1、迁移键(move、dump+restore(dump使用RDB)、migrate)
migrate 英[maɪˈɡreɪt] 迁移
迁移键功能非常重要,因为有时候我们只想把部分数据由一个Redis迁移到另一个Redis(例如从生产环境迁移到测试环境),Redis发展历程中提供了move、dump+restore、migrate三组迁移键的方法,它们的实现方式以及使用的场景不太相同,下面分别介绍:
-
1、move
move key db
move命令用于在Redis内部进行数据迁移,Redis内部可以有多个数据库,由于多个数据库功能后面会进行介绍,这里只需要知道Redis内部可以有多个数据库,彼此在数据上是相互隔离的,move key db就是把指定的键从源数据库移动到目标数据库中,但笔者认为多数据库功能不建议在生产环境使用,所以这个命令读者知道即可。 -
2、dump+restore
dump+restore可以实现在不同的Redis实例之间进行数据迁移的功能,整个迁移的过程分为两步:
1)在源Redis上,dump命令会将键值序列化,格式采用的是RDB格式。
2)在目标Redis上,restore命令将上面序列化的值进行复原,其中ttl参数代表过期时间,如果ttl=0代表没有过期时间。
有关dump+restore有两点需要注意:
第一,整个迁移过程并非原子性的,而是通过客户端分步完成的。
第二,迁移过程是开启了两个客户端连接,所以dump的结果不是在源Redis和目标Redis之间进行传输,下面用一个 例子演示完整过程。
例子:
1、在源Redis上执行dump:
redis-source> set hello world OK
2、在目标Redis上执行restore:
redis-target> restore hello 0 “\x00\x05world\x06\x00\x8f<T\x04%\xfcNQ”
-
3、migrate
migrate host port key|"" destination-db timeout [copy] [replace] [keys key [key …]]下面对migrate的参数进行逐个说明:
·host:目标Redis的IP地址。
·port:目标Redis的端口。
·key|"":在Redis3.0.6版本之前,migrate只支持迁移一个键,所以此处是要迁移的键,但Redis3.0.6版本之后支持迁移多个键,如果当前需要迁移多 个键,此处为空字符串""。
·destination-db:目标Redis的数据库索引,例如要迁移到0号数据库,这migrate命令也是用于在Redis实例间进行数据迁移的,实际上migrate命令就是将dump、restore、del三个命令进行
组合,从而简化了操作流程。 migrate命令具有原子性,而且从Redis3.0.6版本以后已经支持迁移多个键的
功能,有效地提高了迁移效率,migrate在10.4节水平扩容中起到重要作用。里就写0。
·timeout:迁移的超时时间(单位为毫秒)。 ·[copy]:如果添加此选项,迁移后并不删除源键。
·[replace]:如果添加此选项,migrate不管目标Redis是否存在该键都会正常迁移进行数据覆盖。
·[keys key[key…]]:迁移多个键,例如要迁移key1、key2、key3,此处填写“keys key1 key2 key3”。
2、遍历键(keys、scan)
Redis提供了两个命令遍历所有的键,分别是keys和scan,本节将对它们介绍并简要分析。
-
1、全量遍历键
keys 参数
实际上keys命令是支持pattern匹配的
*代表匹配任意字符。
[]代表匹配部分字符,例如[1,3]代表匹配1,3,[1-10]代表匹配1到10的任意数字。
\x用来做转义,例如要匹配星号、问号需要进行转义。
例子:
匹配以j,r开头,紧跟edis字符串的所有键:
keys [j,r]edis
但是如果考虑到Redis的单线程架构就不那么美妙了,如果Redis包含了大量的键,执行keys命令很可能会造成Redis阻塞 -
2、渐进式遍历
Redis从2.8版本后,提供了一个新的命令scan,它能有效的解决keys命令存在的问题。和keys命令执行时会遍历所有键不同,scan
采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是O(1),但是要真正实现keys的功能,
需要执行多次scan。Redis存储键值对实际使用的是hashtable的数据结构.
scan不是一次遍历所有的,第一次游标选择0,下一次使用上一次返回的游标作为结果,如下图的scan 5。
九、数据库管理
1、切换数据库
select dbIndex
与关系型数据库用字符来区分不同数据库名不同,Redis只是用数字作为多个数据库的实现。Redis默认配置中是有16个数据库:
databases 16
那么能不能像使用测试数据库和正式数据库一样,把正式的数据放在0号数据库,测试的数据库放在1号数据库,那么两者在数据上就不会彼此受影响了。事实真有那么好吗?
Redis3.0中已经逐渐弱化这个功能,例如Redis的分布式实现Redis Cluster只允许使用0号数据库,只不过为了向下兼容老版本的数据库功能,
该功能没有完全废弃掉,下面分析一下为什么要废弃掉这个“优秀”的功能 呢?总结起来有三点:
- 1、Redis是单线程的。如果使用多个数据库,那么这些数据库仍然是使用一个CPU,彼此之间还是会受到影响的。
- 2、多数据库的使用方式,会让调试和运维不同业务的数据库变的困难,假如有一个慢查询存在,依然会影响其他数据库,这样会使得别的业务方定位问题非常的困难。
- 3、部分Redis的客户端根本就不支持这种方式。即使支持,在开发的时候来回切换数字形式的数据库,很容易弄乱
2、清空数据库(flushdb/flushall)
flushdb/flushall命令用于清除数据库,两者的区别的是flushdb只清除当前数据库,flushall会清除所有数据库。
flushdb/flushall命令会将所有数据清除,一旦误操作后果不堪设想,第12章会介绍rename-command配置规避这个问题,以及如何在误操作后快速恢 复数据。
如果当前数据库键值数量比较多,flushdb/flushall存在阻塞Redis的可能性。
十、章节总结
- 1)Redis提供5种数据结构,每种数据结构都有多种内部编码实现。
- 2)纯内存存储、IO多路复用技术、单线程架构是造就Redis高性能的三个因素。
- 3)由于Redis的单线程架构,所以需要每个命令能被快速执行完,否则会存在阻塞Redis的可能,理解Redis单线程命令处理机制是开发和运维Redis 的核心之一。
- 4)批量操作(例如mget、mset、hmset等)能够有效提高命令执行的效率,但要注意每次批量操作的个数和字节数。
- 5)了解每个命令的时间复杂度在开发中至关重要,例如在使用keys、hgetall、smembers、zrange等时间复杂度较高的命令时,需要考虑数据规模 对于Redis的影响。
- 6)persist命令可以删除任意类型键的过期时间,但是set命令也会删除字符串类型键的过期时间,这在开发时容易被忽视。
- 7)move、dump+restore、migrate是Redis发展过程中三种迁移键的方式,其中move命令基本废弃,migrate命令用原子性的方式实现了dump+restore,并且支持批量操作,是Redis Cluster实现水平扩容的重要工 具。
- 8)scan命令可以解决keys命令可能带来的阻塞问题,同时Redis还提供了hscan、sscan、zscan渐进式地遍历hash、set、zset。