当前位置:首页 > 芯闻号 > 充电吧
[导读]Redis 中的 5 种数据结构精讲(2)

命令

下面我们还是和学习其它数据类型一样,我们还是先学习一下 Redis 列表类型的命令。

 

1.添加操作

  • 从右边插入元素



1

rpush key value [value ...]

我们看 rpush 命令在插入时,是有返回值的,返回值的数量就是当前列表中所有元素的个数。

我们也可以用下面的命令从左到右获取当前列表中的所有的元素,也就是如上图所示中那样。


1

lrange 0 -1


  • 从左边插入元素



1

lpush key value [value ...]

lpush 命令的返回值及用法和 rpush 命令一样。通过上面的事例证明了我们前面说的,rpush 命令和 lpush 命令的返回值并不是当前插入元素的个数,而是当前 key 中全部元素的个数,因为当前 key 中已经有了 3 个元素,所以我们在执行插入命令时,返回的就是 6 而不是 3,。

  • 向某个元素前或者后插入元素



1

linsert key BEFORE|AFTER pivot value

linsert 命令在执行的时候首先会从当前列表中查找到 pivot 元素,其次再将这个新元素插入到 pivot 元素的前面或者后面。并且我们通过上图可以知道 linsert 命令在执行成功后也是会有返回值的,返回的结果就是当前列表中元素的个数。

 

2.查找

  • 获取指定范围内的元素列表



1

lrange key start stop

lrange 命令会获取列表中指定索引范围的所有元素。

通过索引获取列表主要有两个特点:

  • 索引下标从左到右分别是 0 到 N-1,从右到左是 -1 到 -N。

  • lrange 命令中的 stop 参数在执行时会包括当前元素,并不是所有的语言都是这样的。我们要获取列表中前两个元素则可以如下图所示:

  • 获取列表中指定索引下标的元素



1

lindex key index

  • 获取列表长度



1

llen key

 

3.删除

  • 从列表左侧弹出元素



1

lpop key

lpop 命令执行成功后会返回当前被删除的元素名称。

  • 从列表右侧弹出元素



1

rpop key

rpop 命令和 lpop 命令的使用方式一样。

  • 删除指定元素



1

lrem key count value

lrem 命令会将列表中等于 value 的元素删除掉,并且会根据 count 参数来决定删除 value 的元素个数。

下面我们看一下 count 参数的使用说明:

count > 0:表示从左到右,最多删除 count 个元素。也就是如下图所示:

我们看上图中的命令中,虽然我们将 count 参数指定的是 5,将 value 参数指定的是 2,但由于当前列表中只有一个 2,所以,当前 lrem 命令最多只能删除 1 个元素,并且 lrem 命令也是有返回值的,也就是当前成功删除元素的个数。

count < 0:从右到左,最多删除 count 个元素。

count = 0:删除所有元素。

  • 按照索引范围修剪列表



1

ltrim key start stop

ltrim 命令会直接保留 start 索引到 stop 索引的之间的元素,并包括当前元素,而之外的元素则都会删除掉,所以该命令也叫修剪列表。

并且有一点要注意,ltrim 命令不会返回当前的列表中元素的个数,而是返回改命令是否成功的状态。

 

4.修改

  • 修改指定索引下标的元素



1

lset key index value

 

5.阻塞操作



1

2

blpop key [key ...] timeout

brpop key [key ...] timeout

blpop 和 brpop 命令是 lpop 和 rpop 命令的阻塞版本,它们除了弹出方向不同以外,使用方法基本相同。

  • key [key …]:可以指定多个列表的键。

  • timeout:阻塞时间(单位:秒)

下面我们看一下该命令的详细使用。

列表为空:如果 timeout=3,则表示客户端等待 3 秒后才能返回结果,如果 timeout=0,则表示客户端会一直等待,也就是会阻塞。

由于我期间向列表中插入了元素,否则上述命令将一直阻塞下去。

列表不为空:如果 timeout=0,并且列表不为空时,则 blpop 和 brpop 命令会立即返回结果,不会阻塞。

下面我们看一下 blpop 和 brpop 命令的注意事项。

如果使用 blpop 和 brpop 命令指定多个键时,blpop 和 brpop 命令会从左到右遍历键,并且一旦有一个键能返回元素,则客户端会立即返回。

当列表为空时,上述命令会阻塞,如果向上述中的任何一个键中插入元素,则上述命令会直接返回该键的元素。

如果多个客户端都对同一个键执行 blpop 或者 brpop 命令,则最先执行该命令的客户端会获取到该键的元素。

我同时启动了 3 个客户端,因为当前列表为空,所以上述命令执行后会阻塞。如果此时我向该列表中插入元素,则只有第一个客户端会有返回结果,因为第一个客户端是第一个执行上述命令的。

 

时间复杂度

