Redis开发与运维. 2.5 集合

2.5 集合

集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。如图2-22所示,集合user:1:follow包含着"it"、"music"、

"his"、"sports"四个元素,一个集合最多可以存储232-1个元素。Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集,合理地使用好集合类型,能在实际开发中解决很多实际问题。

2.5.1 命令

下面将按照集合内和集合间两个维度对集合的常用命令进行介绍。

1.?集合内操作

(1)添加元素

sadd key element [element ...]

返回结果为添加成功的元素个数,例如:

127.0.0.1:6379> exists myset

(integer) 0

127.0.0.1:6379> sadd myset a b c

(integer) 3

127.0.0.1:6379> sadd myset a b

(integer) 0

(2)删除元素

srem key element [element ...]

返回结果为成功删除元素个数,例如:

127.0.0.1:6379> srem myset a b

(integer) 2

127.0.0.1:6379> srem myset hello

(integer) 0

(3)计算元素个数

scard key

scard的时间复杂度为O(1),它不会遍历集合所有元素,而是直接用Redis内部的变量,例如:

127.0.0.1:6379> scard myset

(integer) 1

(4)判断元素是否在集合中

sismember key element

如果给定元素element在集合内返回1,反之返回0,例如:

127.0.0.1:6379> sismember myset c

(integer) 1

(5)随机从集合返回指定个数元素

srandmember key [count]

[count]是可选参数,如果不写默认为1,例如:

127.0.0.1:6379> srandmember myset 2

1) "a"

2) "c"

127.0.0.1:6379> srandmember myset

"d"

(6)从集合随机弹出元素

spop key

spop操作可以从集合中随机弹出一个元素,例如下面代码是一次spop后,集合元素变为"d b a":

127.0.0.1:6379> spop myset

"c"

127.0.0.1:6379> smembers myset

1) "d"

2) "b"

3) "a"

需要注意的是Redis从3.2版本开始,spop也支持[count]参数。

srandmember和spop都是随机从集合选出元素,两者不同的是spop命令执行后,元素会从集合中删除,而srandmember不会。

(7)获取所有元素

smembers key

下面代码获取集合myset所有元素,并且返回结果是无序的:

127.0.0.1:6379> smembers myset

1) "d"

2) "b"

3) "a"

smembers和lrange、hgetall都属于比较重的命令,如果元素过多存在阻塞Redis的可能性,这时候可以使用sscan来完成,有关sscan命令2.7节会介绍。

2.?集合间操作

现在有两个集合,它们分别是user:1:follow和user:2:follow:

127.0.0.1:6379> sadd user:1:follow it
music his sports

(integer) 4

127.0.0.1:6379> sadd user:2:follow it
news ent sports

(integer) 4

(1)求多个集合的交集

sinter key [key ...]

例如下面代码是求user:1:follow和user:2:follow两个集合的交集,返回结果是sports、it:

127.0.0.1:6379> sinter user:1:follow
user:2:follow

1) "sports"

2) "it"

(2)求多个集合的并集

suinon key [key ...]

例如下面代码是求user:1:follow和user:2:follow两个集合的并集,返回结果是sports、it、his、news、music、ent:

127.0.0.1:6379> sunion user:1:follow
user:2:follow

1) "sports"

2) "it"

3) "his"

4) "news"

5) "music"

6) "ent"

(3)求多个集合的差集

sdiff key [key ...]

例如下面代码是求user:1:follow和user:2:follow两个集合的差集,返回结果是music和his:

127.0.0.1:6379> sdiff user:1:follow
user:2:follow

1) "music"

2) "his"

前面三个命令如图2-23所示。

 

图2-23 集合求交集、并集、差集

(4)将交集、并集、差集的结果保存

sinterstore destination key [key ...]

suionstore 
destination key [key ...]

sdiffstore 
destination key [key ...]

集合间的运算在元素较多的情况下会比较耗时,所以Redis提供了上面三个命令(原命令 + store)将集合间交集、并集、差集的结果保存在destination key中,例如下面操作将user:1:follow和user:2:follow两个集合的交集结果保存在user:1_2:inter中,user:1_2:inter本身也是集合类型:

127.0.0.1:6379> sinterstore
user:1_2:inter user:1:follow user:2:follow

(integer) 2

127.0.0.1:6379> type user:1_2:inter

set

127.0.0.1:6379> smembers user:1_2:inter

1) "it"

