Redis笔记

Redis常用命令

登陆redis

redis-cli -h [ip] -p [port]
示例:redis-cli -h 127.0.0.1 -p 6379

查看所有键

keys *

查看键总数

dbsize

检查指定键是否存在

exists [key]

删除键

del [key]

设置键过期时间 [time]值为秒,如10秒过期则输入10即可

expire [key] [time]

查看键过期时间 return>0==剩余过期时间,return=-1==未设置过期时间,return=-2==键不存在

ttl [key]

查看键的数据结构,键若不存在返回none

type [key]

设置键值对

set [key] [value]

设置键值对【必须没有该键值对才可设置成功,常用于分布式环境下】

setnx [key] [value]

更新键值对【必须已有该键值对才可更新成功】

set [key] [value] xx

设置键值对并设置秒级过期时间

set [key] [value] ex [time]

设置键值对并设置毫秒级过期时间

set [key] [value] px [time]

获取值[若不存在该键值对则返回nil,即null]

get [key]

批量设置键值对

mset [key] [value] [key] [value] [key] [value]....

批量获取值

mget [key] [key] [key]....

计数功能

incr [key]

该命令有多种返回结果,通常为如下几种

  1. 值非整数,返回错误

  2. 值是整数,返回自增后的结果

  3. key不存在,按照默认值为0后+1操作并返回该结果,即结果为1

自减操作(与incr命令返回值类似)

decr [key]

自增指定数字

incrby [key] [number]

自减指定数字

decrby [key] [number]

自增浮点数

incrbyfloat [key] [number]

不常用命令

追加操作

append [key] [value]

获取字符串长度(中文每个文字占用3个字节)

strlen [key]

设置并返回原有值

getset [key] [value]

设置指定位置的字符[point]为操作的字符串位置,从0开始计算

setrange [key] [point] [value]

获取部分字符串[start]开始位置,[end]结束为止,从0开始计算

getrange [key] [start] [end]

Hash

Redis的hash使用field-value格式

添加一个hash

hset [key] [field] [value] [field] [value]...

获取一个hash中的value

hget [key] [field]

现在有一个需求场景,我们需要在一个hash中保存用户的一些信息,field以user:id形式设置

hset userinfo user:1 {name:sa,age:10} user:2 {name:ua,age:20}

执行后会返回如下内容

(integer) 2

表示你已经插入了两条记录

现在,我们尝试取出其中用户id为1的记录

使用以下命令取出记录

hget userinfo user:1

会返回如下结果

"{name:sa,age:10}"

现在我们由于一些原因,比如该用户申请退出登陆了,因此我们需要清除这条记录。

hdel userinfo user:1

现在我们再来查询这个用户的信息

hget userinfo user:1

返回了如下内容

(nil)

继续查询用户id为2的用户信息

hget userinfo user:2

返回如下信息

"{name:ua,age:20}"

现在我们统计一下userinfo这个hash中保存了几条记录

hlen userinfo

返回如下结果

(integer) 1

现在我们再插入一些用户的信息

hset userinfo user:3 {name:sb,age:15} user:99 {name:aa,age:80}

然后查询一下该hash中有几条field记录

hlen userinfo

返回如下结果

(integer) 3

接着我们尝试判断userid为1的field是否存在

hexists userinfo user:1

返回了一个0,这代表这条记录不存在。

现在我们需要获取多个用户的信息,使用如下命令

hmget userinfo user:2 user:3 user:99

返回了如下内容

1) "{name:ua,age:20}"
2) "{name:sb,age:15}"
3) "{name:aa,age:80}"

现在我们尝试进行批量的插入(实际使用中使用hset依然可以进行批量插入,但建议仍使用hmset进行批量插入)

hmset userinfo user:1024 aaa user:1025 bbb user:1027 ccc

获取一下userinfo这个hash中的所有field

hkeys userinfo

返回如下内容

1) "user:2"
2) "user:3"
3) "user:99"
4) "user:1024"
5) "user:1025"
6) "user:1027"

再获取一下所有的value试试

hvals userinfo

返回如下内容

1) "{name:ua,age:20}"
2) "{name:sb,age:15}"
3) "{name:aa,age:80}"
4) "aaa"
5) "bbb"
6) "ccc"