下面我们看一下列表中命令的相关时间复杂度。

操作类型命令时间复杂度
添加rpush key value [value …]O(k),k是元素的个数
添加lpush key value [value …]O(k),k是元素的个数
添加linsert key BEFORE/AFTER pivot valueO(n),n是pivot距离列表头或者尾的距离
查找lrange key start stopO(s + n),s是start偏移量,n是start到stop的范围
查找lindex key indexO(n),n是索引的偏移量
查找llen keyO(1)
删除lpop keyO(1)
删除rpop keyO(1)
删除lrem key count valueO(n),n是列表长度
删除ltrim key start stopO(n),n是要裁剪的元素个数
修改lset key index valueO(n),n是索引的偏移量
阻塞操作blpop/brpop key [key …] timeoutO(1)

 

内部编码

列表中的内部编码有两种,它们分别是:

  • ziplist(压缩列表):当列表中元素个数小于 512(默认)个,并且列表中每个元素的值都小于 64(默认)个字节时,Redis 会选择用 ziplist 来作为列表的内部实现以减少内存的使用。当然上述默认值也可以通过相关参数修改:list-max-ziplist-entried(元素个数)、list-max-ziplist-value(元素值)。

  • linkedlist(链表):当列表类型无法满足 ziplist 条件时,Redis 会选择用 linkedlist 作为列表的内部实现。

 

集合类型

Redis 中的集合类型,也就是 set。在 Redis 中 set 也是可以保存多个字符串的,经常有人会分不清 list 与 set,下面我们重点介绍一下它们之间的不同:

  • set 中的元素是不可以重复的,而 list 是可以保存重复元素的。

  • set 中的元素是无序的,而 list 中的元素是有序的。

  • set 中的元素不能通过索引下标获取元素,而 list 中的元素则可以通过索引下标获取元素。

  • 除此之外 set 还支持更高级的功能,例如多个 set 取交集、并集、差集等。

 

命令

下面我们介绍一下 set 中的相关命令。

 

1.集合内操作

  • 添加元素



1

sadd key member [member ...]

sadd 命令也是有返回值的,它的返回值就是当前执行 sadd 命令成功添加元素的个数,因为 set 中不能保存重复元素,所以在执行 sadd setkey c d 命令时,返回的是 1,而不是 2。因为元素 c 已经成功保存到 set 中,不能再保存了,只能将 d 保存到 set 中。

  • 删除元素



1

srem key member [member ...]

srem 命令和 sadd 命令一样也是有返回值的,返回值就是当前删除元素的个数。

  • 计算元素个数



1

scard key

scard 命令的时间复杂度为O(1),scard 命令不会遍历 set 中的所有元素,而是直接使用 Redis 中的内部变量。

  • 判读元素是否在集合中



1

sismember key member

sismember 命令也有返回值,如果返回值为1则表示当前元素在当前 set 中,如果返回 0 则表示当前元素不在 set 中。

  • 随机从 set 中返回指定个数元素



1

srandmember key [count]

srandmember 命令中有一个可选参数 count,count 参数指的是返回元素的个数,如果当前 set 中的元素个数小于 count,则 srandmember 命令返回当前 set 中的所有元素,如果 count 参数等于 0,则不返回任何数据,如果 count 参数小于 0,则随机返回当前 count 个数的元素。

  • 从集合中随机弹出元素



1

spop key [count]

spop 命令也是随机从 set 中弹出元素,并且也支持 count 可选参数,但有一点和 srandmember 命令不同。spop 命令在随机弹出元素之后,会将弹出的元素从 set 中删除。

  • 获取所有元素



1

smembers key

smembers 命令虽然能获取当前 set 中所有的元素,但返回元素的顺序与 sadd 添加元素的顺序不一定相同,这也就是前面提到过的保存在 set 中的元素是无序的。

 

2.集合间操作

  • 集合的交集



1

sinter key [key ...]

  • 集合的并集



1

sunion key [key ...]

  • 集合的差集



1

sdiff key [key ...]

  • 将集合的交集、并集、差集的结果保存



1

2

3

sinterstore destination key [key ...]

sunionstore destination key [key ...]

sdiffstore destination key [key ...]

为什么 Redis 要提供 sinterstore、sunionstore、sdiffstore 命令来将集合的交集、并集、差集的结果保存起来呢?这是因为 Redis 在进行上述比较时,会比较耗费时间,所以为了提高性能可以将交集、并集、差集的结果提前保存起来,这样在需要使用时,可以直接通过 smembers 命令获取。

 

时间复杂度

下面我们看一下 set 中相关命令的时间复杂度。

