主要参考资料:http://wiki.jikexueyuan.com/project/redis-guide/data-type.html
1、redis 安装
一、在官网下载安装包
二、解压安装包
tar -zvxf redis-3.2.8.tar.gz
三、进行编译
进入目录
cd redis-3.2.8;
进行编译
make
四、安装redis
进入src目录
cd src/
编译安装
make install
五、后台运行运、
./redis-server ../redis.conf
数据类型
一、字符创
Redis 字符串是二进制安全的,也就是说,一个 Redis 字符串能够包含任意类型的数据,一个字符创最大512M长度。
Redis 的字符串类型作很事情:
使用 INCR 命令族 (INCR,DECR,INCRBY),将字符串做为原子计数器。
使用 APPEND 命令追加字符串。
使用 GETRANGE 和 SETRANGE 命令,使字符串做为随机访问向量 (vectors)。
编码大量数据到很小的空间,或者使用 GETBIT 和 SETBIT 命令,建立一个基于 Redis 的布隆 (Bloom) 过滤器。
二、列表 (Lists)
Redis 列表仅仅是按照插入顺序排序的字符串列表。能够添加一个元素到 Redis 列表的头部 (左边) 或者尾部 (右边)。
LPUSH 命令用于插入一个元素到列表的头部,RPUSH 命令用于插入一个元素到列表的尾部
当这两个命令操做在一个不存在的键时,将会建立一个新的列表
例子:
lpush mylist test
lpush mylist test2
rpush mylist test3
列表的最大长度是 223-1 个元素 (4294967295,超过 40 亿个元素)。
列表用处
为社交网络时间轴 (timeline) 建模,使用 LPUSH 命令往用户时间轴插入元素,使用 LRANGE 命令得到最近事项。
使用 LPUSH 和 LTRIM 命令建立一个不会超出给定数量元素的列表,只存储最近的 N 个元素。
列表能够用做消息传递原语,例如,众所周知的用于建立后台任务的 Ruby 库 Resque。
你能够用列表作更多的事情,这种数据类型支持不少的命令,包括阻塞命令,如 BLPOP。
三、集合 (Sets)
Redis 集合是没有顺序的字符串集合 (collection)。能够在 O(1) 的时间复杂度添加、删除和测试元素存在与否 。
Redis 集合具备你须要的不容许重复成员的性质。屡次加入同一个元素到集合也只会有一个拷贝在其中。实际上,这意味着加入一个元素到集合中并不须要检查元素是否已存在
集合能够在很短的时间内和已经存在的集合一块儿计算并集,交集和差集。
用处:
你可使用 Redis 集合追踪惟一性的事情。你想知道访问某篇博客文章的全部惟一 IP 吗?只要 每次页面访问时使用 SADD 命令就能够了。你能够放心,重复的 IP 是不会被插入进来的。
Redis 集合能够表示关系。你能够经过使用集合来表示每一个标签,来建立一个标签系统。而后你能够把全部拥有此标签的对象的 ID 经过 SADD 命令,加入到表示这个标签的集合中。你想得到同时拥有三个不一样标签的对象的所有 ID 吗?用 SINTER 就能够了。
你可使用 SPOP 或 SRANDMEMBER 命令来从集合中随机抽取元素
四、哈希 / 散列 (Hashes)
Redis 哈希是字符串字段 (field) 与字符串值之间的映射,因此是表示对象的理想数据类型 (例如:一个用户对象有多个字段,像用户名,姓氏,年龄等等
例子:
HMSET user:1000 username antirez password P1pp0 age 34
HGETALL user:1000
HSET user:1000 password 12345
HGETALL user:1000
拥有少许字段 (少许指的是大约 100) 的哈希会以占用不多存储空间的方式存储,因此你能够在一个很小的 Redis 实例里存储数百万的对象。
因为哈希主要用来表示对象,对象能存储不少元素,因此你能够用哈希来作不少其余的事情。
每一个哈希能够存储多达 223-1 个字段值对 (field-value pair)(多于 40 亿个)
五、有序集合 (Sorted sets)
Redis 有序集合和 Redis 集合相似,是非重复字符串集合 (collection)。不一样的是,每个有序集合的成员都有一个关联的分数 (score),用于按照分数高低排序。尽管成员是惟一的,可是分数是能够重复的
对有序集合咱们能够经过很快速的方式添加,删除和更新元素 (在和元素数量的对数成正比的时间内)。因为元素是有序的而无需过后排序,你能够经过分数或者排名 (位置) 很快地来获取一个范围内的元素。访问有序集合的中间元素也是很快的,因此你可使用有序集合做为一个无重复元素,快速访问你想要的一切的聪明列表:有序的元素,快速的存在性测试,快速的访问中间元素!
总之,有序集合能够在很好的性能下,作不少别的数据库没法模拟的事情。
使用有序集合你能够:
例如多人在线游戏排行榜,每次提交一个新的分数,你就使用 ZADD 命令更新。你能够很容易地使用 ZRANGE 命令获取前几名用户,你也能够用 ZRANK 命令,经过给定用户名返回其排行。同时使用 ZRANK 和 ZRANGE 命令能够展现与给定用户类似的用户及其分数。以上这些操做都很是的快。
有序集合经常使用来索引存储在 Redis 内的数据。例如,假设你有不少表示用户的哈希,你可使用有序集合,用年龄做为元素的分数,用用户 ID 做为元素值,因而你就可使用 ZRANGEBYSCORE 命令很快且垂手可得地检索出给定年龄区间的全部用户了。
有序集合或许是最高级的 Redis 数据类型,后续咱们会详细介绍可用的有序集合命令,也会详细介绍 Redis 数据类型的更多高级信息。
六、位图 (Bitmaps) 和超重对数 (HyperLogLogs)
Redis 还支持位图和超重对数这两种基于字符串基本类型,但有本身语义的数据类型。
3、详细讲解(上)
Redis 不是一个无格式 (plain) 的键值存储,而是一个支持各类不一样类型值的数据结构服务器。这就是说,传统键值存储是关联字符串值到字符串键,可是 Redis 的值不只仅局限于简单字符串,还能够持有更复杂的数据结构。下面列的是 Redis 支持的全部数据结构
二进制安全 (binary-safe) 的字符串。
列表:按照插入顺序排序的字符串元素 (element) 的集合 (collection)。一般是链表。
集合:惟一的,无序的字符串元素集合。
有序集合:和集合相似,可是每一个字符串元素关联了一个称为分数 (score) 的浮点数。元素老是按照分数排序,因此能够检索一个范围的元素 (例如,给我前 10,或者后 10 个元素)。
哈希:由字段 (field) 及其关联的值组成的映射。字段和值都是字符串类型。这很是相似于 Ruby 或 Python 中的哈希 / 散列。
位数组 (位图):使用特殊的命令,把字符串当作位数组来处理:你能够设置或者清除单个位值,统计所有置位为 1 的位个数,寻找第一个复位或者置位的位,等等。
超重对数 (HyperLogLog):这是一个用于估算集合的基数 (cardinality,也称势,译者注) 的几率性数据结构。不要惧怕,它比看起来要简单,稍后为你揭晓。
后面的全部例子咱们都是使用 redis-cli 工具,这是一个简单而又方便的命令行工具,用于发送命令给 Redis 服务器
一、Redis 键 (Keys)
Redis 键是二进制安全的,这意味着你可使用任何二进制序列做为键,从像”foo” 这样的字符串到一个 JPEG 文件的内容。空字符串也是合法的键。
关于键的其余一些规则:
不要使用太长的键,例如,不要使用一个 1024 字节的键,不只是由于内存占用,并且在数据集中查找键时须要屡次耗时的键比较。即便手头须要匹配一个很大值的存在性,对其进行哈希 (例如使用 SHA1) 是个不错的主意,尤为是从内存和带宽的角度。
不要使用过短的键。用”u1000flw” 取代”user:1000:followers” 做为键并无什么实际意义,后者更具备可读性,相对于键对象自己以及值对象来讲,增长的空间微乎其微。然而不能否认,短的键会消耗少的内存,你的任务就是要找到平衡点
坚持一种模式 (schema)。例如,”object-type:id” 就不错,就像”user:1000”。点或者横线经常使用来链接多单词字段,如”comment:1234:reply.to”,或者”comment:1234:reply-to”。
键的最大大小是 512MB
二、Redis 字符串 (Strings)
Redis 字符串是能够关联给 redis 键的最简单值类型。字符串是 Memcached 的惟一数据类型,因此新手使用起来也是很天然的。
因为 Redis 的键也是字符串,当咱们使用字符串做为值的时候,咱们是将一个字符串映射给另外一个字符串。字符串数据类型适用于不少场景,例如,缓存 HTML 片断或者页面。
让咱们用 redis-cli 来玩玩字符串类型 (接下来的例子都是使用 redis-cli)。
> set mykey somevalue
OK
> get mykey
"somevalue"
你能够看到,咱们使用 SET 和 GET 命令设置和检索字符串值。注意,若是键已经存在,SET 会替换掉该键已经存在的值,哪怕这个键关联的是一个非字符串类型的值。SET 执行的是赋值操做。
值能够是任何类型的字符串 ,例如,你能够存储一个 JPEG 图像。值不能大于 512MB。
SET 命令还有一些以额外的参数形式提供有意思的选项。例如,若是我要求若是键存在 (或恰好相反) 则执行失败,也就是说健不存在才成功:
#若是不存在则能够插入
> set mykey newval nx
(nil)
#若是存在则能够插入
> set mykey newval xx
OK
尽管字符串是 Redis 最基本的值类型,你仍能够执行不少有趣的操做。例如,原子性增加:
> set counter 100 设置counter只为100
OK
> incr counter 增长1
(integer) 101
> incr counter 增长1
(integer) 102
> incrby counter 50 增长50
(integer) 152
INCR 命令将字符串值解析为整数,并增长一,最后赋值后做为新值。还有一些相似的命令 INCRBY,DECR 和 DECRBY。它们以略微不一样的方式执行,但其内部都是同样的命令。
为何说 INCR 命令是原子的?由于即便多个 客户端对同一个键发送 INCR 命令也不会形成竞争条件 (race condition)。例如,必定不会发生客户端 1 和客户端 2 同时读到”10”,都增长到 11,而后设置新值为 11。最后的结果将会一直是 12,读 - 增长 - 写操做在执行时,其余客户端此时不会执行相关命令。
有许多操做字符串的命令。例如,GETSET 命令给键设置一个新值,同时返回旧值。你可使用这个命令,例如,若是你有一个系统,每当收到一个访问请求就使用 INRC 来增长一个键。你想每隔一个小时收集一次这个信息,而不想漏掉任何一个增加。你可使用 GETSET,将新值赋值为 0,以后读取其旧值。
在一个命令中一次设置或者检索多个键有利于减小延迟。为此有了 MSET 和 MGET 命令:
> mset a 10 b 20 c 30
OK
> mget a b c
1) "10"
2) "20"
3) "30"
当使用 MSET 时,Redis 返回一个值数组。
改变和查询键空间 (key space):
有一些命令并不定义在特定的类型上,可是对键空间的交互颇有用,所以他们能做用在任意键上。
例如,EXISTS 命令返回 1 或者 0,来表示键在数据库中是否存在。另外,DEL 命令删除键及其关联的值,不管值是什么。
> set mykey hello
OK
> exists mykey
(integer) 1
> del mykey
(integer) 1
> exists mykey
(integer) 0
从上面的例子中咱们还能够看到,DEL 命令自己也会返回 1 或者 0,不管键是(存在)否(不存在)删除。
有许多键空间相关的命令,可是上面两个命令与 TYPE 命令关系紧密,TYPE 命令返回某个键的值的类型
> set mykey x
OK
> type mykey
string
> del mykey
(integer) 1
> type mykey
none
Redis 过时 (expires):有限生存时间的键
在咱们继续更复杂的数据结构以前,咱们先抛出一个与类型无关的特性, 称为 Redis 过时 。你能够给键设置超时,也就是一个有限的生存时间。当生存时间到了,键就会自动被销毁,就像用户调用 DEL 命令同样。
快速过一下 Redis 过时的信息:
过时时间能够设置为秒或者毫秒精度。
过时时间分辨率老是 1 毫秒。
过时信息被复制和持久化到磁盘,当 Redis 中止时时间仍然在计算 (也就是说 Redis 保存了过时时间)。
设置过时很是简单:
> set key some-value
OK
> expire key 5
(integer) 1
> get key (immediately)
"some-value"
> get key (after some time) 5秒后执行
(nil)
键在两次 GET 调用期间消失了,由于第二次调用推迟了超过 5 秒。在上面的例子中,咱们使用 EXPIRE 命令设置过时 (也能够为一个已经设置过时时间的键设置不一样的过时时间,就像 PERSIST 命令能够删除过时时间使键永远存在)。固然咱们也可使用其余 Redis 命令来建立带过时时间的键。例如使用 SET 选项:
> set key 100 ex 10
OK
> ttl key
(integer) 9
上面的例子中设置 10 秒过时的键,值为字符串 100。而后使用 TTL 命令检查键的生存剩余时间。
为了使用毫秒来设置和检查过时,请查看 PEXPIRE 和 PTTL 命令,以及 SET 命令的所有选项。
4、详细讲解(中)
Redis 列表(Lists)
Redis 的列表是使用链表实现的。这意味着,及时你的列表中有上百万个元素,增长一个元素到列表的头部或者尾部的操做都是在常量时间完成。使用 LPUSH 命令增长一个新元素到拥有 10 个元素的列表的头部的速度,与增长到拥有 1000 万个元素的列表的头部是同样的。
缺点又是什么呢?使用索引下标来访问一个数组实现的列表很是快(常量时间),可是访问链表实现列表就没那么快了(与元素索引下标成正比的大量工做)。
Redis 采用链表来实现列表是由于,对于数据库系统来讲,快速插入一个元素到一个很长的列表很是重要。另一个即将描述的优点是,Redis 列表能在常数时间内得到常数长度。
LPUSH 命令从左边 (头部) 添加一个元素到列表,RPUSH 命令从右边(尾部)添加一个元素的列表。LRANGE 命令从列表中提取一个范围内的元素。
以下:
> 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"
注意 LRANGE 命令使用两个索引下标,分别是返回的范围的开始和结束元素。两个索引坐标能够是负数,表示从后往前数,因此 - 1 表示最后一个元素,-2 表示倒数第二个元素,等等。
如你所见,RPUSH 添加元素到列表的右边,LPUSH 添加元素到列表的左边。
两个命令都是可变参数命令,也就是说,你能够在一个命令调用中自由的添加多个元素到列表中:
> 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 列表上的一个重要操做是弹出元素。弹出元素指的是从列表中检索元素,并同时将其从列表中清楚的操做。你能够从左边或者右边弹出元素,相似于你能够从列表的两端添加元素
> rpush mylist a b c
(integer) 3
> rpop mylist
"c"
> rpop mylist
"b"
> rpop mylist
"a"
咱们添加了三个元素而且又弹出了三个元素,因此这一串命令执行完之后列表是空的,没有元素能够弹出了。若是咱们试图再弹出一个元素,就会获得以下结果:
> rpop mylist
(nil)
Redis 返回一个 NULL 值来代表列表中没有元素了。
列表的通用场景(Common use cases)
列表能够完成不少任务,两个有表明性的场景以下:
记住社交网络中用户最近提交的更新。
使用生产者消费者模式来进程间通讯,生产者添加项(item)到列表,消费者(一般是 worker)消费项并执行任务。Redis 有专门的列表命令更加可靠和高效的解决这种问题。
例如,两种流行的 Ruby 库 resque 和 sidekiq,都是使用 Redis 列表做为钩子,来实现后台做业 (background jobs)。
流行的 Twitter 社交网络,使用 Redis 列表来存储用户最新的微博 (tweets)。
为了一步一步的描述通用场景,假设你想加速展示照片共享社交网络主页的最近发布的图片列表。
每次用户提交一张新的照片,咱们使用 LPUSH 将其 ID 添加到列表。
当用户访问主页时,咱们使用 LRANGE 0 9 获取最新的 10 张照片。
上限列表(Capped)
不少时候咱们只是想用列表存储最近的项,随便这些项是什么:社交网络更新,日志或者任何其余东西。
Redis 容许使用列表做为一个上限集合,使用 LTRIM 命令仅仅只记住最新的 N 项,丢弃掉全部老的项。
LTRIM 命令相似于 LRANGE,可是不一样于展现指定范围的元素,而是将其做为列表新值存储。全部范围外的元素都将被删除。
举个例子你就更清楚了:
> rpush mylist 1 2 3 4 5
(integer) 5
> ltrim mylist 0 2
OK
> lrange mylist 0 -1
1) "1"
2) "2"
3) "3"
上面 LTRIM 命令告诉 Redis 仅仅保存第 0 到 2 个元素,其余的都被抛弃。这可让你实现一个简单而又有用的模式,一个添加操做和一个修剪操做一块儿,实现新增一个元素抛弃超出元素。
LPUSH mylist <some element>
LTRIM mylist 0 999
上面的组合增长一个元素到列表中,同时只持有最新的 1000 个元素。使用 LRANGE 命令你能够访问前几个元素而不用记录很是老的数据。
注意:尽管 LRANGE 是一个O(N)时间复杂度的命令,访问列表头尾附近的小范围是常量时间的操做。
列表的阻塞操做 (blocking)
列表有一个特别的特性使得其适合实现队列,一般做为进程间通讯系统的积木:阻塞操做。
假设你想往一个进程的列表中添加项,用另外一个进程来处理这些项。这就是一般的生产者消费者模式,可使用如下简单方式实现:
生产者调用 LPUSH 添加项到列表中。
消费者调用 RPOP 从列表提取 / 处理项。
然而有时候列表是空的,没有须要处理的,RPOP 就返回 NULL。因此消费者被强制等待一段时间并重试 RPOP 命令。这称为轮询(polling),因为其具备一些缺点,因此不合适在这种状况下
强制 Redis 和客户端处理无用的命令 (当列表为空时的全部请求都没有执行实际的工做,只会返回 NULL)。
因为工做者受到一个 NULL 后会等待一段时间,这会延迟对项的处理。
因而 Redis 实现了 BRPOP 和 BLPOP 两个命令,它们是当列表为空时 RPOP 和 LPOP 的会阻塞版本:仅当一个新元素被添加到列表时,或者到达了用户的指定超时时间,才返回给调用者。 这个是咱们在工做者中调用 BRPOP 的例子:
> brpop tasks 5
1) "tasks"
2) "do_something"
上面的意思是” 等待 tasks 列表中的元素,若是 5 秒后尚未可用元素就返回”。
注意,你可使用 0 做为超时让其一直等待元素,你也能够指定多个列表而不只仅只是一个,同时等待多个列表,当第一个列表收到元素后就能获得通知。
关于 BRPOP 的一些注意事项。
客户端按顺序服务:第一个被阻塞等待列表的客户端,将第一个收到其余客户端添加的元素,等等。
与 RPOP 的返回值不一样:返回的是一个数组,其中包括键的名字,由于 BRPOP 和 BLPOP 能够阻塞等待多个列表的元素。
若是超时时间到达,返回 NULL。
还有更多你须要知道的关于列表和阻塞选项,建议你阅读下面的页面:
使用 RPOLPUSH 构建更安全的队列和旋转队列。
BRPOPLPUSH 命令是其阻塞变种命令。
自动建立和删除键
到目前为止的例子中,咱们尚未在添加元素前建立一个空的列表,也没有删除一个没有元素的空列表。要注意,当列表为空时 Redis 将删除该键,当向一个不存在的列表键(如使用 LPUSH)添加一个元素时,将建立一个空的列表。
这并不仅是针对列表,适用于全部 Redis 多元素组成的数据类型,所以适用于集合,有序集合和哈希。
基本上咱们能够归纳为三条规则:
当咱们向聚合(aggregate)数据类型添加一个元素,若是目标键不存在,添加元素前将建立一个空的聚合数据类型。
当咱们从聚合数据类型删除一个元素,若是值为空,则键也会被销毁。
调用一个像 LLEN 的只读命令(返回列表的长度),或者一个写命令从空键删除元素,老是产生和操做一个持有空聚合类型值的键同样的结果。
规则 1 的例子:
> del mylist
(integer) 1
> lpush mylist 1 2 3
(integer) 3
然而,咱们不能执行一个错误键类型的操做:
> set foo bar
OK
> lpush foo 1 2 3
(error) WRONGTYPE Operation against a key holding the wrong kind of value
> type foo
string
规则 2 的例子:
> lpush mylist 1 2 3
(integer) 3
> exists mylist
(integer) 1
> lpop mylist
"3"
> lpop mylist
"2"
> lpop mylist
"1"
> exists mylist
(integer) 0
当全部元素弹出后,键就不存在了。
规则 3 的例子:
> del mylist
(integer) 0
> llen mylist
(integer) 0
> lpop mylist
(nil)
Redis 哈希/散列 (Hashes)
> hmset user:1000 username antirez birthyear 1977 verified 1
OK
> hget user:1000 username
"antirez"
> hget user:1000 birthyear
"1977"
> hgetall user:1000
1) "username"
2) "antirez"
3) "birthyear"
4) "1977"
5) "verified"
6) "1"
哈希就是字段值对(fields-values pairs)的集合。因为哈希容易表示对象,事实上哈希中的字段的数量并无限制,因此你能够在你的应用程序以不一样的方式来使用哈希。
HMSET 命令为哈希设置多个字段,HGET 检索一个单独的字段。HMGET 相似于 HGET,可是返回值的数组:
> hmget user:1000 username birthyear no-such-field
1) "antirez"
2) "1977"
3) (nil)
也有一些命令能够针对单个字段执行操做,例如 HINCRBY:
> hincrby user:1000 birthyear 10
(integer) 1987
> hincrby user:1000 birthyear 10
(integer) 1997
你能够从命令页找到所有哈希命令列表。
值得注意的是,小的哈希 (少许元素,不太大的值) 在内存中以一种特殊的方式编码以高效利用内存
Redis 集合 (Sets)
Redis 集合是无序的字符串集合 (collections)。SADD 命令添加元素到集合。还能够对集合执行不少其余的操做,例如,测试元素是否存在,对多个集合执行交集、并集和差集,等等。
> sadd myset 1 2 3
(integer) 3
> smembers myset
1. 3
2. 1
3. 2
咱们向集合总添加了 3 个元素,而后告诉 Redis 返回全部元素。如你所见,他们没有排序,Redis 在每次调用时按随意顺序返回元素,由于没有与用户有任何元素排序协议。
咱们有测试成员关系的命令。一个指定的元素存在吗?
> sismember myset 3
(integer) 1
> sismember myset 30
(integer) 0
“3” 是集合中的成员,”30” 则不是。
假设,咱们想标记新闻。若是咱们的 ID 为 1000 的新闻,被标签 1,2,5 和 77 标记,咱们能够有一个这篇新闻被关联标记 ID 的集合:
> sadd news:1000:tags 1 2 5 77
(integer) 4
然而有时候咱们也想要一些反向的关系:被某个标签标记的全部文章:
> sadd tag:1:news 1000
(integer) 1
> sadd tag:2:news 1000
(integer) 1
> sadd tag:5:news 1000
(integer) 1
> sadd tag:77:news 1000
(integer) 1
获取指定对象的标签很简单:
> smembers news:1000:tags
1. 5
2. 1
3. 77
4. 2
例如,咱们想获取全部被标签 1,2,10 和 27 同时标记的对象列表。咱们可使用 SINTER 命令实现这个,也就是对不一样的集合执行交集。咱们只须要:
> sinter tag:1:news tag:2:news
1) "1000"
并不只仅是交集操做,你也能够执行并集,差集,随机抽取元素操做等等。
抽取一个元素的命令是 SPOP,就方便为不少问题建模。例如,为了实现一个基于 web 的扑克游戏,你能够将你的一副牌表示为集合。假设咱们使用一个字符前缀表示(C)lubs 梅花, (D)iamonds 方块,(H)earts 红心,(S)pades 黑桃。
> sadd deck C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK
D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK H1 H2 H3
H4 H5 H6 H7 H8 H9 H10 HJ HQ HK S1 S2 S3 S4 S5 S6
S7 S8 S9 S10 SJ SQ SK
如今咱们为每位选手提供 5 张牌。SPOP 命令删除一个随机元素,返回给客户端,是这个场景下的最佳操做。
然而,若是咱们直接对这副牌调用,下一局咱们须要再填充一副牌,这个可能不太理想。因此咱们一开始要复制一下 deck 键的集合到 game:1:deck 键。
这是经过使用 SUNIONSTORE 命令完成的,这个命令一般对多个集合执行交集,而后把结果存储在另外一个集合中。而对单个集合求交集就是其自身,因而我能够这样拷贝个人这副牌:
> sunionstore game:1:deck deck
(integer) 52
如今咱们准备好为第一个选手提供 5 张牌:
> spop game:1:deck
"C6"
> spop game:1:deck
"CQ"
> spop game:1:deck
"D1"
> spop game:1:deck
"CJ"
> spop game:1:deck
"SJ"
只有一对 jack,不太理想……
如今是时候介绍提供集合中元素数量的命令。这个在集合理论中称为集合的基数(cardinality,也称集合的势),因此相应的 Redis 命令称为 SCARD。
> scard game:1:deck
(integer) 47
数学计算式为:52 - 5 = 47。
当你只须要得到随机元素而不须要从集合中删除,SRANDMEMBER 命令则适合你完成任务。它具备返回重复的和非重复的元素的能力
5、详细讲解(下)
Redis 有序集合 (Sorted sets)
有序集合相似于集合和哈希的混合体的一种数据类型。像集合同样,有序集合由惟一的,不重复的字符串元素组成,在某种意义上,有序集合也就是集合。
集合中的每一个元素是无序的,但有序集合中的每一个元素都关联了一个浮点值,称为分数(score,这就是为何该类型也相似于哈希,由于每个元素都映射到一个值)。
此外,有序集合中的元素是按序存储的(不是请求时才排序的,顺序是依赖于表示有序集合的数据结构)。他们按照以下规则排序:
若是 A 和 B 是拥有不一样分数的元素,A.score > B.score,则 A > B。
若是 A 和 B 是有相同的分数的元素,若是按字典顺序 A 大于 B,则 A > B。A 和 B 不能相同,由于排序集合只能有惟一元素。
让咱们开始一个简单的例子,添加一些黑客的名字做为有序集合的元素,以他们的出生年份为分数。
> zadd hackers 1940 "Alan Kay"
(integer) 1
> zadd hackers 1957 "Sophie Wilson"
(integer 1)
> zadd hackers 1953 "Richard Stallman"
(integer) 1
> zadd hackers 1949 "Anita Borg"
(integer) 1
> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
> zadd hackers 1916 "Claude Shannon"
(integer) 1
> zadd hackers 1969 "Linus Torvalds"
(integer) 1
> zadd hackers 1912 "Alan Turing"
(integer) 1
如你所见,ZADD 命令相似于 SADD,可是多一个参数(位于添加的元素以前),即分数。ZADD 命令也是可变参数的,因此你能够自由的指定多个分数值对(score-value pairs),尽管上面的例子中并无使用。
使用排序集合能够很容易返回按照出生年份排序的黑客列表,由于他们已是排序好的。 实现注意事项:有序集合是经过双端(dual-ported)数据结构实现的,包括跳跃表(skiplist,后续文章会详细介绍,译者注)和哈希表(hashtable),因此咱们每次添加元素时 Redis 执行 O(log(N)) 的操做。这还好,可是当咱们请求有序元素时,Redis 根本不须要作什么工做,由于已是所有有序了:
> zrange hackers 0 -1
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
6) "Richard Stallman"
7) "Sophie Wilson"
8) "Yukihiro Matsumoto"
9) "Linus Torvalds"
注意:0 和 - 1 表示从索引为 0 的元素到最后一个元素(-1 像 LRANGE 命令中同样工做)。
若是我想按照相反的顺序排序,从最年轻到最年长?使用 ZREVRANGE 代替 ZRANGE:
> zrevrange hackers 0 -1
1) "Linus Torvalds"
2) "Yukihiro Matsumoto"
3) "Sophie Wilson"
4) "Richard Stallman"
5) "Anita Borg"
6) "Alan Kay"
7) "Claude Shannon"
8) "Hedy Lamarr"
9) "Alan Turing"
也能够同时返回分数,使用 WITHSCORES 参数:
> zrange hackers 0 -1 withscores
1) "Alan Turing"
2) "1912"
3) "Hedy Lamarr"
4) "1914"
5) "Claude Shannon"
6) "1916"
7) "Alan Kay"
8) "1940"
9) "Anita Borg"
10) "1949"
11) "Richard Stallman"
12) "1953"
13) "Sophie Wilson"
14) "1957"
15) "Yukihiro Matsumoto"
16) "1965"
17) "Linus Torvalds"
18) "1969"
范围操做 (ranges)
有序集合远比这些要强大。他们能够在范围上操做。让咱们获取 1950 年前出生的全部人。咱们使用 ZRANGEBYSCORE 命令来办到:
> zrangebyscore hackers -inf 1950
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
咱们要求 Redis 返回分数在负无穷到 1950 之间的全部元素(包括两个极端)。
也能够删除某个范围的元素。让咱们从有序集合中删除出生于 1940 年到 1960 年之间的黑客:
> zremrangebyscore hackers 1940 1960
(integer) 4
ZREMRANGEBYSCORE 也许不是最合适的命令名,可是很是有用,返回删除的元素数目。
另外一个很是有用的操做是用来获取有序集合中元素排行的操做。也就是能够询问集合中元素的排序位置。
> zrank hackers "Anita Borg"
(integer) 4
ZREVRANK 命令用来按照降序排序返回元素的排行。
字典分数 (Lexicographical scores)
最近的 Redis2.8 版本引入了一个新的特性,假定集合中的元素都具备相同的分数,容许按字典顺序获取范围(元素按照 C 语言中的 memcmp 函数进行比较,所以能够保证没有整理,每一个 Redis 实例会有相同的输出)。
操做字典顺序范围的主要命令是 ZRANGEBYLEX,ZREVRANGEBYLEX,ZREMRANGEBYLEX 和 ZLEXCOUNT。例如,咱们再次添加咱们的著名黑客清单。可是此次为每一个元素使用 0 分数:
> zadd hackers 0 "Alan Kay" 0 "Sophie Wilson" 0 "Richard Stallman" 0 "Anita Borg" 0 "Yukihiro Matsumoto" 0 "Hedy Lamarr" 0 "Claude Shannon" 0 "Linus Torvalds" 0 "Alan Turing"
根据有序集合的排序规则,他们已经按照字典顺序排好了:
> zrange hackers 0 -1
1) "Alan Kay"
2) "Alan Turing"
3) "Anita Borg"
4) "Claude Shannon"
5) "Hedy Lamarr"
6) "Linus Torvalds"
7) "Richard Stallman"
8) "Sophie Wilson"
9) "Yukihiro Matsumoto"
使用 ZRANGEBYLEX 咱们能够查询字典顺序范围:
> zrangebylex hackers [B [P
1) "Claude Shannon"
2) "Hedy Lamarr"
3) "Linus Torvalds"
范围能够是包容性的或者排除性的(取决于第一个字符,即开闭区间,译者注),+ 和 - 分别表示正无穷和负无穷。查看该命令的文档获取更详细信息(该文档后续即奉献,译者注)。
这个特性很是重要,由于这容许有序集合做为通用索引。例如,若是你想用一个 128 位无符号整数来索引元素,你须要作的就是使用相同的分数(例如 0)添加元素到有序集合中,元素加上由 128 位大端(big endian)数字组成的 8 字节前缀。因为数字是大端编码,字典顺序排序(原始 raw 字节顺序)其实就是数字顺序,你能够在 128 位空间查询范围,获取元素后抛弃前缀。若是你想在一个更正式的例子中了解这个特性,能够看看 Redis 自动完成范例(后续献上,译者注)。
更新分数:排行榜 (leader boards)
这一部分是开始新的主题前最后一个关于有序集合的内容。有序集合的分数能够随时更新。对一个存在于有序集合中的元素再次调用 ZADD,将会在 O(log(N))时间复杂度更新他的分数 (和位置),因此有序集合适合于常常更新的场合。
因为这个特性,一般的一个使用场景就是排行榜。最典型的应用就是 facebook 游戏,你能够组合使用按分数高低存储用户,以及获取排名的操做,来展现前 N 名的用户以及用户在排行榜上的排行(你是第 4932 名最佳分数)。
html