尝试获取userinfo中的所有field-value组合

hgetall userinfo

返回如下内容

1) "user:2"
2) "{name:ua,age:20}"
3) "user:3"
4) "{name:sb,age:15}"
5) "user:99"
6) "{name:aa,age:80}"
7) "user:1024"
8) "aaa"
9) "user:1025"
10) "bbb"
11) "user:1027"
12) "ccc"

现在我们尝试一个场景,对电影的播放次数进行统计

执行以下三条命令,以设置三个电影的统计量

hincrby moviecount dragon 1
hincrby moviecount bigmax 1
hincrby moviecount hallo 1

现在我们来看看他们的统计量

hgetall moviecount
1) "dragon"
2) "1"
3) "bigmax"
4) "1"
5) "hallo"
6) "1"

可以看到每个电影的播放量都为1

现在我们来设置dragon这部电影,让它的播放量增加一次

hincrby moviecount dragon 1

再执行一次hgetall moviecount命令来查看他们的统计数量

返回如下的查询结果

1) "dragon"
2) "2"
3) "bigmax"
4) "1"
5) "hallo"
6) "1"

现在我们单独查看dragon这部电影的播放量

hget moviecount dragon

返回了2,代表我们已经成功的添加了一次播放量

我们还可以这样,让它一次性新增50条播放记录,只需要执行以下命令即可

hincrby moviecount dragon 50

再来查询dragon电影的播放量,可以看到返回了52

hincrbyfload [key] [field]

上方命令则是以浮点数的形式增加或减少,用法与hincrby命令一致

Hash命令汇总

设置或新增一个hash
hset key [field] [value]..
批量设置一个hash
hmset key [field] [value] [field] [value]..
查询一个hash的field的value值
hget [key] [field]
批量查询一个hash的多个field的value值
hmget [key] [field] [field]..
删除一个hash中的field-value组合
hdel [key] [field] [field]...
查询一个hash的field数量
hlen key
获取一个hash中的所有field-value组合
hgetall [key]
判断一个hash中某个field是否存在
hexists [key] [field]
获取一个hash中的所有field
hkeys [key]
获取一个hash中的所有value
hvals [key]
设置一个hash中假设不存在的field的值,若该field存在,则设置失败,返回0
hsetnx [key] [field] [value]
新增或对某个hash中的field的value值增加或减少指定数的值,若该值不为number类型,则返回异常
hincrby [key] [field] [number]
hincrbyfloat [key] [field] [floatnumber]
统计某个hash中的field的value值长度(一个中文字符统计为2个字符)
hstrlen key field

hash类型的内部编码有两种:ziplist和hashtable,当hash的field个数(默认512个)与value的值(默认64字节)小于配置的值时,会使用ziplist来存储,其中任意条件不满足时候使用hashtable来存储。

Redis List常用命令

List通常用来存放多个有序的字符串,单个list中最多可以存储2的32次方-1个元素。

list类型特点:有序 / 可重复

添加操作

右边添加元素

rpush [key] [value]...

示例

rpush lista a b c d e f

内存模型

左边添加元素

lpush [key] [value]...

示例

lpush lista a b c d e f

内存模型现在我们已经插入了总计12个元素,来看看他们吧

lrange lista 0 11

结果

1) "f"
2) "e"
3) "d"
4) "c"
5) "b"
6) "a"
7) "a"
8) "b"
9) "c"
10) "d"
11) "e"
12) "f"

现在我们要插入一个元素,就选在从左数起第一个a的前面进行插入吧。

linsert lista before a ss

再来查看一下该list中的元素

lrange lista 0 12

可以看到打印的结果如下

1) "f"
2) "e"
3) "d"
4) "c"
5) "b"
6) "ss"
7) "a"
8) "a"
9) "b"
10) "c"
11) "d"
12) "e"
13) "f"

ss这个字符串已经成功的插入到了a的前面

现在我们再尝试在a后面插入一个字符

linsert lista after a vv

查看结果

1) "f"
2) "e"
3) "d"
4) "c"
5) "b"
6) "ss"
7) "a"
8) "vv"
9) "a"
10) "b"
11) "c"
12) "d"
13) "e"
14) "f"