命令时间复杂度
sadd key member [member …]O(k),k是元素的个数
srem key member [member …]O(k),k是元素的个数
scard keyO(1)
sismember key memberO(1)
srandmember key [count]O(count)
spop key [count]O(1)
smembers keyO(n),n是元素的总数
sinter key [key …]O(m * k),k是多个集合中元素最少的个数,m是键个数
sunion key [key …]O(k),k是多个元素个数和
sdiff key [key …]O(k),k是多个元素个数和
sinterstore destination key [key …]O(m * k),k是多个集合中元素最少的个数,m是键个数
sunionstore destination key [key …]O(k),k是多个元素个数和
sdiffstore destination key [key …]O(k),k是多个元素个数和

 

内部编码

  • intset(整数集合):当集合中的元素都是整数,并且集合中的元素个数小于 512 个时,Redis 会选用 intset 作为底层内部实现。

  • hashtable(哈希表):当上述条件不满足时,Redis 会采用 hashtable 作为底层实现。

备注:我们可以通过 set-max-intset-entries 参数来设置上述中的默认参数。

下面我们看一下具体的事例,来验证我们上面提到的内部编码。

当元素个数较少并且都是整数时,内部编码为 intset。

当元素不全是整数时,内部编码为 hashtable。

当元素个数超过 512 个时,内部编码为 hashtable。


1

2

3

4

5

6

7

8

9

10

11

import redis

 

r = redis.Redis(host='127.0.0.1', port=6379)

    if r.object('encoding', 'setkey') != None:    

    print('Key为【setkey】的字节编码为【%s】' % r.object('encoding', 'setkey').decode('utf-8'))

for i in range(1, 600):

    r.sadd('setkey', i)

if r.object('encoding', 'setkey') != None:

    print('Key为【setkey】的字节编码为【%s】' % r.object('encoding', 'setkey').decode('utf-8'))

Key为【setkey】的字节编码为【intset】

Key为【setkey】的字节编码为【hashtable】

 

有序集合类型

看名字我们就知道,有序集合也是一种集合,并且这个集合还是有序的。列表也是有序的,那它和有序集合又有什么不同呢?有序集合的有序和列表的有序是不同的。列表中的有序指的的是插入元素的顺序和查询元素的顺序相同,而有序集合中的有序指的是它会为每个元素设置一个分数(score),而查询时可以通过分数计算元素的排名,然后再返回结果。因为有序集合也是集合类型,所以有序集合中也是不插入重复元素的,但在有序集合中分数则是可以重复,那如果在有序集合中有多个元素的分数是相同的,这些重复元素的排名是怎么计算的呢?后边我们再做详细说明。

下面先看一下列表、集合、有序集合三种数据类型之间的区别:

数据结构是否允许重复元素是否有序有序实现方式应用场景
列表索引下标时间轴、消息队列
集合标签、社交
有序集合分数排行榜、社交

 

命令

 

1.集合内操作

  • 添加元素



1

zadd key [NX|XX] [CH] [INCR] score member [score member ...]

zadd 命令也是有返回值的,返回值就是当前 zadd 命令成功添加元素的个数。zadd 命令有很多选填参数:

  • nx: 元素必须不存在时,才可以设置成功。

  • xx: 元素必须存在时,才可以设置成功。

  • ch: 返回此命令执行完成后,有序集合元素和分数发生变化的个数

  • incr: 对 score 做增加。

备注:由于有序集合相比集合提供了排序字段,正是因为如此也付出了相应的代价,sadd 的时间复杂度为 O(1),而 zadd 的时间复杂度为O(log(n))。

  • 计算成员个数



1

zcard key

  • 计算某个成员的分数



1

zscore key member

在使用 zscore 命令时,如果 key 不存在,或者元素不存在时,该命令返回的都是(nil)。

  • 计算成员的排名



1

2

zrank key member

zrevrank key member

zrank 命令是从分数低到高排名,而 zrevrank 命令则恰恰相反,从高到低排名。有一点要特别注意, zrank 和 zrevrank 命令与 zscore 是命令不同的,前者通过分数计算出最后的排名,而后者则是直接返回当前元素的分数。

  • 删除元素



1

zrem key member [member ...]

返回的结果为成功删除元素的个数,因为 zrem 命令是支持批量删除的。

  • 增加元素分数



1

zincrby key increment member

虽然 zincrby 命令是增加元素分数的,但我们也可以指定负数,这样当前元素的分数,则会相减。

  • 返回指定排名范围的元素



1

2

zrange key start stop [WITHSCORES]

zrevrange key start stop [WITHSCORES]

zrange 命令是通过分数从低到高返回数据,而 zrevrange 命令是通过分数从高到低返回数据。如果执行命令时添加了 WITHSCORES 可选参数,则返回数据时会返回当前元素的分数。

  • 返回指定分数范围的元素



1

2

zrangebyscore key min max [WITHSCORES] [LIMIT offset count]

zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]

