集合使用与内部实现原理

集合类型 (Set) 是一个无序并唯一的键值集合。

之所以说集合类型是一个无序集合,是因为它的存储顺序不会按照插入的先后顺序进行存储,如下代码所示:

127.0.0.1:6379> sadd myset v2 v1 v3 #插入数据 v2、v1、v3
(integer) 3
127.0.0.1:6379> smembers myset #查询数据
1) "v1"
2) "v3"
3) "v2"

从上面代码执行结果可以看出,myset 的存储顺序并不是以插入的先后顺序进行存储的。

集合类型和列表类型的区别如下:

  • 列表可以存储重复元素,集合只能存储非重复元素;
  • 列表是按照元素的先后顺序存储元素的,而集合则是无序方式存储元素的。

1 代码实战

集合类型的功能比列表类型丰富一些,集合类型可以用来统计多个集合的交集、错集和并集,如下代码所示。

from redis import StrictRedisredis_cli = StrictRedis(host="xx", port=xx, password="xx", db=xx, decode_responses=True)# 1)添加一个或多个元素
redis_cli.sadd("set", "1", "3", 4, 8, 9, 3)  # “3”和 3 被看做是相同的# 2)查询集合所有元素
mem = redis_cli.smembers("set")
print(mem)# 3)查询集合的成员数量
num = redis_cli.scard("set")
print(num)# 4)查询集合中是否包含某个元素
is_exists = redis_cli.sismember("set", "10")
print(is_exists)# 5)从一个集合中移动一个元素到另一个集合
redis_cli.sadd("set2", 10, 11, 12)redis_cli.smove("set", "set2", "3")
print(redis_cli.smembers("set2"))
print(redis_cli.smembers("set"))# 6)移除并返回集合中的一个随机元素
random_key = redis_cli.spop("set2")
print(random_key)
print(redis_cli.smembers("set2"))# 7)随机返回集合中指定数量的元素列表
mems = redis_cli.srandmember("set2", 2)
print(mems)# 8)移除集合中一个或多个元素(注意:使用 srem 指令,不存在的元素将会被忽略。)
redis_cli.srem("set", "1", "8")
print(redis_cli.smembers("set"))# 9)返回一个集合或多个集合的交集
redis_cli.sadd("set3", 1, 3, 5, 7, 8, 9)
redis_cli.sadd("set4", 2, 3, 5, 6, 7)inter = redis_cli.sinter("set3", "set4")
print(inter)# 10)把集合的交集复制到新的集合中 (从以下代码可以看出,我们把集合set3和集合set4的交集元素复制到了新的集合des中,但 交集元素并不会从原有集合中移除。)
redis_cli.sinterstore("des", "set3", "set4")
des_mems = redis_cli.smembers("des")
print("des_mems >>>>>>>>>>>>>>>", des_mems)# 11)查询一个或多个集合的并集
union = redis_cli.sunion("set3", "set4")
print(union)# 12)把一个或多个集合的并集复制到新集合中(注意:只是把一个或多个集合的并集复制到新集合中,并不会在原集合中删除复制的元素。)
uni_des = redis_cli.sunionstore("uni", "set3", "set4")
print(redis_cli.smembers("uni"))# 13)查询一个或多个集合的错集 (查询差集是以第一个集合为基准,去除掉与第二个,第三个。。。集合相同的元素,剩下的就是差集)
redis_cli.sadd("set8", 2, 5, 7, 6, 9)
redis_cli.sadd("set9", 1, 3, 6, 8)
redis_cli.sadd("set10", 4, 7, 99)
set6_diff_set5 = redis_cli.sdiff("set8", "set9", "set10")
set5_diff_set6 = redis_cli.sdiff("set9", "set8")
print(set6_diff_set5)
print(set5_diff_set6)# 14)把一个或多个集合的错集复制到新集合
redis_cli.sdiffstore("diff", "set8", "set9")
print(redis_cli.smembers("diff"))

2 内部实现

集合类型是由 intset (整数集合) 或 hashtable (普通哈希表) 组成的。当集合类型以 hashtable 存储时,哈希表的 key 为要插入的元素值,而哈希表的 value 则为 Null,如下图所示:

当集合中所有的值都为整数时,Redis 会使用 intset 结构来存储,如下代码所示:

127.0.0.1:6379> sadd myset 1 9 3 -2
(integer) 4
127.0.0.1:6379> object encoding myset
"intset"

从上面代码可以看出,当所有元素都为整数时,集合会以 intset 结构进行(数据)存储。 当发生以下两种情况时,会导致集合类型使用 hashtable 而非 intset 存储: 1)当元素的个数超过一定数量时,默认是 512 个,该值可通过命令 set-max-intset-entries xxx 来配置。 2)当元素为非整数时,集合将会使用 hashtable 来存储,如下代码所示:

127.0.0.1:6379> sadd myht "redis" "db"
(integer) 2
127.0.0.1:6379> object encoding myht
"hashtable"

从上面代码可以看出,当元素为非整数时,集合会使用 hashtable 进行存储

3 源码解析

集合源码在 t_set.c 文件中,核心源码如下:

