Redis数据类型简介(十分钟快速学习Redis)

  1. 如何在ubuntu18.04上安装和保护redis
  2. 如何链接到Redis数据库
  3. 如何管理Redis数据库和Keys
  4. 如何在Redis中管理副本和客户端
  5. 如何在Redis中管理字符串
  6. 如何在Redis中管理list
  7. 如何在Redis中管理Hashes
  8. 如何在Redis中管理Sets
  9. 如何在Redis中管理Sorted Sets
  10. 如何在Redis中运行事务
  11. 如何使Redis中的Key失效
  12. 如何解决Redis中的问题
  13. 如何从命令行更改Redis的配置
  14. Redis数据类型简介

Redis数据类型简介

Redis不是_简单的_键值存储,它其实是一个_数据结构服务器_,支持不一样类型的值。这意味着在传统键值存储中,您将字符串键与字符串值相关联,而在Redis中,该值不只限于简单的字符串,还能够容纳更复杂的数据结构。如下是Redis支持的全部数据结构的列表,本教程将分别进行介绍:git

  • 二进制安全字符串。
  • 列表:根据插入顺序排序的字符串元素的集合。它们基本上是_链表_。
  • 集:惟一,未排序的字符串元素的集合。
  • 相似于Sets的排序集合,但每一个字符串元素都与一个称为_score_的浮点值相关联。元素老是按它们的分数排序,所以与Sets不一样,能够检索一系列元素(例如,您可能会问:给我前10名或后10名)。
  • 哈希,是由与值关联的字段组成的映射。字段和值都是字符串。这与Ruby或Python哈希很是类似。
  • 位数组(或简称为位图):可使用特殊命令像位数组同样处理字符串值:您能够设置和清除单个位,计数全部设置为1的位,找到第一个设置或未设置的位,等等。
  • HyperLogLogs:这是一个几率数据结构,用于估计集合的基数。别惧怕,它比看起来更简单...请参阅本教程的HyperLogLog部分。
  • 流:提供抽象日志数据类型的类地图项的仅追加集合。在“ Redis流简介”中对它们进行了深刻 介绍

命令参考中掌握这些数据类型的工做方式以及使用什么来解决给定问题并不老是那么容易,所以,本文档是有关Redis数据类型及其最多见模式的速成课程。github

对于全部示例,咱们将使用该redis-cli实用程序(一个简单但方便的命令行实用程序)对Redis服务器发出命令。redis

*Redis keys

Redis密钥是二进制安全的,这意味着您可使用任何二进制序列做为密钥,从“ foo”之类的字符串到JPEG文件的内容。空字符串也是有效的键。算法

有关密钥的其余一些规则:数据库

  • 太长的键不是一个好主意。例如,1024字节的密钥不只是内存方面的问题,也是一个坏主意,并且由于在数据集中查找密钥可能须要进行一些代价高昂的密钥比较。即便手头的任务是匹配一个大值的存在,对它进行散列(例如使用SHA1)也是一个更好的主意,尤为是从内存和带宽的角度来看。
  • 很是短的键一般不是一个好主意。若是您能够改写“ user:1000:followers”,那么将“ u1000flw”写为密钥毫无心义。与键对象自己和值对象使用的空间相比,后者更具可读性,而且添加的空间较小。虽然短键显然会消耗更少的内存,但您的工做是找到合适的平衡。
  • 尝试坚持使用架构。例如,“ object-type:id”是一个好主意,例如“ user:1000”。点或破折号一般用于多字字段,例如“ comment:1234:reply.to”或“ comment:1234:reply-to”中。
  • 容许的最大密钥大小为512 MB。

*Redis Strings

Redis字符串类型是您能够与Redis键关联的最简单的值类型。它是Memcached中惟一的数据类型,所以对于新手来讲,在Redis中使用它也是很天然的。ubuntu

因为Redis键是字符串,所以当咱们也使用字符串类型做为值时,咱们会将一个字符串映射到另外一个字符串。字符串数据类型对于许多用例颇有用,例如缓存HTML片断或页面。数组

让咱们使用来处理字符串类型redis-cli(全部示例将redis-cli在本教程中经过来执行)。缓存

