• Redis 基本使用


    基本的数据类型

    • 二进制安全的字符串
    • Lists: 按插入顺序排序的字符串元素的集合。他们基本上就是链表(linked lists)
    • Sets: 不重复且无序的字符串元素的集合。
    • Sorted sets,类似Sets,但是每个字符串元素都关联到一个叫score浮动数值(floating number value)。里面的元素总是通过score进行着排序,所以不同的是,它是可以检索的一系列元素。(例如你可能会问:给我前面10个或者后面10个元素)。
    • Hashes,由field和关联的value组成的map。field和value都是字符串的。这和Ruby、Python的hashes很像。

    Redis keys

    Redis key值是二进制安全的,这意味着可以用任何二进制序列作为key值,从形如”foo”的简单字符串到一个JPEG文件的内容都可以。空字符串也是有效key值。

    关于key的几条规则:

    • 太长的键值不是个好主意,例如1024字节的键值就不是个好主意,不仅因为消耗内存,而且在数据中查找这类键值的计算成本很高。
    • 太短的键值通常也不是好主意,如果你要用”u:1000:pwd”来代替”user:1000:password”,这没有什么问题,但后者更易阅读,并且由此增加的空间消耗相对于key object和value object本身来说很小。当然,没人阻止您一定要用更短的键值节省一丁点儿空间。
    • 最好坚持一种模式。例如:”object-type:id:field”就是个不错的注意,像这样”user:1000:password”。我喜欢对多单词的字段名中加上一个点,就像这样:”comment:1234:reply.to”。

    Redis key值是二进制安全的,这意味着可以用任何二进制序列作为key值,从形如”foo”的简单字符串到一个JPEG文件的内容都可以。空字符串也是有效key值。

    关于key的几条规则:

    • 太长的键值不是个好主意,例如1024字节的键值就不是个好主意,不仅因为消耗内存,而且在数据中查找这类键值的计算成本很高。
    • 太短的键值通常也不是好主意,如果你要用”u:1000:pwd”来代替”user:1000:password”,这没有什么问题,但后者更易阅读,并且由此增加的空间消耗相对于key object和value object本身来说很小。当然,没人阻止您一定要用更短的键值节省一丁点儿空间。
    • 最好坚持一种模式。例如:”object-type:id:field”就是个不错的注意,像这样”user:1000:password”。我喜欢对多单词的字段名中加上一个点,就像这样:”comment:1234:reply.to”。

    Redis Strings

    这是最简单Redis类型。如果你只用这种类型,Redis就像一个可以持久化的memcached服务器(注:memcache的数据仅保存在内存中,服务器重启后,数据将丢失)。

    > set mykey somevalue
    OK
    > get mykey
    "somevalue"

    正如你所见到的,通常用SET command 和 GET command来设置和获取字符串值。

    值可以是任何种类的字符串(包括二进制数据),例如你可以在一个键下保存一副jpeg图片。值的长度不能超过512 MB。

     虽然字符串是Redis的基本值类型,但你仍然能通过它完成一些有趣的操作。例如:原子递增:

    > set counter 100
    OK
    > incr counter
    (integer) 101
    > incr counter
    (integer) 102
    > incrby counter 50
    (integer) 152

    INCR 命令将字符串值解析成整型,将其加一,最后将结果保存为新的字符串值,类似的命令有INCRBYDECR 和 DECRBY。实际上他们在内部就是同一个命令,只是看上去有点儿不同。

    INCR是原子操作意味着什么呢?就是说即使多个客户端对同一个key发出INCR命令,也决不会导致竞争的情况。例如如下情况永远不可能发生:『客户端1和客户端2同时读出“10”,他们俩都对其加到11,然后将新值设置为11』。最终的值一定是12,read-increment-set操作完成时,其他客户端不会在同一时间执行任何命令。

    对字符串,另一个的令人感兴趣的操作是GETSET命令,行如其名:他为key设置新值并且返回原值。这有什么用处呢?例如:你的系统每当有新用户访问时就用INCR命令操作一个Redis key。你希望每小时对这个信息收集一次。你就可以GETSET这个key并给其赋值0并读取原值。

    为减少等待时间,也可以一次存储或获取多个key对应的值,使用MSETMGET命令:

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

    MGET 命令返回由值组成的数组。

    修改或查询键空间

    有些指令不是针对任何具体的类型定义的,而是用于和整个键空间交互的。因此,它们可被用于任何类型的键。

    使用EXISTS命令返回1或0标识给定key的值是否存在,使用DEL命令可以删除key对应的值,DEL命令返回1或0标识值是被删除(值存在)或者没被删除(key对应的值不存在)。

    > set mykey hello
    OK
    > exists mykey
    (integer) 1
    > del mykey
    (integer) 1
    > exists mykey
    (integer) 0

    TYPE命令可以返回key对应的值的存储类型:

    > set mykey x
    OK
    > type mykey
    string
    > del mykey
    (integer) 1
    > type mykey
    none

    Redis超时:数据在限定时间内存活

    在介绍复杂类型前我们先介绍一个与值类型无关的Redis特性:超时。你可以对key设置一个超时时间,当这个时间到达后会被删除。精度可以使用毫秒或秒。

    > set key some-value
    OK
    > expire key 5
    (integer) 1
    > get key (immediately)
    "some-value"
    > get key (after some time)
    (nil)

    上面的例子使用了EXPIRE来设置超时时间(也可以再次调用这个命令来改变超时时间,使用PERSIST命令去除超时时间 )。我们也可以在创建值的时候设置超时时间:

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

    TTL命令用来查看key对应的值剩余存活时间。

    Redis Lists

    要说清楚列表数据类型,最好先讲一点儿理论背景,在信息技术界List这个词常常被使用不当。例如”Python Lists”就名不副实(名为Linked Lists),但他们实际上是数组(同样的数据类型在Ruby中叫数组)

    一般意义上讲,列表就是有序元素的序列:10,20,1,2,3就是一个列表。但用数组实现的List和用Linked List实现的List,在属性方面大不相同。

    Redis lists基于Linked Lists实现这意味着即使在一个list中有数百万个元素,在头部或尾部添加一个元素的操作,其时间复杂度也是常数级别的。用LPUSH 命令在十个元素的list头部添加新元素,和在千万元素list头部添加新元素的速度相同。

    那么,坏消息是什么?在数组实现的list中利用索引访问元素的速度极快,而同样的操作在linked list实现的list上没有那么快。

    Redis Lists用linked list实现的原因是:对于数据库系统来说,至关重要的特性是:能非常快的在很大的列表上添加元素。另一个重要因素是,正如你将要看到的:Redis lists能在常数时间取得常数长度。

    如果快速访问集合元素很重要,建议使用可排序集合(sorted sets)。可排序集合我们会随后介绍。

    Redis lists 入门

    LPUSH 命令可向list的左边(头部)添加一个新元素,而RPUSH命令可向list的右边(尾部)添加一个新元素。最后LRANGE命令可从list中取出一定范围的元素:

    > 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表示list中的倒数第二个元素,以此类推。

    上面的所有命令的参数都可变,方便你一次向list存入多个值。

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

    还有一个重要的命令是pop,它从list中删除元素并同时返回删除的值。可以在左边或右边操作。

    > rpush mylist a b c
    (integer) 3
    > rpop mylist
    "c"
    > rpop mylist
    "b"
    > rpop mylist
    "a"

    我们增加了三个元素,并弹出了三个元素,因此,在这最后 列表中的命令序列是空的,没有更多的元素可以被弹出。如果我们尝试弹出另一个元素,这是我们得到的结果:

    > rpop mylist
    (nil)

    当list没有元素时,Redis 返回了一个NULL。

    List的常用案例

    正如你可以从上面的例子中猜到的,list可被用来实现聊天系统。还可以作为不同进程间传递消息的队列。关键是,你可以每次都以原先添加的顺序访问数据。这不需要任何SQL ORDER BY 操作,将会非常快,也会很容易扩展到百万级别元素的规模。

    例如在评级系统中,比如社会化新闻网站 reddit.com,你可以把每个新提交的链接添加到一个list,用LRANGE可简单的对结果分页。

    在博客引擎实现中,你可为每篇日志设置一个list,在该list中推入博客评论,等等。

    Capped lists

    可以使用LTRIM把list从左边截取指定长度。

    > rpush mylist 1 2 3 4 5  // 5在最左边
    (integer) 5
    > ltrim mylist 0 2
    OK
    > lrange mylist 0 -1
    1) "1"
    2) "2"
    3) "3"

    List上的阻塞操作

    可以使用Redis来实现生产者和消费者模型,如使用LPUSH和RPOP来实现该功能。但会遇到这种情景:list是空,这时候消费者就需要轮询来获取数据,这样就会增加redis的访问压力、增加消费端的cpu时间,而很多访问都是无用的。为此redis提供了阻塞式访问 BRPOP 和 BLPOP 命令。 消费者可以在获取数据时指定如果数据不存在阻塞的时间,如果在时限内获得数据则立即返回,如果超时还没有数据则返回null, 0表示一直阻塞。

    同时redis还会为所有阻塞的消费者以先后顺序排队。

    如需了解详细信息请查看 RPOPLPUSH 和 BRPOPLPUSH

    key 的自动创建和删除

    目前为止,在我们的例子中,我们没有在推入元素之前创建空的 list,或者在 list 没有元素时删除它。在 list 为空时删除 key,并在用户试图添加元素(比如通过 LPUSH)而键不存在时创建空 list,是 Redis 的职责。

    这不光适用于 lists,还适用于所有包括多个元素的 Redis 数据类型 – Sets, Sorted Sets 和 Hashes。

    基本上,我们可以用三条规则来概括它的行为:

    1. 当我们向一个聚合数据类型中添加元素时,如果目标键不存在,就在添加元素前创建空的聚合数据类型。
    2. 当我们从聚合数据类型中移除元素时,如果值仍然是空的,键自动被销毁。
    3. 对一个空的 key 调用一个只读的命令,比如 LLEN (返回 list 的长度),或者一个删除元素的命令,将总是产生同样的结果。该结果和对一个空的聚合类型做同个操作的结果是一样的。

    规则 1 示例:

    > del mylist
    (integer) 1
    > lpush mylist 1 2 3
    (integer) 3

    但是,我们不能对存在但类型错误的 key 做操作:   > 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

    所有的元素被弹出之后, key 不复存在。

    规则 3 示例:

    > del mylist
    (integer) 0
    > llen mylist
    (integer) 0
    > lpop mylist
    (nil)

    Redis Hashes

    Redis hash 看起来就像一个 “hash” 的样子,由键值对组成:

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

    Hash 便于表示 objects,实际上,你可以放入一个 hash 的域数量实际上没有限制(除了可用内存以外)。所以,你可以在你的应用中以不同的方式使用 hash。

    HMSET 指令设置 hash 中的多个域,而 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

    你可以在文档中找到 hash 指令的完整列表

    值得注意的是,小的 hash 被用特殊方式编码,非常节约内存。

    Redis Sets

    Redis Set 是 String 的无序排列。SADD 指令把新的元素添加到 set 中。对 set 也可做一些其他的操作,比如测试一个给定的元素是否存在,对不同 set 取交集,并集或差,等等。

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

    现在我已经把三个元素加到我的 set 中,并告诉 Redis 返回所有的元素。可以看到,它们没有被排序 —— Redis 在每次调用时可能按照任意顺序返回元素,因为对于元素的顺序并没有规定。

    Redis 有检测成员的指令。一个特定的元素是否存在?

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

    “3” 是 set 的一个成员,而 “30” 不是。

    Sets 适合用于表示对象间的关系。 例如,我们可以轻易使用 set 来表示标记。

    一个简单的建模方式是,对每一个希望标记的对象使用 set。这个 set 包含和对象相关联的标签的 ID。

    假设我们想要给新闻打上标签。 假设新闻 ID 1000 被打上了 1,2,5 和 77 四个标签,我们可以使用一个 set 把 tag 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

    获取一个对象的所有 tag 是很方便的:

    > smembers news:1000:tags
    1. 5
    2. 1
    3. 77
    4. 2

    注意:在这个例子中,我们假设你有另一个数据结构,比如一个 Redis hash,把标签 ID 对应到标签名称。

    使用 Redis 命令行,我们可以轻易实现其它一些有用的操作。比如,我们可能需要一个含有 1, 2, 10, 和 27 标签的对象的列表。我们可以用 SINTER 命令来完成这件事。它获取不同 set 的交集。我们可以用:

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

    不光可以取交集,还可以取并集,差集,获取随机元素,等等。

  • 相关阅读:
    ROS工业机器人 ROSI Kinetic培训课程
    伺服电机电流、速度、位置环控制
    人工智能入门书单
    更新 Ubuntu 系统,避免报错「校验和不符」
    arduino+16路舵机驱动板连接测试
    Visual SLAM算法:2010年至2016年的回顾
    roslink
    java——自动装箱拆箱
    java_四大流
    Java反射
  • 原文地址:https://www.cnblogs.com/cangqinglang/p/9914436.html
Copyright © 2020-2023  润新知