可以看到我们已经成功的在a的前面插入了ss,在a的后面插入了vv

list查找

现在我们需要查找这个list中的第一个元素

lrange lista 0 0

可以看到返回了一个f

继续查找,现在我们要查找最后一个元素

lrange lista 13 13

接着查找包含ss和vv这一段的所有元素

lrange lista 5 7

你可以发现,lrange命令后面的两个数字,就是元素在list中的位置-1

与一些编程语言不同的是,当你只想查找某一个位置的单个元素,你仍旧需要加上该元素的位置

lrange命令格式:

lrange [key] [start] [end]

当你执行

lrange [key] 0 -1

时,会返回该list中的所有元素

获取指定下标的值

lindex [key] [number]

获取list长度

llen [key]

删除元素

删除左侧第一个元素并返回这个被删除的元素

lpop [key]

删除右侧第一个元素并返回这个被删除的元素

rpop [key]

删除指定数量的元素

lrem [key] [count] [value]

[count]:数量大于0时从左往右删除最多count个元素

数量等于0时删除所有的和value相同的元素
数量小于0时从右到左删除最多count个元素

需要注意的是,如果list里没有存在key就会被当作空list处理,所以当 key 不存在的时候,这个命令会返回 0。

按照索引删除元素(仅保留start位置开始到end位置结束这一区域中的元素)

ltrim [key] [start] [end]

修改指定元素

lset [key] [index] [newValue]

[index]:需修改的元素下标

[newValue]:修改后的值+

阻塞删除

阻塞式删除与普通删除不同的是,它会有一个超时时间的设定

删除左侧第一个元素并返回这个被删除的元素

blpop [key] [timeout]

删除右侧第一个元素并返回这个被删除的元素

brpop [key] [timeout]

可以看到的是,与普通删除不同的是,多了一个timeout的参数。

timeout参数为0时,且该list中没有值时,客户端会一直等待返回。

timeout参数为正整数时,且该list中没有值时,会等待该<整数 * 1秒>的时间后返回一个空

以上两种状态保持中时,当该list中被添加了一个值时,会立即返回该值到客户端。

如果同时有多个客户端对该list执行此类阻塞式删除时,结果会返回给最先执行该类删除的客户端,其他客户端继续等待返回

根据Redis默认提供的这些方法,通常可以实现以下场景:

  • lpush+lpop=栈

  • lpush+rpop=队列

  • lpush+ltrim=有限集合

  • lpush+brpop=消息队列

Redis Set常用命令

set是一种无序且同类元素只能保存一个的类型,即set类型不允许重复元素,且保存的方式是无序的,并且不能通过下标来获取元素。

添加元素

sadd [key] [element]....

删除元素

srem [key] [element]....

计算指定set的元素个数

scard [key]

判断集合中是否有该元素(有返回1,没有返回0)

sismember [key] [element]

从指定集合内随机返回指定个数元素(count为需要返回的元素个数,该参数可不填,默认为返回1个元素)

srandmember [key] [count]

从指定集合随机移除指定个数元素并返回这些元素(count为需要返回的元素个数,该参数可不填,默认为返回1个元素)-redis3.2后支持count参数

spop [key] [count]

获取集合中所有元素

smembers [key]

集合间操作

求指定集合间的交集

sinter [key] [key]...

求指定集合间的并集

sunion [key] [key]...

求指定集合间的差集

sdiff [key] [key]...

集合间的操作保存

求指定集合间的交集并保存

sinterstore [savekey] [key] [key]...

求指定集合间的并集并保存

sunionstore [savekey] [key] [key]...

求指定集合间的差集并保存

sdiffstore [savekey] [key] [key]...

与集合间操作不同的是,在命令后都追加了stroe,并且需要添加一个savekey的参数,这个参数用来当作保存的集合的key

Redis ZSet常用命令

有序集合与Set类似,它保留了Set不能有相同元素的特性,但同时增加有序集合中的元素可以排序的特性。

它和List类型不同的是,List类型使用下标进行排序,而ZSet使用score作为排序的依据

向集合内添加元素

zadd [key] [score] [member] [score] [member]...