min 和 max 参数还支持开区间(小括号)和闭区间(中括号),同时我们还可以用 -inf 和 +inf 参数代表无限小和无限大。

  • 返回指定分数范围元素个数



1

zcount key min max

  • 删除指定排名内的升序元素



1

zremrangebyrank key start stop

  • 删除指定分数范围元素



1

zremrangebyscore key min max

 

2.集合间操作

  • 交集



1

zinterstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]

zinterstore 命令参数比较多:

  • destination:将交集的计算结果,保存到这个键中。

  • numkeys:需要做交集计算键的个数。

  • key [key …]:需要做交集计算的键。

  • WEIGHTS weight:每个键的权重,在做交集计算时,每个键中的分数值都会乘以这个权重,默认每个键的权重为 1。

  • AGGREGATE SUM|MIN|MAX:计算成员交集后,分值可以按照 sum(和)、min(最小值)、max(最大值)做汇总,默认值为  sum。

下面我们将权重设置为 0.5,这样当计算交集后,有序集合中的元素分数将都会减半,并且使用 max 参数汇总。

  • 并集



1

zunionstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]

zunionstore 命令的相关参数和 zinterstore 命令相同。

 

时间复杂度

命令时间复杂度
zadd key [NX/XX] [CH] [INCR] score member [score member …]O(k*log(n)),k是添加元素的个数,n是当前有序集合元素个数
zcard keyO(1)
zscore key memberO(1)
zrank key memberO(log(n)),n是当前有序集合元素个数
zrevrank key memberO(log(n)),n是当前有序集合元素个数
zrem key member [member …]O(k*log(n)),k是删除元素的个数,n是当前有序集合元素个数
zincrby key increment memberO(log(n)),n是当前有序集合元素个数
zrange key start stop [WITHSCORES]O(log(n) + k),k是要获取的元素个数,n是当前有序集合个数
zrevrange key start stop [WITHSCORES]O(log(n) + k),k是要获取的元素个数,n是当前有序集合个数
zrangebyscore key min max [WITHSCORES] [LIMIT offset count]O(log(n) + k),k是要获取的元素个数,n是当前有序集合个数
zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]O(log(n) + k),k是要获取的元素个数,n是当前有序集合个数
zcount key min maxO(log(n)),n是当前有序集合元素个数
zremrangebyrank key start stopO(log(n) + k),k是要删除的元素个数,n是当前有序集合个数
zremrangebyscore key min maxO(log(n) + k),k是要删除的元素个数,n是当前有序集合个数
zinterstore destination numkeys key [key …] [WEIGHTS weight] [AGGREGATE SUM/MIN/MAX]O(n k) + O(m log(m)),n是元素元素最小的有序集合元素个数,k是有序集合个数,m是结果集中元素个数
zunionstore destination numkeys key [key …] [WEIGHTS weight] [AGGREGATE SUM/MIN/MAX]O(n) + O(m * log(m)),n是所有有序集合元素个数和,m是结果集中元素个数。

 

内部编码

有序集合类型的内部编码有两种,它们分别是:

  • ziplist(压缩列表):当有序集合的元素个数小于 128 个(默认设置),同时每个元素的值都小于 64 字节(默认设置),Redis 会采用 ziplist 作为有序集合的内部实现。

  • skiplist(跳跃表):当上述条件不满足时,Redis 会采用 skiplist 作为内部编码。

备注:上述中的默认值,也可以通过以下参数设置:zset-max-ziplist-entries 和 zset-max-ziplist-value。

下面我们用以下示例来验证上述结论。

当元素个数比较少,并且每个元素也比较小时,内部编码为 ziplist:

当元素个数超过 128 时,内部编码为 skiplist。下面我们将采用 python 动态创建128个元素,下面为源码:


1

2

3

4

5

6

7

8

9

10

11

import redis

 

r = redis.Redis(host='127.0.0.1', port=6379)

if r.object('encoding', 'zsetkey') != None:

    print('Key为【zsetkey】的字节编码为【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))

for i in range(1, 600):

    r.zadd('zsetkey',i,1)

if r.object('encoding', 'zsetkey') != None:

    print('Key为【zsetkey】的字节编码为【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))

Key为【zsetkey】的字节编码为【ziplist】

Key为【zsetkey】的字节编码为【skiplist】

当有序集合中有任何一个元素大于 64 个字节时,内部编码为 skiplist。


1

2

3

4

5

6

7

8

9

10

11

12

import redis

 

r = redis.Redis(host='127.0.0.1', port=6379)

if r.object('encoding', 'zsetkey') != None:

    print('Key为【zsetkey】的字节编码为【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))

value = ''

for i in range(1, 600):

    value += str(i)

r.zadd('zsetkey',value,1)

if r.object('encoding', 'zsetkey') != None:

    print('Key为【zsetkey】的字节编码为【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))

Key为【zsetkey】的字节编码为【skiplist】


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