> set mykey somevalue
OK
> get mykey
"somevalue"

如您所见,使用SETGET命令是咱们设置和检索字符串值的方式。请注意,即便键已与非字符串值相关联,SET仍将替换已存储在键中的任何现有值。所以SET执行分配。安全

值能够是每种类型的字符串(包括二进制数据),例如,您能够在值内存储jpeg图像。值不能大于512 MB。服务器

SET命令有有趣的选项,这是做为附加参数。例如,若是密钥已经存在,我可能会要求SET失败,或者相反,只有密钥已经存在时,它才会成功:

> 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命令由一个解析字符串值做为一个整数,它的增量,并最终将得到的值做为新的值。还有其余相似的命令,例如INCRBYDECRDECRBY。在内部,它始终是相同的命令,其执行方式略有不一样。

INCR是原子的意味着什么?即便使用相同密钥发出INCR的多个客户也永远不会进入竞争状态。例如,客户端1不会同时读取“ 10”,客户端2会同时读取“ 10”,都递增为11,并将新值设置为11。最终值将始终为12,而在全部其余客户端未同时执行命令时执行增量设置操做。

有许多用于操做字符串的命令。例如,GETSET命令将键设置为新值,并返回旧值做为结果。例如,若是您的系统在 每次网站接收新访客时使用INCR递增Redis密钥,则可使用此命令。您可能但愿每小时收集一次此信息,而又不会丢失任何增量。您能够GETSET键,为其分配新值“ 0”,而后回读旧值。

在单个命令中设置或检索多个键的值的功能对于减小延迟也颇有用。所以,有MSETMGET命令:

> mset a 10 b 20 c 30
OK
> mget a b c
1) "10"
2) "20"
3) "30"

使用MGET时,Redis返回一个值数组。

*Altering and querying the 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: keys with limited time to live

在继续使用更复杂的数据结构以前,咱们须要讨论另外一个功能,该功能无论值类型如何均可以工做,而且称为Redis expires。基本上,您能够为密钥设置一个超时时间,这是有限的生存时间。生存时间过去后,该密钥将自动销毁,就像用户使用该密钥调用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)
(nil)

因为第二次呼叫延迟了5秒钟以上,所以在两次GET呼叫之间密钥消失了。在上面的示例中,咱们使用EXPIRE来设置过时时间(也可使用它来为已经具备密钥的密钥设置不一样的过时时间,例如可使用PERSIST来删除过时并使密钥永久持久化)。可是,咱们也可使用其余Redis命令来建立具备过时密钥。例如,使用SET选项:

> set key 100 ex 10
OK
> ttl key
(integer) 9

上面的示例使用一个字符串值设置一个密钥,该密钥100的到期时间为十秒钟。稍后调用TTL命令以检查密钥的剩余生存时间。

为了设置和检查以毫秒为单位到期,检查PEXPIRE热释光的命令,以及完整列表SET选项。

*Redis Lists

为了解释List数据类型,最好从理论上入手,由于_List_一词常常被信息技术人员以不正当的方式使用。例如,“ Python列表”并非名称(连接列表)所建议的,而是数组(在Ruby中,相同的数据类型实际上称为数组)。

从很是广泛的角度来看,列表只是一系列有序元素:10,20,1,2,3是一个列表。可是,使用Array实现的List的属性与使用_Linked List_实现的List的属性很是不一样 。

Redis列表是经过连接列表实现的。这意味着即便您在列表中有数百万个元素,在列表的开头或结尾添加新元素的操做也会_在固定时间内_执行。使用LPUSH命令将新元素添加到具备10个元素的列表的开头的速度与将元素添加到具备1000万个元素的列表的开头的速度相同。

缺点是什么?在使用Array实现的列表中,_按索引_访问元素_的_速度很是快(恒定时间索引访问),而在经过连接列表实现的列表中访问速度不是那么快(其中操做须要的工做量与所访问元素的索引成比例)。

Redis列表是经过连接列表实现的,由于对于数据库系统而言,相当重要的是可以以很是快的方式将元素添加到很长的列表中。稍后您将看到,另外一个强大的优点是Redis列表能够在恒定的时间内以恒定的长度获取。