该命令有以下参数:

nx:member必须不存在,才可以设置成功

xx:member必须存在,才可以设置成功

ch:返回此命令后集合中元素和和scope变化的个数

incr:对score做增加操作

tips:有序集合相比无序集合提供了排序字段,但也因此产生了性能代价,zadd的时间复杂度为O(log(n)),sadd的时间复杂度为O(1)

统计成员数量

zcard [key]

查找某个member的score

zscore [key] [member]

计算成员位置

zrank [key] [member]
zrevrank [key] [member]

其中zrank是从低到高返回位置,zrevrank反之。

删除元素

zrem [key] [member]

操作指定元素的score,其中number可以为负数

zincrby [key] [number] [member]

返回指定范围位置的元素

zrange [key] [start] [end]
zrevrange [key] [start] [end]

由于有序集合是以score进行排序的,因此zrange是从低到高返回,zrevrange反之。且在这两个命令后追加withscores参数,则会同时返回这些元素的score

返回指定score范围的元素

zrangebyscore [key] [minnumber] [maxnumber]
zrevrangebyscore [key] [maxnumber] [minnumber]

其中zrangebyscore按score从低到高返回,zrevrangebyscore反之。且这两个命令还可以追加withscores参数,以便同时返回他们的score,同时minnumber和maxnumber参数还支持开区间(小括号)和闭去间(中括号),-inf和+inf分别代表无限小和无限大。

举个例子:

ZRANGEBYSCORE zset (1 5

返回所有符合条件1<score<=5的成员,而

ZRANGEBYSCORE zset (5 (10

则返回所有符合条件5<score<10的成员。

其他说明

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

返回有序集key中,所有score值介于min和max之间(包括等于min或max)的成员。有序集成员按score值递增(从小到大)次序排列。

具有相同score值的成员按字典序(lexicographical order)来排列(该属性是有序集提供的,不需要额外的计算)。

可选的LIMIT参数指定返回结果的数量及区间(就像SQL中的SELECTLIMIToffset,count),注意当offset很大时,定位offset的操作可能需要遍历整个有序集,此过程最坏复杂度为 O(N) 时间。

可选的

WITHSCORES

参数决定结果集是单单返回有序集的成员,还是将有序集成员及其score值一起返回。

返回指定score范围成员的个数

zcount [key] [minnumber] [maxnumber]

删除指定排名内升序元素

zremrangebyrank [key] [start] [end]

移除有序集key中,所有score值介于minmax之间(包括等于minmax)的成员。

自版本2.1.6开始,score值等于minmax的成员也可以不包括在内,详情请参见ZRANGEBYSCORE命令。

可用版本:

>= 1.2.0

时间复杂度:

O(log(N)+M),

N为有序集的基数,而M为被移除成员的数量。

返回值:

被移除成员的数量。

求ZSet之间的交集

ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

计算给定的一个或多个有序集的交集,其中给定key的数量必须以numkeys参数指定,并将该交集(结果集)储存到destination

默认情况下,结果集中某个成员的score值是所有给定集下该成员score值之和.

关于WEIGHTSAGGREGATE选项的描述,参见ZUNIONSTORE命令。

可用版本:

>= 2.0.0

时间复杂度:

O(N*K)+O(M*log(M)),

N为给定key中基数最小的有序集,K为给定有序集的数量,M为结果集的基数。

返回值:

保存到destination的结果集的基数。

参数详情:

WEIGHTS

使用WEIGHTS选项,你可以为每个给定有序集分别指定一个乘法因子(multiplication factor),每个给定有序集的所有成员的score值在传递给聚合函数(aggregation function)之前都要先乘以该有序集的因子。

如果没有指定WEIGHTS选项,乘法因子默认设置为1

AGGREGATE+

使用AGGREGATE选项,你可以指定并集的结果集的聚合方式。

默认使用的参数SUM,可以将所有集合中某个成员的score值之作为结果集中该成员的score值;使用参数MIN,可以将所有集合中某个成员的最小score值作为结果集中该成员的score值;而参数MAX则是将所有集合中某个成员的最大score值作为结果集中该成员的score值。

Redis Key管理

重命名键

rename [key] [newkey]
renamenx [key] [newkey]

两者区别在于前者不检查,若newkey存在则直接覆盖,后者会检查是否有newkey,若有则返回0代表没有成功重命名。另外在重命名期间会执行del命令删除旧的键,如果该键的值较大,可能会阻塞redis。还有则是rename和renamenx中的key和newkey若相同,在redis3.2和之前的版本中返回的结果会有不同,在redis3.2版本中会返回ok,在之前的版本会提示错误。

键过期

EXPIRE key seconds

为给定key设置生存时间,当key过期时(生存时间为0),它会被自动删除。

在 Redis 中,带有生存时间的key被称为『易失的』(volatile)。

生存时间可以通过使用DEL命令来删除整个key来移除,或者被SETGETSET命令覆写(overwrite),这意味着,如果一个命令只是修改(alter)一个带生存时间的key的值而不是用一个新的key值来代替(replace)它的话,那么生存时间不会被改变。

比如说,对一个key执行INCR命令,对一个列表进行LPUSH命令,或者对一个哈希表执行HSET命令,这类操作都不会修改key本身的生存时间。

另一方面,如果使用RENAME对一个key进行改名,那么改名后的key的生存时间和改名前一样。

RENAME命令的另一种可能是,尝试将一个带生存时间的key改名成另一个带生存时间的another_key,这时旧的another_key(以及它的生存时间)会被删除,然后旧的key会改名为another_key,因此,新的another_key的生存时间也和原本的key一样。

使用PERSIST命令可以在不删除key的情况下,移除key的生存时间,让key重新成为一个『持久的』(persistent)key

更新生存时间

可以对一个已经带有生存时间的key执行EXPIRE命令,新指定的生存时间会取代旧的生存时间。

过期时间的精确度

在 Redis 2.4 版本中,过期时间的延迟在 1 秒钟之内 —— 也即是,就算key已经过期,但它还是可能在过期之后一秒钟之内被访问到,而在新的 Redis 2.6 版本中,延迟被降低到 1 毫秒之内。

Redis 2.1.3 之前的不同之处

在 Redis 2.1.3 之前的版本中,修改一个带有生存时间的key会导致整个key被删除,这一行为是受当时复制(replication)层的限制而作出的,现在这一限制已经被修复。

可用版本:

>= 1.0.0

时间复杂度:

O(1)

返回值:

设置成功返回1。当key不存在或者不能为key设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新key的生存时间),返回0

EXPIREAT key timestamp

EXPIREAT的作用和EXPIRE类似,都用于为key设置生存时间。

不同在于EXPIREAT命令接受的时间参数是 UNIX 时间戳(unix timestamp)。+

可用版本:

>= 1.2.0

时间复杂度:

O(1)

返回值:

如果生存时间设置成功,返回1。当key不存在或没办法设置生存时间,返回0

PEXPIRE key milliseconds

这个命令和EXPIRE命令的作用类似,但是它以毫秒为单位设置key的生存时间,而不像EXPIRE命令那样,以秒为单位。

可用版本:

>= 2.6.0

时间复杂度:

O(1)

返回值:

设置成功,返回1 key不存在或设置失败,返回0

PEXPIREAT key milliseconds-timestamp

这个命令和EXPIREAT命令类似,但它以毫秒为单位设置key的过期 unix 时间戳,而不是像EXPIREAT那样,以秒为单位。

可用版本:

>= 2.6.0

时间复杂度:

O(1)

返回值:

如果生存时间设置成功,返回1。当key不存在或没办法设置生存时间时,返回0

Redis数据结构

Redis包含五种数据结构

String
Hash
List
Set
Zset

每种数据结构都有不同的内部编码实现,通常有以下几种实现。

String
-int
-embstr
-raw
Hash
-hashtable
-ziplist
List
-zipList
-LinkedList
-quicklist
Set
-hashtable
-intset
Zset
-ziplist
-skipset

Redis优势

redis优势主要可以分为以下三点:

  • 纯内存访问,速度更快

  • 非阻塞I/O,使用epoll进行I/O多路复用,避免网络I/O时间消耗过多

  • 单线程机制,避免线程切换和竞态消耗