直接基于set将系统里需要去重的数据扔进去,自动就给去重了,如果你需要对一些数据进行快速的全局去重,你当然也可以基于JVM内存里的HashSet进行去重,但若你的某个系统部署在多台机器呢?就需要基于redis进行全局的set去重 。
可基于set玩交集、并集、差集操作,比如交集:
- 把两个人的粉丝列表整一个交集,看看俩人的共同好友
- 把两个大v的粉丝都放在两个set中,对两个set做交集
- 记录唯一的事物
比如想知道访问某个博客的IP地址,不要重复的IP,这种情况只需要在每次处理一个请求时简单的使用SADD命令就可以了,可确保不会插入重复IP - 表示关系
你可以使用Redis创建一个标签系统,每个标签使用一个Set表示 。然后你可以使用SADD命令把具有特定标签的所有对象的所有ID放在表示这个标签的Set中如果你想要知道同时拥有三个不同标签的对象,那么使用SINTER - 可使用SPOP 或 SRANDMEMBER 命令从集合中随机的提取元素 。
key=150value=https://www.isolves.com/it/sjk/Redis/2021-11-01/{“id”: 150,“name”: “zhangsan”,“age”: 20}
hash类的数据结构,主要存放一些对象,把一些简单的对象给缓存起来,后续操作的时候,可直接仅修改该对象中的某字段的值 。value=https://www.isolves.com/it/sjk/Redis/2021-11-01/{“id”: 150,“name”: “zhangsan”,“age”: 21}
因为Redis本身是一个K.V存储结构,Hash结构可理解为subkey - subvalue这里面的subkey - subvalue只能是
- 整型
- 浮点型
- 字符串
内存数据结构hash有HashTable 和 ziplist 两种实现 。对于数据量较小的hash,使用ziplist 实现 。
HashTable 实现HashTable在Redis 中分为3 层,自底向上分别是:
- dictEntry:管理一个field - value 对,保留同一桶中相邻元素的指针,以此维护Hash 桶中的内部链
- dictht:维护Hash表的所有桶链
- dict:当dictht需要扩容/缩容时,用户管理dictht的迁移
// 哈希表(字典)数据结构,Redis 的所有键值对都会存储在这里 。其中包含两个哈希表 。typedef struct dict {// 哈希表的类型,包括哈希函数,比较函数,键值的内存释放函数dictType *type;// 存储一些额外的数据void *privdata;// 两个哈希表dictht ht[2];// 哈希表重置下标,指定的是哈希数组的数组下标int rehashidx; /* rehashing not in progress if rehashidx == -1 */// 绑定到哈希表的迭代器个数int iterators; /* number of iterators currently running */} dict;
Hash表的核心结构是dictht,它的table 字段维护着 Hash 桶,桶(bucket)是一个数组,数组的元素指向桶中的第一个元素(dictEntry) 。typedef struct dictht {//槽位数组dictEntry **table;//槽位数组长度unsigned long size;//用于计算索引的掩码unsigned long sizemask;//真正存储的键值对数量unsigned long used; } dictht;
结构图文章插图
Hash表使用【链地址法】解决Hash冲突 。当一个 bucket 中的 Entry 很多时,Hash表的插入性能会下降,此时就需要增加bucket的个数来减少Hash冲突 。
Hash表扩容和大多数Hash表实现一样,Redis引入负载因子判定是否需要增加bucket个数:
负载因子 = Hash表中已有元素 / bucket数量
扩容后,bucket 数量是原先2倍 。目前有2 个阀值:- 小于1 时一定不扩容
- 大于5 时一定扩容
- 在1 ~ 5 之间时,Redis 如果没有进行bgsave/bdrewrite 操作时则会扩容
- 当key - value 对减少时,低于0.1时会进行缩容 。缩容之后,bucket的个数是原先的0.5倍
不同的是,Map#ziplist的Entry个数总是2的整数倍: