Redis
不是一个简单键值对存储器,而是一个数据结构服务,它支持不一样类型的值。这意味着传统的键值对存储器将字符串键和字符串值关联起来,在Redis
中,值的类型不只仅局限于字符串,还能够是更加复杂的数据结构,下面是Redis
支持的数据结构,将会在各个章节接触到:redis
map
,键和值都是字符串。对于Ruby
和Python
很是的友好。知道这些数据类型和怎样使用对于解决命令索引给出的问题并不老是微不足道的。知道这些数据类型和怎样使用对于解决命令索引给出的问题是很重要的,因此这个文档将做为了解Redis
数据类型和他们基本模式的一个入门课程。数据库
对于全部的案例咱们将使用redis-cli
工具,这是一个很简单可是很便利的命令行工具,用来和Redis
服务端作交互。数组
Redis
的key
Redis
的key
是比特安全的,这意味着你可使用任何的二进制序列做为key
,从像foo
的字符串到一个JPEG
文件的内容。甚至空字符串也是能够的。缓存
关于key
有一些其余的规则:安全
key
是不推荐的。一个1024 bytes
是一个很是坏的注意,不只仅是由于内存浪费,更是由于在数据集中搜索对比的时候须要耗费更多的成本。当要处理的是匹配一个很是大的值,从内存和带宽的角度来看,使用这个值的hash
值是更好的办法(好比使用SHA1
)。key
一般也是不推荐的。在写像u100flw
这样的键的时候,有一个小小的要点,咱们能够用user:1000:followers
代替。可读性更好,对于key
对象和value
对象增长的空间占用与此相比来讲却是次要的。当短的key
能够很明显减小空间占用的时候,你的工做就是找到正确的平衡object-type:id
是一个好主意,-
和.
一般用于多个字符的域,就像comment:1234:reply.to
,或者comment:1234:reply-to
。key
容许512MB
Redis
字符串Redi
字符串类型是Redis
的key
能够关联的的最简单的数据类型。这是Mmcached
惟一的数据类型,因此对于Redis
的使用新手来讲,这是很是天然的。网络
由于Redis
的key
是字符串,当咱们使用字符串类型做为值的时候,咱们是将一个字符串映射到另外一个字符串。字符串类型在不少场景中是很是有用的,好比缓存HTML
片断或者页面。数据结构
接下来使用redis-cli
使用一下字符串类型(在这个文章中全部的示例都经过使用redis-cli
):app
> set mykey somevalue OK > get mykey "somevalue"
正如你看到的,使用SET
和GET
命令能够设置和获取一个字符串值。值得注意的是SET
将会覆盖key
已经存在的值,即便这个key
关联了一个不是字符串的值。因此SET
表现为一个任务。ide
值能够是任意类型的字符串(包括二进制数据),好比你能够存储jpeg
图片。一个值不能超过512MB
。工具
SET
命令有一些有趣的选项,做为而外的参数。好比。我可让SET
在key
已经存在的时候失败,或者相反,只有在key
存在的时候才成功:
> set mykey newval nx (nil) > set mykey newval xx OK
即便字符串是Redis
最基本的值,依旧有不少有趣的操做可使用。好比,原子增加:
> set counter 100 OK > incr counter (integer) 101 > incr counter (integer) 102 > incrby counter 50 (integer) 152
INCR
命令将字符串转化为integer
,自增1,而后保存成新的值,还有其余相似的命令,好比INCRBY
、DECR
、DECRBY
。在内部他们实际上是同样的命令,只是执行的时候有一点小差异而已。
INCR
是原子的意味着什么?这意味着即便多个客户端发送INCR
获取同一个key,将不会进入竞争状态,例如,客户端1获取到10
,同时客户端2也获取到10
是不可能的,所有获取的都是11
,而且将11
保存成新的值。最懂的值将会是12
,读取-自增-设置
三个操做将在其余客户端还没执行命令的时候同时完成。
有不少的命令能够操做字符串。好比GETSET
命令给一个key
设置一个新的个值,同时返回旧的值做为结果。好比,你的系统在你的网站有一个新的访客到来的时候,使用INCR
自增一个Redis
的key
,你可使用这个命令。你可能须要每小时收集全部的信息,甚至不错过每一次增加,你能够GETSET
一个key
,将它的值设置为0的同时获取新的值。
使用一个命令同时设置或者获取多个key
的能力是下降延迟的好方法。MSET
和MGET
命令能够作到:
> mset a 10 b 20 c 30 OK > mget a b c 1) "10" 2) "20" 3) "30"
当MGET
使用的时候,Redis
将会返回一个值。
key
空间有一些命令在部分类型中并无定义,可是和key
空间交互的时候是很是有用的,因此,可使用在任意类型的key
之上。
好比,当DEL
命令删了吃了一个key和他所关联的值的时候,EXISTS
命令返回1
或者0
去标记一个key
是否存在在数据库,无论这个key
关联的值是什么类型。
> set mykey hello OK > exists mykey (integer) 1 > del mykey (integer) 1 > exists mykey (integer) 0
从例子中能够看出,DEL
命令返回1
或者0
取决与key
是否被移除了(存在,或者没有这个名字的key
)。
From the examples you can also see how DEL itself returns 1 or 0 depending on whether the key was removed (it existed) or not (there was no such key with that name).
有很过key
空间相关的命令,上面的两个命令和TYPE
命令是最主要的,TYPE
命令返回的是存储在这个key
中的类型。
> set mykey x OK > type mykey string > del mykey (integer) 1 > type mykey none
Redis
期限:key
的生存时间在继续了解更多复杂的数据类型以前,咱们须要先讨论另外一个无视值类型的特性,咱们称之为Redis
生存时间。简单来讲你能够为一个key
设置一个过时时间,这个就是key
能够存在的时间。当能够存在的时间过了,这个key
就会自动销毁,就像用户使用DEL
命令删除了这个key。
关于Redis
期限的一些简单信息:
Redis
服务端中止的时候,时间也会过去(这意味着Redis
将会保存一个key
的过时日期)。设置一个过时时间是很简单的
> set key some-value OK > expire key 5 (integer) 1 > get key (immediately) "some-value" > get key (after some time) (nil)
在两次相隔5s的GET
调用中,key
彻底消失了。在上面的例子中,咱们用EXPIRE
去设置过时时间(固然也能够用来给一个已经存在过时时间的key
设置一个不一样的过时时间,好比PERSIST
能够用来移除过时时间,使这个key
永久持久化)。固然咱们也可使用其余Redis
命令建立一个有过时时间的key
。好比,使用SET
命令的选项:
> set key 100 ex 10 OK > ttl key (integer) 9
的绗棉这个栗子设置了一个值为100,过时时间为10秒的key
,接下来的TTL
命令用来检查这个key
剩下的生存时间。
为了用毫秒设置和检查生存时间,可使用PEXPIRE
和PTTL
命令,和完整的SET
命令选项。
Redis List
为了解释List
这种数据类型,最好先来点理论知识做为开胃菜,其实术语List
在信息技术领域的使用是常常是不恰单的。好比Python Lists
并不像名字所体现的(Linked Lists
),更像Arrays
(实际上相同的数据类型在Ruby
中称为Array
)。
从通常的观点看,一个List
只是一个由一系列有序元素组成的列表:10,20,1,2,3
。可是使用Array
实现的List
和用Linked List
实现的List
在特性上有很大的不一样。Redis List
是经过Linked List
实现的。这意味着即便你有百万个元素在列表内,添加一个元素的操做到头部或者尾部的操做的时间是一个常量。使用LPUSH
命令添加一个新元素到一个有10个元素的列表的头部所耗费的时间是和添加一个元素到一个有10000000万元素的列表的头部是同样的。
不利的一面是什么呢?使用Arrays
实现的List
经过索引访问一个元素是很是迅速的(常量时间),然而用Linked List
实现的则不会这么快(这个操做须要的时间是和要访问的索引成正比的)。
Redis List
使用Linked List
实现是由于对于数据库系统来讲,它须要可以经过很是快的方式添加元素到一个很是长的列表。接下来你将看到一个很是强的优点,那就是Redis Lists
能够采起常量长度在常量时间内。
当须要很是快的访问一个巨大聚合元素的其中一个数据时候,有另外一种数据结构可供选择,那就是Sorted Sets
,Sorted Set
将在下面的章节涉及。
Redis Lists
使用第一步LPUSH
命令添加一个新的元素到一个列表的左边(头部),RPUSH
命令添加一个新的元素到一个列表的右边(尾部)。LRAGE
命令从列表中提取元素。
> rpush mylist A (integer) 1 > rpush mylist B (integer) 2 > lpush mylist first (integer) 3 > lrange mylist 0 -1 1) "first" 2) "A" 3) "B"
注意:LRAGE
须要两个索引,要返回的第一个元素的索引和最后一个元素的索引。两个因此均可以被导航,告诉Redis
从开始统计到结束:因此,-1是列表的最后一个元素,-2是列表的倒数第一个元素,以此类推。
就像你看到的RPISH
添加元素到列表的右边,LPUSH
添加元素到列表的左边。
As you can see RPUSH appended the elements on the right of the list, while the final LPUSH appended the element on the left.
两个命令都是可变参数长度的命令,,这意味着你能够在一次执行中自由的推入多个元素到一个列表:
> rpush mylist 1 2 3 4 5 "foo bar" (integer) 9 > lrange mylist 0 -1 1) "first" 2) "A" 3) "B" 4) "1" 5) "2" 6) "3" 7) "4" 8) "5" 9) "foo bar"
定义在Redis Lists
中的一个重要操做是pop
的能力。弹出元素是从列表获取元素而且淘汰元素的操做。你能够从左边或则右边弹出元素,就像你能够从列表两边推入元素同样:
> rpush mylist a b c (integer) 3 > rpop mylist "c" > rpop mylist "b" > rpop mylist "a"
咱们推入了三个元素而且弹出了三个元素,因此执行完这一系列命令,这个列表最终变成空的,而且将再也不有数据弹出。若是咱们依旧尝试弹出其余元素,咱们将会获得以下结果:
> rpop mylist (nil) Redis returned a NULL value to signal that there are no elements in the list.
Redis Lists
的应用场景Lists
对一系列任务都颇有帮助,下面是两个典型应用场景:
Redis
有特殊的列表命令去保证这种用户场景更加可靠和有效。好比,Ruby
的库resque
和sidekiq
在底层使用Redis Lists
去实现后台任务。
流行的社交网络Twitter
将最新的Twitter
用户文章推入Redis Lists
。
为了一步一步归纳一个普通的用户场景,想一想你的主页显示了发布在一个照片分享社交网络的最新的照片,你想要很快的访问。
每次一个用户发布一张新的照片,咱们使用LPUSH
将照片的ID放入一个列表。当用户访问主页的时候,咱们使用LRANGE 0 9
去获取最新的10张照片。
List
在不少应用场景下,咱们只是想使用列表去存储最新的项目,好比:社交网络更新,日志,诸如此类。Redis
容许咱们像使用有限集合同样使用列表,只记住最新的N条数据并使用LTRIM
抛弃掉最旧的数据。
LTRIM
和LRANGE
很像,可是它不是现实指定的元素范围,而是设置指定的方位为新的列表值。全部不在这个范围以内的元素将被移除:
An example will make it more clear:
> rpush mylist 1 2 3 4 5 (integer) 5 > ltrim mylist 0 2 OK > lrange mylist 0 -1 1) "1" 2) "2" 3) "3"
TRIM
命令告诉Redis
只获取列中中索引0到2的元素,其余不在这个范围内的元素所有抛弃。这让一个简单可是有用的模式获得实现:向列表推入数据操做+修剪操做一块儿,实现了添加一个新元素并抛弃超出范围的元素:
LPUSH mylist <some element> LTRIM mylist 0 999
上面的命令结合起来实现了添加一个新的元素到列表并获取列表前1000条最新的元素。LRANGE
命令让你能够获取到定模的元素而且不准要记住每个旧的数据
注意:尽管LRANGE
命令技术上是一个O(N)
的命令,获取列表头部或者尾部很小范围的的数据依旧是一个常量时间操做。
List
会阻塞的操做列表有一个很特别的特性让它能够很适合用来实现队列,通常做为内部进程通讯系统的构建块:阻塞操做。
想一想你的一个进程想要将一个元素推入列表,另外一个进程想要对这些元素进行某些操做。这就是一般说的生产者/消费者模式,能够用下面的方式简单的实现:
LPUSH
向列表推入数据。RPOP
从列表消费数据。然而,有时列表有可能时空的,没有什么好执行的,因此RPOP
将会返回NULL
,这种状况下,消费者强制等待一些时间而后从新
Redis
和客户端去执行无效的命令(当列表是空的的时候,针对全部的请求其实没有作任何的工做,只是简单的返回NULL
)。NULL
后会等待一些时间。让延迟更小,咱们在能够在两个执行RPOP
命令之间等待更少的时间,可是会引发问题1,也就是更多的无效请求。因此Redis
实现了BRPOP
和BLPOP
命令,这个命苦可让RPOP
和LPOP
能够在列表为空的时候堵塞:他们将只在一个新的元素添加进列表的时候执行,或者当用户指定的超时时间到了。
这是一个关于咱们可使用的BRPOP
命令示例:
> brpop tasks 5 1) "tasks" 2) "do_something"
这意味着:等待列表中的元素,可是若是5s以后没有元素就返回。
值得注意的是,你能够设置超时时间为0,从而让线程永远等待,固然你也能够指定多个列表,而不是一个,同一时间等待多个列表,将会收到第一个收到新元素列表的通知。
一些关于BRPOP
的笔记:
RPOP
不同:是一个包含两个元素的数组,他包含了key
的名字,由于BRPOP
和BLPOP
能够作到堵塞等待多个列表的元素。NULL
。关于列表和堵塞操做还有更多的星系你须要知道。咱们推荐你能够阅读下面的更多内容:
RPOPLPUSH
命令能够构建一个更安全的队列或者旋转队列。BRPOPLPUSH
命令是RPOPLPUSH
命令堵塞的变形。