iga肾病患者能吃鸡肉吗

Linux Redis

Redis 中的 5 种数据结构精讲(2)

2019-01-22
92次浏览

命令

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

?

1.添?#30828;?#20316;

  • 从右边插入元素



1

rpush?key?value?[value?...]

31549cdc4aa07d086553492694c971ee.png

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

我们也可?#26434;?#19979;面的命令从左到?#19968;?#21462;当前列表中的所有的元素,也就是如上图所示中那样。


1

lrange?0?-1


  • 从左边插入元素



1

lpush?key?value?[value?...]

72d4e86bdd1f81858b8211b389c5174d.png

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

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



1

linsert?key?BEFORE|AFTER?pivot?value

015187d7d49cc89ef5f99512d3a6455f.png

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

?

2.查找

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



1

lrange?key?start?stop

474524edd3c452af6a8dd609d280ad8c.png

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

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

  • 索引下标从左到右分别是 0 到 N-1,从?#19994;?#24038;是 -1 到 -N。

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

bdccbb4b87f8e46661a2fc45089ffc8d.png

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



1

lindex?key?index

4c4f7c5e0347f01d84b7c810eb016d2c.png

  • 获取列表长度



1

llen?key

062aa1ecbbb461f0fad394d83af4b6b1.png

?

3.删除

  • 从列表左侧弹出元素



1

lpop?key

c6f66a25a4596ccd907b28ac5d71ad61.png

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

  • 从列表右侧弹出元素



1

rpop?key

bc356eafeace8a51c92a28a4143ec7af.png

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

  • 删除指定元素



1

lrem?key?count?value

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

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

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

f5c7100c526bee912803ef6d50cde68c.png

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

2949da72e50327cecaa7a09ad1e5f2f9.png

count < 0:从?#19994;?#24038;,最多删除 count 个元素。

afdb2e49ce743e86b017e4410046b646.png

count = 0:删除所有元素。

74df93d41e17354015d289e0c8656398.png

  • 按照索引范围修剪列表



1

ltrim?key?start?stop

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

f8ca0f5756da5535cafb219ec17989a4.png

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

?

4.修改

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



1

lset?key?index?value

67c400e678459565120f1e4d282a53af.png

?

5.阻塞操作



1

2

blpop?key?[key?...]?timeout

brpop?key?[key?...]?timeout

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

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

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

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

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

9c54f738c0af51fb413a3989255bd9e7.png

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

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

40cde915c1b054294c650b89890b1b15.png

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

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

2a6c7c0126fd35db013ca276b2c99785.png

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

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

6590874dbcb021ca5d030011d26a91e6.png

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

02244756b88a2467de2738e9e523c8d6.png

b238364336f7aa1883555a4040773491.png

?

时间复杂度

下面我们看一下列表中命令的相关时间复杂?#21462;?/p>

操作类型命令时间复杂度
添加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 来作为列表的内部实现以减少内存的使用。当然上述默认值也可以通过相关参数修?#27169;簂ist-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 取交集、并集、差集?#21462;?/p>

?

命令

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

?

1.集合内操作

  • 添加元素



1

sadd?key?member?[member?...]

f97d3718b1138ffaebe4e1dde241a392.png

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

  • 删除元素



1

srem?key?member?[member?...]

d116239ca08aa5ad2ca3e2d91afe145d.png

srem 命令和 sadd 命令一样也是有返回值的,返回值就是当?#21543;?#38500;元素的个数。

  • 计算元素个数



1

scard?key

3d4caa6d2fd620822e51d7945bdfa886.png

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

  • 判读元素是否在集合中



1

sismember?key?member

d7e372e64f098629eab504d909277e2f.png

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

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



1

srandmember?key?[count]

79c76e9059061c38dbbf1e79a122abfd.png

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

  • 从集合中随机弹出元素



1

spop?key?[count]

99dc4921ccae808c0fca3a287087cdf3.png

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

  • 获取所有元素



1

smembers?key

4b232525b1456ecc47a4299b049c557a.png

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

?

2.集合间操作

  • 集合的交集



1

sinter?key?[key?...]

d04d0f29a3101b001ea5ede53644737b.png

  • 集合的并集



1

sunion?key?[key?...]

1245c1e7becf3df6f25a94f013fdd2a2.png

  • 集合的差集



1