2) "sports"

至此有关集合的命令基本已经介绍完了,表2-6给出集合常用命令的时间复杂度,开发人员可以根据自身需求进行选择。

表2-6 集合常用命令时间复杂度

命  令         时间复杂度

sadd key element [element ...]        O(k),k是元素个数

srem key element [element ...]       O(k),k是元素个数

scard key O(1)

sismember key element  O(1)

srandmember key [count]        O(count)

spop key  O(1)

smembers key O(n),n是元素总数

sinter key [key ...] 或者
sinterstore      O(m*k),k是多个集合中元素最少的个数,m是键个数

suinon key [key ...] 或者 suionstore      O(k),k是多个集合元素个数和

sdiff key [key ...] 或者 sdiffstore   O(k),k是多个集合元素个数和

 

2.5.2 内部编码

集合类型的内部编码有两种:

intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,Redis会选用intset来作为集合的内部实现,从而减少内存的使用。

hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。

下面用示例来说明:

1)当元素个数较少且都为整数时,内部编码为intset:

127.0.0.1:6379> sadd setkey 1 2 3 4

(integer) 4

127.0.0.1:6379> object encoding setkey

"intset"

2.1)当元素个数超过512个,内部编码变为hashtable:

127.0.0.1:6379> sadd setkey 1 2 3 4 5 6
... 512 513

(integer) 509

127.0.0.1:6379> scard setkey

(integer) 513

127.0.0.1:6379> object encoding listkey

"hashtable"

2.2)当某个元素不为整数时,内部编码也会变为hashtable:

127.0.0.1:6379> sadd setkey a

(integer) 1

127.0.0.1:6379> object encoding setkey

"hashtable"

有关集合类型的内存优化技巧将在8.3节中详细介绍。

2.5.3 使用场景

集合类型比较典型的使用场景是标签(tag)。例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。例如一个电子商务的网站会对不同标签的用户做不同类型的推荐,比如对数码产品比较感兴趣的人,在各个页面或者通过邮件的形式给他们推荐最新的数码产品,通常会为网站带来更多的利益。

下面使用集合类型实现标签功能的若干功能。

(1)给用户添加标签

sadd user:1:tags tag1 tag2 tag5

sadd user:2:tags tag2 tag3 tag5

 ...

sadd user:k:tags tag1 tag2 tag4

...

(2)给标签添加用户

sadd tag1:users user:1 user:3

sadd tag2:users user:1 user:2 user:3

...

sadd tagk:users user:1 user:2

...

用户和标签的关系维护应该在一个事务内执行,防止部分命令失败造成的数据不一致,有关如何将两个命令放在一个事务,第3章会介绍事务以及Lua的使用方法。

(3)删除用户下的标签

srem user:1:tags tag1 tag5

...

(4)删除标签下的用户

srem tag1:users user:1

srem tag5:users user:1

...

(3)和(4)也是尽量放在一个事务执行。

(5)计算用户共同感兴趣的标签

可以使用sinter命令,来计算用户共同感兴趣的标签,如下代码所示:

sinter user:1:tags user:2:tags

前面只是给出了使用Redis集合类型实现标签的基本思路,实际上一个标签系统远比这个要复杂得多,不过集合类型的应用场景通常为以下几种:

sadd = Tagging(标签)

spop/srandmember = Random item(生成随机数,比如抽奖)

sadd + sinter = Social Graph(社交需求)?

时间: 2024-11-08 23:31:37

Redis开发与运维. 2.5 集合的相关文章

Redis开发与运维. 导读

数据库技术丛书 Redis开发与运维 付磊 张翼军编著   Redis作为基于键值对的NoSQL数据库,具有高性能.丰富的数据结构.持久化.高可用.分布式等特性,同时Redis本身非常稳定,已经得到业界的广泛认可和使用.掌握Redis已经逐步成为开发和运维人员的必备技能之一. 本书关注了Redis开发运维的方方面面,尤其对于开发运维中如何提高效率.减少可能遇到的问题进行详细分析,但本书不单单介绍怎么解决这些问题,而是通过对Redis重要原理的解析,帮助开发运维人员学会找到问题的方法,以及理解背后

Redis开发与运维. 2.6 有序集合