当快速访问大量元素的中间位置很重要时,可使用另外一种称为排序集的数据结构。排序的集将在本教程的后面部分介绍。

*First steps with Redis Lists

所述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须要两个索引,要返回的范围的第一个和最后一个元素。两个索引均可觉得负,告诉Redis从末尾开始计数:所以-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 for lists

列表对于许多任务颇有用,如下是两个很是有表明性的用例:

  • 记住用户发布到社交网络上的最新更新。
  • 使用生产者将项目推送到列表中的消费者与生产者模式进行流程之间的通讯,而消费者(一般是_worker_)消耗这些项目和已执行的动做。Redis具备特殊的列表命令,以使此用例更加可靠和高效。

例如,流行的Ruby库resquesidekiq都在后台使用Redis列表,以实现后台做业。

流行的Twitter社交网络 用户发布的最新推文放入Redis列表中。

为了逐步描述一个常见的用例,假设您的主页显示了在照片共享社交网络中发布的最新照片,而且您想加快访问速度。

  • 每次用户发布新照片时,咱们都会使用LPUSH将其ID添加到列表中。
  • 当用户访问主页时,咱们LRANGE 0 9为了获取最新发布的10个项目。

*Capped lists

在许多用例中,咱们只想使用列表来存储_最新项目_,不管它们是什么:社交网络更新,日志或其余任何内容。

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列出列表元素,其余全部内容都将被丢弃。这容许一个很是简单但有用的模式:一块儿执行List推操做+ List修剪操做,以便添加新元素并丢弃超出限制的元素:

LPUSH mylist <some element>
LTRIM mylist 0 999

上面的组合添加了一个新元素,而且仅将1000个最新元素归入列表。使用LRANGE,您能够访问最重要的项目,而无需记住很是旧的数据。

注意:虽然LRANGE从技术上讲是O(N)命令,但朝列表的开头或结尾访问较小范围是恒定时间操做。

*Blocking operations on lists

列表具备一项特殊功能,使其适合于实现队列,而且一般用做进程间通讯系统的构建块:阻止操做。

想象一下,您想经过一个流程将项目推入列表,而后使用不一样的流程来对这些项目进行某种工做。这是一般的生产者/使用者设置,能够经过如下简单方式实现:

  • 为了将项目推送到列表中,生产者调用LPUSH
  • 为了从列表中提取/处理项目,消费者调用RPOP

可是,有时列表可能为空,没有任何要处理的内容,所以RPOP仅返回NULL。在这种状况下,消费者被迫等待一段时间,而后使用RPOP重试。这称为_轮询_,在这种状况下不是一个好主意,由于它有几个缺点:

  1. 强制Redis和客户端处理无用的命令(列表为空时的全部请求将没法完成任何实际工做,它们只会返回NULL)。
  2. 因为工做人员在收到NULL以后会等待一段时间,所以会增长项目处理的延迟。为了使延迟更小,咱们能够在两次调用RPOP之间等待的时间更少,从而扩大了问题编号1,即对Redis的调用更加无用。

因此,所谓的Redis命令工具BRPOPBLPOP它们的版本RPOPLPOP可以阻止若是列表是空的:他们将回到只有当新的元素添加到列表中的来电者,或在用户指定的超时到达。

这是咱们能够在worker中使用的BRPOP调用的示例:

> brpop tasks 5
1) "tasks"
2) "do_something"

这意味着:“等待列表中的元素tasks,但若是5秒钟后没有可用元素,则返回”。

请注意,您能够将0用做超时来永远等待元素,还能够指定多个列表,而不只仅是一个列表,以便同时等待多个列表,并在第一个列表收到一个元素时获得通知。

有关BRPOP的几点注意事项

  1. 客户端以有序方式提供服务:第一个阻塞等待列表的客户端,在某个元素被其余客户端推送时首先提供服务,依此类推。
  2. 返回值与RPOP相比有所不一样:它是一个包含两个元素的数组,由于它还包含键的名称,由于BRPOPBLPOP可以阻止等待来自多个列表的元素。
  3. 若是达到超时,则返回NULL。

