(三) Redis高级类型
Redis 列表 ( Lists )
Redis的列表是使用链表实现的. 所以头部/尾部操作都是在常量时间完成.LPUSH/RPUSH 头尾增加Redis 采用链表来实现列表是因为,对于数据库系统来说,他们认为快速插入一个元素到一个很长的列表非常重要.另外一个即将描述的优势是, Redis列表能在常数时间内获得常数长度.如果需要快速访问一个拥有大量元素的集合的中间数据,可以用另一个称为有序集合的数据结构.
Redis 列表起步
LPUSH/RPUSH 分别在头部/尾部添加一个元素到列表.
LRANGE 从列表中提取一个范围内元素
注意 LRANGE 命令使用两个索引下表,分别是返回的范围的开始和结束院所索引.两个索引坐标可以是负数,表示从后往前数,所以 -1 表示最后一个元素, -2 表示倒数第二个元素.以此类推.另:LPUSH/RPUSH 两个命令的参数都是可变长参数.
LPOP/RPOP 分别在头部/尾部弹出一个元素 返回nil值表明列表中没有元素了.
列表的通用场景 ( Common use cases )
具有列表代表性的场景如下:
记录社交网络中用户最近提交的更新.可以模拟队列操作 ( 生产者消费者模式 ) 不过对于此项 Redis 有提供专用的列表命令来更可靠高效的解决该问题。
上限列表 ( Capped )
Redis 允许使用列表作为一个上限集合,使用 LTRIM 命令仅仅记住最新的N项,丢弃掉所有老的项.
LTRIM 命令类似于 LRANGE, 但是不同于展示指定范围的元素,而是将其作为列表新值存储.所有范围外的元素都将被删除.eg:LTRIM 所指定的范围是0到2 所以 数值 4 5 都被删除了.同时又返回了我们需要的数据.所以但要访问元素列表而又要抛弃老元素的时候,则可以使用 LTRIM 来操作注意:尽管LRANGE是一个 O ( N ) 时间复杂度的命令,访问列表头尾附近的小范围是常量时间的操作.
列表的阻塞操作 ( blocking )
列表有一个特别的特性:阻塞操作.
简单实现方式:生产者调用 LPUSH 添加项到列表中消费者调用 RPOP 从列表提取 / 处理项如果列表是空的 则RPOP返回NULL.所以消费者被签字等待一段时间并且重试 RPOP 命令.这称为轮询 ( polling ), 由于其具有一些缺点,所以不适合在如下情况中使用:1.强制 Redis 和客户端处理无用的命令 ( 但列表为空时,所有请求无实际操作 返回NULL ).2.由于工作者受到一个 NULL 后会等待一段时间,这会延迟对项的处理.介于以上的原因 Redis 实现了 BRPOP 和 BLPOP 两个命令,它们是但列表为空时 RPOP 和 LPOP 的会阻塞版本: 仅但一个新元素被添加到列表时, 或者到达了用户的指定超时时间, 才返回给调用者. 这个是我们在工作者中调用 BRPOP 的例子:消费者:生产者:brpop tasks 10 从tasks中弹出右边的数据 等待10秒lpush tasks 5 向tasks中压入数据5到左边注意: 这里可以使用0作为超时让其消费者一直等待元素,你也可以指定多个列表而不仅仅是一个,同时等待多个列表,但第一个列表收到元素后就能得到通知.关于BRPOP的一些注意事项1.客户端按顺序服务:第一个被阻塞等待列表的客户端,将第一个收到其他客户端添加的元素,等等.2.与 RPOP 的返回值不同: 返回的是一个数组,其中包括键的名字,因为 BRPOP 和 BLPOP 可以阻塞等待多个列表的元素.3.如果超时时间到达,返回 NULL还有更多需要知道的关于列表和阻塞的选项,可以具体阅读使用 RPOLPUSH 构建跟安全的队列和旋转队列.BRPOPLPUSH 命令是其阻塞命令的变种命令.
自动创建和删除键
当一个列表为空时 Redis 将删除该键,但向一个不存在的列表键 ( 如使用 LPUSH ) 添加一个元素时,将创建一个空的列表.这并不只是针对列表,适用于所有 Redis 多元素组成的数据类型,因此适用于集合,有序集合和哈希.基本上我们可以概括为三条规则:1.但我们向聚合 ( aggregate ) 数据类型添加一个元素,如果键不存在,添加元素前就会自动创建一个空的聚合数据类型.2.但我们从聚合数据类型删除一个元素,如果值为空,则键也会被销毁.3.调用一个像 LLEN 的只读命令 ( 返回列表的长度 ),或者一个写命令从空键删除元素, 总是产生和操作一个持有空聚合类型值的键一样的结果.eg:规则1.lpush foo 1 2 3 报错是因为 foo的数据类型是string 而我们用了列表操作lpush规则2.如上图所示但所有元素弹出后,键就不存在了规则3.
Redis 哈希/散列 ( Hashes )
Redis 哈希看起来正如你所期待的那样:哈希就是字段值对 ( fields-values pairs ) 的集合.由于哈希容易表示对象,事实上哈希中的字段的数量并没有限制,所以我们可以在应用程序以不同的方式来使用哈希.HMSET 命令为哈希设置多个字段, HGET 检索一个单独的字段. HMGET 类似于 HGET, 但是返回值是数组:也有一些命令可以针对单个字段执行操作, 例如 HINCRBY:你可以从命令页找到全部哈希命令列表.值得注意的是,小的哈希 ( 少量元素,不太大的值 ) 在内存中以一种特殊的方式编码以高效利用内存.
Redis 集合 (Sets)
Redis 集合是无序的字符串集合 ( collections ). SADD 命令添加元素到集合.还可以对集合执行很多其他的操作,例如:测试元素是否存在,对多个集合执行交集/并集/和差集,等等.如上图给集合添加了 3 个元素,然后告诉 Redis 返回所有元素.注:这里我用的MS维护的Redis客户端就自动排序了.其实他们是没有排序,Redis在每次调用时按随意顺序返回元素,因为没有与用户有任何元素排序协议.我们有测试成员关系的命令.一个指定的元素存在吗?" 3 " 是成员中的元素 反之 " 4 " 不是集合适用于表达对象间的关系.建模等~假设,我们想标记新闻.如果我们的 ID 为 1000 的新闻,被标签1, 2, 5 和 77标记,我们可以有一个这篇新闻被关联标记 ID 的集合:然后有时候我们也想要一些方向的关系 : 被某个标签标记的所有文章:获取指定对象的标签很简单:注意:在这个例子中,我们假设有另一个数据结构,例如,一个 Redis 哈希, 存储标签 ID 到标签名的映射.还有一些 Redis 命令很容易实现该操作.eg: 我们想获取所有被标签 1, 2, 10 和 27 同时标记的对象列表.我们可以使用 SINTER 命令实现这个, 也就是对不同的集合执行交集.并不仅仅是交集操作, 也可以执行并集,差集,随机抽取元素操作等等.抽取一个元素的命令是 SPOP, 就方便很多问题建模.eg:实现一个基于 web 的扑克游戏, 我可以将一副牌表示为集合.假设我们使用一个字符前缀表示(C)lubs 梅花, (D)iamonds 方块, (H)earts 红心, (S)pades 黑桃.现在我们要先copy一副牌数据,然后为每位选手提供5张牌.SPOP命令删除一个随机元素,返回给客户端,是在额个场景下的最佳操作.SUNIONSTORE 命令通常对多个集合执行交集,然后把结果存储在另外一个集合中.而对单个集合求交集就是其自身,于是:为第一个选手提供5张牌:好差的牌~ 哈SCARD 是提供集合中元素数量的命令.这个在集合理论中称为集合的基数 ( cardinality, 也称集合的势 ).OK 因为刚刚spop了5次 所以还剩47张牌 ( 元素 ).SRANDMEMBER 命令是提供获取随机元素的命令,只是他不会将获取到的元素从集合中删除.它具有返回重复元素和非重复元素的特性.
把握好方向,踏着脚下的路.做一个傻子 - 终会有一个终点.