美文网首页
Redis数据类型 - Lists

Redis数据类型 - Lists

作者: 马蹄哒 | 来源:发表于2018-09-16 14:34 被阅读0次

    一般来说,列表是有序元素的序列:10、20、1、2、3是一个列表。基于数组实现的列表和基于链表( Linked Lists)实现的列表的属性是不同的。

    Redis列表是通过链表实现的。这意味着,即使在列表中有数百万个元素,在列表的头部或尾部添加新元素的操作所耗费的时间是恒定的。使用LPUSH命令向包含10个元素的列表头部添加新元素的速度与向包含1000万个元素的列表头部添加一个元素的速度是一样的。

    缺点是,通过索引访问一个基于数组实现的Lists中的元素速度是非常快的(恒定的访问时间),而相比之下,在基于链表实现的Lists中速度就没有那么快(在这种情况下,操作需要的时间与被访问元素的索引成正比)。

    Redis列表采用链表来实现的主要原因是,对于数据库系统来说,能够以非常快的速度向一个非常长的列表添加元素是非常重要的。另一个强大的优势,就是Redis列表可以在恒定的时间内获取恒定长度的元素。

    常用命令:
    lpush listname value 向列表的左边(头部)添加一个元素;也可同时添加多个值,用空格隔开
    rpush listname value 向列表的右边(尾部)添加一个元素;也可同时添加多个值,用空格隔开
    lrange listname 0 -1 获取列表的所有元素
    lpop listname 取出列表头部的第一个元素(列表中的元素会减少),如果没有则返回:nil;
    rpop listname 取出列表尾部的最后一个元素(列表中的元素会减少),如果没有则返回:nil;

    使用场景:
    Lists非常有用,下面是两个例子:
    1.存储用户在社交网络上发布的最新动态。
    2.进程之间的通信。使用消费者-生产者模式,其中生产者将项目推入列表,消费者(通常是worker进程)使用这些项目并执行操作。Redis有特殊的列表命令来使这些操作更加可靠和高效。

    例如,流行的Ruby库resque和sidekiq都在底层使用Redis列表来实现。
    假设想你在主页显示社交网络中发布的最新照片,希望加快访问速度,可以这样做:
    a. 每当用户发布新照片时,我们都会用LPUSH将其ID添加到列表中。
    b. 当用户访问主页时,我们使用lrange 0 9来获取最新发布的10个条目。

    限定列表(Capped lists)
    有时候我们只想使用列表来存储最新的数据,比如,社交网络更新、日志或任何其他东西。
    Redis允许我们使用列表作为限定集合,只保持最新的N条记录,使用LTRIM命令丢弃所有旧的的记录。
    LTRIM命令与LRANGE类似,但是它不是显示指定范围内的元素,而是将指定范围内的元素设置为列表的值。所有超出给定范围的元素都被删除。
    一个非常简单但有用的模式:一起执行列表lpush操作+列表ltrim操作,以便添加一个新元素并丢弃超过限制的元素:

    LPUSH mylist <some element>
    LTRIM mylist 0 999
    

    阻塞操作
    列表的特性使它适合用来实现队列,并且通常作为进程间通信系统的构建块:阻塞操作。

    假设你想用一个进程将元素推入列表,并使用不同的进程来实际处理这元素。这就是常见的生产者/消费者模式,可以通过以下简单的方式实现:
    1. 将元素推入列表中,生产者会调用LPUSH。
    2. 要从列表中提取/处理元素,消费者调用RPOP。

    然而,有时候列表是空的,没有什么要处理的,所以RPOP只返回NULL。在这种情况下,消费者必须等待一段时间,然后再用RPOP重试一次。这被称为轮询(polling),在这种情况下使用RPOP并不是一个好主意,因为它有几个缺点:
    1. 强制Redis和客户端处理无用的命令(当列表为空时,所有的请求都没有作用,它们只会返回NULL)。
    2. 元素的处理会产生延迟,因为在worker接收到NULL之后,它会等待一段时间。即使减小延迟,可以在调用RPOP之间等待更少的时间,但这样会加重问题1,也就是增加对Redis无用的调用。

    因此,Redis实现了名为BRPOP和BLPOP的命令,这些命令是RPOP和LPOP的一个版本,当列表为空时,它们会阻塞:只有当列表中添加新元素或达到用户指定的超时时间,它们才会返回给调用者。

    brpop tasks 5  #等待列表tasks中的元素,但如果5秒后没有可用元素,则返回
    

    你可以使用0作为超时时间来永远等待列表的元素,并且还可以同时指定多个列表而不仅仅是一个列表,以便同时等待多个列表的,并在第一个列表有可用元素时获得通知。
    关于BRPOP有几点需要注意:
    1. 客户端以一种有序的方式获得响应:当其他客户端向列表推入一个元素时,阻塞等待这个列表的第一个客户端,优先得到服务。
    2. BRPOP返回值与RPOP不同:它返回包含两个元素的数组,(除了值)它还包含键的名称,因为BRPOP和BLPOP能够阻塞等待多个列表中的元素。
    3. 如果超时,则返回NULL。

    • 可以使用RPOPLPUSH构建更安全的队列或回转队列。
    • 还有一个命令的阻塞变体:BRPOPLPUSH
    自动创建和删除键

    到目前为止,在我们的示例中,我们从未在推送元素之前创建过空列表,或者当列表内没有元素时删除空列表。Redis的职责是在列表为空时删除键,或者当键不存在,我们试图向其添加元素时,创建一个空列表,例如,LPUSH。

    这不是列表特有的,它适用于由多个元素组成的所有Redis数据类型——Sets、Sorted Sets和Hashes。

    基本上我们可以用三个规则来概括这个行为:

    1. 当我们向聚合数据类型(aggregate data type)添加元素时,如果目标键不存在,则在添加元素之前创建一个空的聚合数据类型。
    2. 当我们从聚合数据类型中删除元素时,如果值为空,则键将自动销毁。
    3. 对空键调用诸如LLEN(返回列表的长度)之类的只读命令或使用写命令删除元素,总是会产生相同的结果,就好像该键持有命令希望找到的类型的空聚合类型一样。

    规则1的例子:

    127.0.0.1:6379> exists mylist
    (integer) 0
    127.0.0.1:6379> lpush mylist 1 2 3
    (integer) 3
    127.0.0.1:6379> exists mylist
    (integer) 1
    

    但如果键已经存在,我们不能对错误的类型进行操作:

    127.0.0.1:6379> set foo bar
    OK
    127.0.0.1:6379> lpush foo 1 2 3
    (error) WRONGTYPE Operation against a key holding the wrong kind of value
    127.0.0.1:6379> type foo
    string
    

    规则2的例子:
    移除所有元素之后,键也不存在了。

    127.0.0.1:6379> lpush mylist 1 2 3
    (integer) 3
    127.0.0.1:6379> exists mylist
    (integer) 1
    127.0.0.1:6379> lpop mylist
    "3"
    127.0.0.1:6379> lpop mylist
    "2"
    127.0.0.1:6379> lpop mylist
    "1"
    127.0.0.1:6379> exists mylist
    (integer) 0
    

    规则3的例子:

    127.0.0.1:6379> exists mylist
    (integer) 0
    127.0.0.1:6379> llen mylist
    (integer) 0
    127.0.0.1:6379> lpop mylist
    (nil)
    

    相关文章

      网友评论

          本文标题:Redis数据类型 - Lists

          本文链接:https://www.haomeiwen.com/subject/zywdgftx.html