关于列表和阻止操做,您应该了解更多信息。咱们建议您阅读如下内容:

  • 使用RPOPLPUSH能够构建更安全的队列或轮换队列。
  • 该命令还有一个阻塞变体,称为BRPOPLPUSH

*Automatic creation and removal of keys

到目前为止,在咱们的示例中,咱们无需在推入元素以前建立空列表,也无需在内部再也不包含元素时删除空列表。Redis的责任是在列表为空时删除键,或者在键不存在而且咱们试图向其添加元素(例如,使用LPUSH)时建立一个空列表。

这不是特定于列表的,它适用于由多个元素组成的全部Redis数据类型-流,集合,排序集合和哈希。

基本上,咱们能够用三个规则来总结行为:

  1. 当咱们将元素添加到聚合数据类型时,若是目标键不存在,则在添加元素以前会建立一个空的聚合数据类型。
  2. 当咱们从聚合数据类型中删除元素时,若是该值保持为空,则键将自动销毁。流数据类型是此规则的惟一例外。
  3. 调用带有空键的只读命令(例如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

Redis散列与字段值对看起来彻底同样,多是人们指望的“散列”外观:

> 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"

尽管哈希能够方便地表示_对象_,可是实际上能够放入哈希中的字段数没有实际限制(可用内存除外),所以您能够在应用程序内部以多种不一样方式使用哈希。

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集是字符串的无序集合。该 SADD命令添加新的元素,一组。还能够对集合进行许多其余操做,例如测试给定元素是否已存在,执行多个集合之间的交集,并集或求差等等。

> sadd myset 1 2 3
(integer) 3
> smembers myset
1. 3
2. 1
3. 2

在这里,我在集合中添加了三个元素,并告诉Redis返回全部元素。如您所见,它们没有排序-Redis能够在每次调用时随意以任何顺序返回元素,由于与用户之间没有关于元素顺序的约定。

Redis具备用于测试成员资格的命令。例如,检查元素是否存在:

> sismember myset 3
(integer) 1
> sismember myset 30
(integer) 0

“ 3”是集合的成员,而“ 30”不是集合的成员。

集合很是适合表示对象之间的关系。例如,咱们能够轻松地使用集合来实现标签。

对这个问题进行建模的一种简单方法是为咱们要标记的每一个对象设置一个集合。该集合包含与对象关联的标签的ID。

一个例证是标记新闻文章。若是商品ID 1000带有标签一、二、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

注意:在示例中,咱们假设您具备另外一个数据结构,例如Redis哈希,它将标签ID映射到标签名称。

还有其余一些很是简单的操做,使用正确的Redis命令仍然很容易实现。例如,咱们可能须要包含标签一、二、10和27的全部对象的列表。咱们可使用SINTER命令执行此操做,该命令执行不一样集合之间的交集。咱们能够用:

> sinter tag:1:news tag:2:news tag:10:news tag:27:news
... results here ...

除了交集以外,您还能够执行并集,求差,提取随机元素等等。

提取元素的命令称为SPOP,对于建模某些问题很是方便。例如,为了实现基于Web的扑克游戏,您可能须要用一组来表明您的套牌。假设咱们对(C)lubs,(D)钻石,(H)耳钉,(S)垫使用一个单字符前缀:

>  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
   (integer) 52

如今咱们要为每一个玩家提供5张卡片。该SPOP命令删除一个随机元素,将其返回到客户端,因此在这种状况下完美运行。

可是,若是咱们直接在甲板上对其进行称呼,那么在游戏的下一场比赛中,咱们将须要再次填充纸牌,这可能并不理想。所以,首先,咱们能够将存储在deck密钥中的集合复制到game:1:deck密钥中。

这可使用SUNIONSTORE来完成,SUNIONSTORE一般执行多个集合之间的联合,并将结果存储到另外一个集合中。可是,因为单个集合的并集自己,我可使用如下命令复制个人卡组:

> sunionstore game:1:deck deck
(integer) 52

如今,我准备为第一位玩家提供五张牌:

> 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"

一副千斤顶,不是很好...

如今是引入set命令的好时机,该命令提供集合中元素的数量。 在集合理论的上下文中,这一般称为_集合_的_基数_,所以Redis命令称为SCARD

> scard game:1:deck
(integer) 47

数学原理:52-5 = 47。

当您只须要获取随机元素而不将其从集合中删除时,可使用适合该任务的SRANDMEMBER命令。它还具备返回重复元素和非重复元素的功能。

*Redis Sorted sets

排序集是一种数据类型,相似于集合和哈希之间的混合。像集合同样,排序集合由惟一的,非重复的字符串元素组成,所以从某种意义上说,排序集合也是一个集合。

可是,虽然集内的元素没有排序,但排序后的集合中的每一个元素都与一个称为_得分_的浮点值相关联 (这就是为何该类型也相似于哈希的缘由,由于每一个元素都映射到一个值)。

此外,已排序集合中的元素是按_顺序进行的_(所以,它们不是应请求而排序的,顺序是用于表示已排序集合的数据结构的特殊性)。它们按照如下规则排序:

  • 若是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

如您所见,ZADDSADD类似,可是使用一个额外的参数(放置在要添加的元素以前)做为得分。 ZADD也是可变参数,所以即便上面的示例中未使用它,您也能够自由指定多个得分-值对。

使用排序集,返回按其出生年份排序的黑客列表很简单,由于实际上_他们已经被排序了_。

实施说明:排序集是经过包含跳过列表和哈希表的双端口数据结构实现的,所以,每次添加元素时,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"

*Operating on 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可能不是最好的命令名称,可是它可能很是有用,并返回已删除元素的数量。

为排序的集合元素定义的另外一个极其有用的操做是get-rank操做。能够问一个元素在有序元素集合中的位置是什么。

> zrank hackers "Anita Borg"
(integer) 4

ZREVRANK命令也能够为了得到军衔,考虑的要素排序的降低方式。

*Lexicographical scores

在最新版本的Redis 2.8中,引入了一项新功能,该功能容许按字典顺序获取范围,假设已排序集中的元素都以相同的相同分数插入(将元素与C memcmp函数进行比较 ,所以能够确保没有排序规则) ,而且每一个Redis实例将以相同的输出进行回复)。