2.6 有序集合 有序集合相对于哈希.列表.集合来说会有一点点陌生,但既然叫有序集合,那么它和集合必然有着联系,它保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序.但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据.如图2-24所示,该有序集合包含kris.mike.frank.tim.martin.tom,它们的分数分别是1.91.200.220.250.251,有序集合提供了获取指定分数和元素范围查询.计算成员排名等功能,合

Redis开发与运维. 3.2 Redis Shell

3.2 Redis Shell Redis提供了redis-cli.redis-server.redis-benchmark等Shell工具.它们虽然比较简单,但是麻雀虽小五脏俱全,有时可以很巧妙地解决一些问题. 3.2.1 redis-cli详解 第1章曾介绍过redis-cli,包括-h.-p参数,但是除了这些参数,还有很多有用的参数,要了解redis-cli的全部参数,可以执行redis-cli -help命令来进行查看,下面将对一些重要参数的含义以及使用场景进行说明. 1.?-r -r(

Redis开发与运维. 3.4 事务与Lua

3.4 事务与Lua 为了保证多条命令组合的原子性,Redis提供了简单的事务功能以及集成Lua脚本来解决这个问题.本节首先简单介绍Redis中事务的使用方法以及它的局限性,之后重点介绍Lua语言的基本使用方法,以及如何将Redis和Lua脚本进行集成,最后给出Redis管理Lua脚本的相关命令. 3.4.1 事务 熟悉关系型数据库的读者应该对事务比较了解,简单地说,事务表示一组动作,要么全部执行,要么全部不执行.例如在社交网站上用户A关注了用户B,那么需要在用户A的关注表中加入用户B,并且在用

Redis开发与运维. 2.1 预备

2.1 预备 在正式介绍5种数据结构之前,了解一下Redis的一些全局命令.数据结构和内部编码.单线程命令处理机制是十分有必要的,它们能为后面内容的学习打下一个好的基础,主要体现在两个方面:第一.Redis的命令有上百个,如果纯靠死记硬背比较困难,但是如果理解Redis的一些机制,会发现这些命令有很强的通用性.第二.Redis不是万金油,有些数据结构和命令必须在特定场景下使用,一旦使用不当可能对Redis本身或者应用本身造成致命伤害. 2.1.1 全局命令 Redis有5种数据结构,它们是键值对

Redis开发与运维. 2.7 键管理

2.7 键管理 本节将按照单个键.遍历键.数据库管理三个维度对一些通用命令进行介绍. 2.7.1 单个键管理 针对单个键的命令,前面几节已经介绍过一部分了,例如type.del.object.exists.expire等,下面将介绍剩余的几个重要命令. 1.?键重命名 rename key newkey 例如现有一个键值对,键为python,值为jedis: 127.0.0.1:6379> get python "jedis" 下面操作将键python重命名为java: 127.

Redis开发与运维. 1.1 盛赞Redis

1.1 盛赞Redis Redis是一种基于键值对(key-value)的NoSQL数据库,与很多键值对数据库不同的是,Redis中的值可以是由string(字符串).hash(哈希).list(列表).set(集合).zset(有序集合).Bitmaps(位图).HyperLogLog.GEO(地理信息定位)等多种数据结构和算法组成,因此Redis可以满足很多的应用场景,而且因为Redis会将所有数据都存放在内存中,所以它的读写性能非常惊人.不仅如此,Redis还可以将内存的数据利用快照和日志

Redis开发与运维. 1.2 Redis特性

1.2 Redis特性 Redis之所以受到如此多公司的青睐,必然有之过人之处,下面是关于Redis的8个重要特性. 1.?速度快 正常情况下,Redis执行命令的速度非常快,官方给出的数字是读写性能可以达到10万/秒,当然这也取决于机器的性能,但这里先不讨论机器性能上的差异,只分析一下是什么造就了Redis除此之快的速度,可以大致归纳为以下四点: Redis的所有数据都是存放在内存中的,表1-1是谷歌公司2009年给出的各层级硬件执行速度,所以把数据放在内存中是Redis速度快的最主要原因.

Redis开发与运维. 3.1 慢查询分析

3.1 慢查询分析 许多存储系统(例如MySQL)提供慢查询日志帮助开发和运维人员定位系统存在的慢操作.所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来,Redis也提供了类似的功能. 如图3-1所示,Redis客户端执行一条命令分为如下4个部分:   图3-1 一条客户端命令的生命周期 1)发送命令 2)命令排队 3)命令执行 4)返回结果 需要注意,慢查询只统计步骤3)的时间,所以没有慢查询并不