sdiff?key?[key?...]

a67781da30221ce099c9f215fc74c69a.png

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



1

2

3

sinterstore?destination?key?[key?...]

sunionstore?destination?key?[key?...]

sdiffstore?destination?key?[key?...]

d18158a6a6db56c37ac69c6f9d968e96.png

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

?

时间复杂度

下面我们看一下 set 中相关命令的时间复杂?#21462;?/p>

命令时间复杂度
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。

032ce38a47d910e17c63f34f0315de26.png

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

3b7537ab62a1d9e09b45374a6038e320.png

当元素个数超过 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】

?

有序集合类型

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

下面先看一下列表、集合、有序集?#20808;?#31181;数据类型之间的区别:

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

?

命令

?

1.集合内操作

  • 添加元素



1

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

60ccc27eea0596ddc23b30551095d1d8.png

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

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

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

  • ch: 返回此命令执?#22411;?#25104;后,有序集?#26174;?#32032;和分数发生变化的个数

  • incr: 对 score 做增加。

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

  • 计算成员个数



1

zcard?key

2e6cc22628e34c27dd3de164ec804786.png

  • 计算某个成员的分数



1

zscore?key?member

1423752d151499452622621ba252db47.png

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

  • 计算成员的排名



1

2

zrank?key?member

zrevrank?key?member

80764823270bb47ffd23fb316c457408.png

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

  • 删除元素



1

zrem?key?member?[member?...]

c550abf5ffe862b57098ca8d75fde4c0.png

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

  • 增加元素分数



1

zincrby?key?increment?member

e7167c2e45e4477bc59e12bb8aa6e238.png

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

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



1

2

zrange?key?start?stop?[WITHSCORES]

zrevrange?key?start?stop?[WITHSCORES]

9f62d98bb7f227031bc97c9bc126f453.png

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

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



1

2

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

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

2340562372478164c677fdcdf54fd4e1.png

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

74112fd7bc941f5327ba07a11bad199e.png

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



1

zcount?key?min?max

0c6cee2383de790299aa69bcac8a3deb.png

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



1

zremrangebyrank?key?start?stop

47daedbac97a44de1900d5e8cc5dee69.png

  • 删除指定分数范围元素



1

zremrangebyscore?key?min?max

450994dc892494011bde9fe06d29587a.png

?

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:计算成员交集后,?#31181;?#21487;以按照 sum(和)、min(最小值)、max(最大值)做汇总,默认值为? sum。

ac15f280cb2ae2d9208d4d292fc6cab1.png

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

7364fd999fb8e4984bdcd7bf751f3d21.png

  • 并集



1

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

78686fc08428a438b20a0820ba816e1b.png

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

?

时间复杂度

命令时间复杂度
zadd key [NX/XX] [CH] [INCR] score member [score member …]O(k*log(n)),k是添加元素的个数,n是当前有序集?#26174;?#32032;个数
zcard keyO(1)
zscore key memberO(1)
zrank key memberO(log(n)),n是当前有序集?#26174;?#32032;个数
zrevrank key memberO(log(n)),n是当前有序集?#26174;?#32032;个数
zrem key member [member …]O(k*log(n)),k是删除元素的个数,n是当前有序集?#26174;?#32032;个数
zincrby key increment memberO(log(n)),n是当前有序集?#26174;?#32032;个数
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是当前有序集?#26174;?#32032;个数
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是元素元素最小的有序集?#26174;?#32032;个数,k是有序集合个数,m是结果集中元素个数
zunionstore destination numkeys key [key …] [WEIGHTS weight] [AGGREGATE SUM/MIN/MAX]O(n) + O(m * log(m)),n是所有有序集?#26174;?#32032;个数和,m是结果集中元素个数。

?

内部编码

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

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

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

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

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

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

e931308682681fbe62180e41ba7a7dac.png

当元素个数超过 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】

当有序集合中有任?#25105;?#20010;元素大于 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】

444a9a4b6f2bb75f08572f25d7d8d534.png


我要点评

iga肾病患者能吃鸡肉吗
有分分时时彩的软件 海王捕鱼刷金币软件 重庆时时彩2018官方 新疆时时视频 黑龙江时时开奖结果查询结果 广东时时任选2 重庆时时彩走势图教学 pk走势技巧 江西时时计划软件下载 中国福彩网3d开奖