用于按字典顺序操做的主要命令是ZRANGEBYLEXZREVRANGEBYLEXZREMRANGEBYLEXZLEXCOUNT

例如,让咱们再次添加咱们的著名黑客列表,可是此次对全部元素使用零分:

> 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"

范围能够是包含(inclusive)或排除(exclusive)(取决于第一个字符),字符串无限和负无限分别用+-字符串指定。有关更多信息,请参见文档。

此功能很是重要,由于它容许咱们将排序后的集合用做通用索引。例如,若是要经过128位无符号整数参数索引元素,则只需将元素添加到具备相同分数(例如0)但具备由128个字节组成的16字节前缀的排序集中大尾数中的位数。因为big endian中的数字实际上按数字顺序也按字典顺序(以原始字节顺序)排序,所以您能够要求128位空间中的范围,并得到丢弃前缀的元素值。

若是要在更严重的演示环境中查看该功能,请检查Redis自动完成演示

*Updating the score: leader boards

在切换到下一个主题以前,请只对已排序集作最后的说明。排序集的分数能够随时更新。只需对已包含在排序集中的元素调用ZADD,将以O(log(N))时间复杂度更新其得分(和位置)。这样,当有大量更新时,排序集是合适的。

因为这种特性,常见的用例是排行榜。典型的应用是Facebook游戏,您能够将按高分对用户进行排序的能力与得到排名的操做结合起来,以显示前N名的用户以及排行榜中的用户排名(例如,“您是这里的#4932最佳成绩”)。

*Bitmaps

位图不是实际的数据类型,而是在String类型上定义的一组面向位的操做。因为字符串是二进制安全Blob,而且最大长度为512 MB,所以它们适合设置多达2 32个不一样的位。

位操做分为两类:固定时间的单个位操做(如将一个位设置为1或0或获取其值),以及对位组的操做,例如计算给定位范围内设置的位的数量(例如,人口计数)。

位图的最大优势之一是,它们在存储信息时一般能够节省大量空间。例如,在以增量用户ID表示不一样用户的系统中,仅使用512 MB内存就能够记住40亿用户的一位信息(例如,知道用户是否要接收新闻通信)。

使用SETBITGETBIT命令设置和检索位:

> setbit key 10 1
(integer) 1
> getbit key 10
(integer) 1
> getbit key 11
(integer) 0

所述SETBIT命令采用做为第一个参数的比特数,和做为第二个参数的值以设置所述位,其为1或0的命令自动放大字符串,若是寻址位是当前字符串长度以外。

GETBIT只是返回指定索引处的位的值。超出范围的位(寻址超出存储在目标键中的字符串长度的位)始终被视为零。

在位组上有三个命令:

  1. BITOP在不一样的字符串之间执行按位运算。提供的运算为AND,OR,XOR和NOT。
  2. BITCOUNT执行填充计数,报告设置为1的位数。
  3. BITPOS查找指定值为0或1的第一位。

不管BITPOS比特计数可以与字符串的字节范围进行操做,而不是该字符串的整个长度运行。如下是BITCOUNT调用的一个简单示例:

> setbit key 0 1
(integer) 0
> setbit key 100 1
(integer) 0
> bitcount key
(integer) 2

位图的常见用例是:

  • 各类实时分析。
  • 存储与对象ID相关的空间高效但高性能的布尔信息。

例如,假设您想知道网站用户天天访问量最长的时间。您从零开始计算天数,即从您公开网站的那一天开始,并在用户每次访问该网站时对SETBIT进行设置。做为位索引,您只需花费当前的unix时间,减去初始偏移量,而后除以一天中的秒数(一般为3600 * 24)。

这样,对于每一个用户,您都有一个小的字符串,其中包含天天的访问信息。使用BITCOUNT,能够轻松得到给定用户访问网站的天数,而只需几个BITPOS调用,或者仅获取和分析客户端的位图,就能够轻松计算最长的连胜记录。

位图很容易分红多个键,例如,为了分片数据集,而且由于一般最好避免使用大键。要在不一样的密钥上拆分位图,而不是将全部位都设置为密钥,一个简单的策略就是为每一个密钥存储M位,并使用来获取密钥名称,使用来获取bit-number/M第N位bit-number MOD M

*HyperLogLogs

HyperLogLog是一种几率数据结构,用于对惟一事物进行计数(从技术上讲,这是指估计集合的基数)。一般,对惟一项目进行计数须要使用与要计数的项目数量成比例的内存量,由于您须要记住过去已经看到的元素,以免屡次对其进行计数。可是,有一组算法会之内存为代价来交换精度:您最终会获得带有标准偏差的估计量度,在Redis实现的状况下,该偏差小于1%。这种算法的神奇之处在于,您再也不须要使用与所计数项目数量成正比的内存量,而是可使用恒定数量的内存!在最坏的状况下为12k字节,若是您的HyperLogLog(从如今开始将它们称为HLL)看到的元素不多,则少得多。

Redis中的HLL尽管在技术上是不一样的数据结构,但被编码为Redis字符串,所以您能够调用GET来序列化HLL,而后调用SET 来将其反序列化回服务器。

从概念上讲,HLL API就像使用Set来执行相同的任务。你会 萨德每一个观测元素为一组,而且将使用SCARD检查组中的元素,这是惟一的数量自SADD不会再添加一个现有的元素。

尽管您并未真正_将项目添加_到HLL中,但因为数据结构仅包含不包含实际元素的状态,所以API相同:

  • 每次看到新元素时,均可以使用PFADD将其添加到计数中。

  • 到目前为止,每次您要检索_添加_到PFADD的惟一元素的当前近似值时,均可以使用PFCOUNT

    > pfadd hll a b c d
    (integer) 1
    > pfcount hll
    (integer) 4

该数据结构用例的一个例子是天天计算用户在搜索表单中执行的惟一查询。

Redis也可以执行HLL的合并,请查看 完整的文档以获取更多信息。

*Other notable features

Redis API中还有其余重要内容,在本文档的上下文中没法探讨,但值得您注意:

*Learn more

本教程毫不完整,仅涵盖了API的基础知识。阅读命令参考以发现更多内容。

感谢您的阅读,并祝您使用Redis玩得开心!

相关文章
相关标签/搜索