/* * 添加元素到集合* 如果当前值已经存在,则返回 0 不作任何处理,否则就添加该元素,并返回 1。*/
int setTypeAdd(robj *subject, sds value) {long long llval;if (subject->encoding == OBJ_ENCODING_HT) { // 字典类型dict *ht = subject->ptr;dictEntry *de = dictAddRaw(ht,value,NULL);if (de) {// 把 value 作为字典到 key,将 Null 作为字典到 value,将元素存入到字典dictSetKey(ht,de,sdsdup(value));dictSetVal(ht,de,NULL);return 1;}} else if (subject->encoding == OBJ_ENCODING_INTSET) { // inset 数据类型if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {uint8_t success = 0;subject->ptr = intsetAdd(subject->ptr,llval,&success);if (success) {// 超过 inset 的最大存储数量,则使用字典类型存储if (intsetLen(subject->ptr) > server.set_max_intset_entries)setTypeConvert(subject,OBJ_ENCODING_HT);return 1;}} else {// 转化为整数类型失败,使用字典类型存储setTypeConvert(subject,OBJ_ENCODING_HT);serverAssert(dictAdd(subject->ptr,sdsdup(value),NULL) == DICT_OK);return 1;}} else {// 未知编码(类型)serverPanic("Unknown set encoding");}return 0;
}

以上这些代码验证了,我们上面所说的内容,当元素都为整数并且元素的个数没有到达设置的最大值时,键值的存储使用的是 intset 的数据结构,反之到元素超过了一定的范围,又或者是存储的元素为非整数时,集合会选择使用 hashtable 的数据结构进行存储。

4 使用场景

集合类型的经典使用场景如下:

  • 微博关注我的人和我关注的人都适合用集合存储,可以保证人员不会重复;
  • 中奖人信息也适合用集合类型存储,这样可以保证一个人不会重复中奖。

5 小结

通过本文我们知道了,集合类型是由整数集合 (intset) 或者是哈希表 (hashtable) 组成的,集合类型比较适合用来数据去重和保障数据的唯一性,除此之外,集合类型还可以用来统计多个集合的交集、错集和并集。当我们存储的数据是无序并且需要去重的情况下,比较适合使用集合类型进行存储。

python 操作redis之五(集合)相关推荐

  1. Python 操作redis有序集合

    #coding:utf8 import redis r =redis.Redis(host="23.226.74.190",port=63279,password="66 ...

  2. python 操作redis之——有序集合(sorted set) (七)

    #coding:utf8 import redis r =redis.Redis(host="23.226.74.190",port=63279,password="66 ...

  3. python操作redis set_Python操作redis学习系列之(集合)set,redis set详解 (六)

    #-*- coding: utf-8 -*- importredis r= redis.Redis(host="126.56.74.190",port=639,password=& ...

  4. python 操作redis之——HyperLogLog (八)

    #coding:utf8 import redis # python 操作redis之--HyperLogLog r =redis.Redis(host="33.23.724.12190&q ...

  5. python操作redis用法详解

    1.简单介绍 安装redis pip install redis Redis是一个基于内存的高效的键值型非关系型数据库,存取效率极高,而且支持多种存储数据结构,使用也非常简单.本节中,我们就来介绍一下 ...

  6. Python 操作 redis

    官网命令(英文)列表:http://redis.io/commands Redis 教程:http://www.redis.net.cn/tutorial/3501.html Redis 命令参考:h ...

  7. Python 操作redis 常用方法

    Python 操作redis 1.字符串 #!/usr/bin/env python # -*- coding:utf-8 -*-import redis# python 操作str class Te ...

  8. Python操作Redis和Memcached

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

  9. Python操作Redis

    [外链图片转存失败,源站可能有防盗在这里插入!链机制,建描述]议将图片上https://传(imblog.csdnimg.cn/201912281v5vUa18517.png?x-oss-proces ...

最新文章

  1. 数据结构和算法,到底有多重要?
  2. python和R文件IO操作对比及dataframe创建方式对比:read_csv、to_csv、write.csv、 data.frame、pd.DataFrame
  3. hssfcolor 不建议使用_POI导出Excel经典实现
  4. Boost:自定义双端队列的测试程序
  5. Google走了 站长应该更相信百度
  6. linux 查看hdfs文件,Hadoop之HDFS文件操作
  7. java xml binding_JAXB(Java Architecture for XML Binding)
  8. spring容器_Spring 容器的启动过程探秘
  9. python里order什么意思_python中OrderedDict的使用方法详解
  10. python工资多少钱一个月-苏州工业园区学编程大概多少钱一个月
  11. phpredis中文手册
  12. 长期主义:永远做你余生中最重要的事!
  13. OCiOS开发:汉字转拼音
  14. php strtok函数,strtok函数的用法是什么
  15. xp 架设网站服务器,WinXP如何设置iis服务器?WinXP iis服务器设置教程
  16. 《甄嬛传》影评(整理)
  17. Python Challenge-39 有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。
  18. 干货 | 超全整理|Python 操作 Excel 库 xlwings 常用操作详解!
  19. 猿创征文|云原生|kubernetes学习之多账户管理--权限精细化分配放啊(两种方式-sa和用户)
  20. 荐:Java常见设计模式

热门文章

  1. 简单制作视频画面水平镜像播放特效
  2. 对话亚洲高校首个博士论文奖-裘捷中丨KDD2022
  3. 解决打开idea时候的Unmapped Spring configuration files found. Please configure Spring facet.警告
  4. SOIC8E (EXPOSED PAD)
  5. 【121期】面试官:什么是熔断?什么是服务降级?
  6. 乐动机器人 2D DTOF激光雷达 LD06、LD19驱动包开源仓库分享
  7. 上海尤劲恩AI视觉检测为工业制造赋能,帮助企业实现智造升级
  8. 《百面机器学习》学习笔试之模型评估(第2章)
  9. 我设计开发的第一个产品发布了,微信小程序“集美装修效果图“
  10. html表格翻页简单,利用jQuery实现一个简单的表格上下翻页效果