Redis Set 用了 2 种数据结构来存储,到现在才知道

news2024/11/29 6:29:20

Sets 无序集合,他的功能就好像你熟悉的 Java 中的 HashSet 一样。集合是通过散列表实现的,所以添加、删除、查找元素的时间复杂度是 O(1)。

1. 是什么

Sets 是 String 类型的无序集合,集合中的元素是唯一的,集合中不会出现重复的数据

Java 的 HashSet 底层是用 HashMap 实现,Sets 的底层数据结构也是用 Hashtable(散列表)实现,散列表的 key 存的是 Sets 集合元素的 value,散列表的 value 则指向 NULL。

不同的是,当元素内容都是 64 位以内的十进制整数的时候,并且元素个数不超过 set-max-intset-entries 配置的值(默认 512)的时候,会使用更加省内存的 intset(整形数组)来存储

4c9b5dd8cd19ea1e5591c6e494cf79e6.png
图2-15

使用场景

当你需要存储多个元素,并且要求不能出现重复数据,无需考虑元素的有序时,就可以使用 Sets 来存储,这样能利用我对单个元素操作 O(1) 时间复杂度带来的性能优势。

并且 Sets 还支持在集合之间做交集、并集、差集操作,比如当你遇到如下场景,需要统计多个集合元素的聚合结果。

  • 统计多个元素的共有数据(交集)。

  • 统计两个集合其中的一个独有元素(差集统计)。

  • 统计多个集合的所有元素(并集统计)。

常见的使用场景。

  1. 社交软件中共同关注,通过交集实现。

  2. 每日新增关注数,只需要对近两天的总注册用户量集合取差集即可。

  3. 打标签:比如微信收藏功能,你可以为自己收藏的每一篇文章打标签,这样你可以快速的找到被添加了某个标签的所有文章。

2. 修炼心法

关于散列表结构我会在专门的章节介绍,先看 intset 结构,结构体定义在源码 intset.h中。

typedef struct intset {
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
} intset;
  • length,记录整数集合存储的元素个数,其实就是 contents 数组的长度。

  • contents,真正存储整数集合的数组,是一块连续内存区域。每个元素都是数组的一个数组元素,数组中的元素会按照值的大小从小到大有序排列存储,并且不会有重复元素。

  • encoding,编码格式,决定数组类型,一共有三种不同的值。

    • INTSET_ENC_INT16,表示 contents 数组的存储元素是 int16_t 类型,每 2 字节表示一个整数元素。

    • INTSET_ENC_INT32,表示 contents 数组的存储元素是 int32_t 类型,每 4 字节表示一个元素。

    • INTSET_ENC_INT64,表示 contents 数组的存储元素是 int64_t 类型,每 8 字节表示一个元素。

358f54ffb620cc561a20c41e997752b3.png
图2-16

MySQL:“如果在一个 int16_t 类型的整数集合中插入一个 int64_t 类型的值会怎样?”

这个问题问得好,下次可以继续保持。

这种情况会触发整数集合升级,也就是集合的所有元素都会转换成 int64_t 类型,步骤如下。

  1. 根据新元素的类型,以及集合元素的数量,包括新添加的元素在内,计算新的空间大小,对底层数组空间扩容,进行空间重新分配。

  2. 将数组原有的元素都转换成新元素类型,把转换后的元素按照从大到小的顺序放到正确的位置上,需要保证数组元素的有序性

  3. 修改 encoding 的值,length + 1。

所以每次向整形数组集合添加新元素都可能会引起升级,升级又会对原始数据进行类型转换,时间复杂度是 O(N)。

MySQL:“如果删除刚刚添加的 int64_t 类型元素,会执行降级操作么?”

整形数组不支持降级操作。

MySQL:“Sets 是无序集合,为何存储整形数字的场景下 contents 数组元素需要有序?”

为了查询元素速度,数组有序我就能使用二分法来提高查询效率insetFind() 函数返回值等于 0 表示集合中没有目标数据,反之 1 存在目标数据。方法的内部会调用 intsetSearch() 函数使用二分法来实现。

static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
    int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
    int64_t cur = -1;
    // 省略一些检查代码

    while(max >= min) {
        mid = ((unsigned int)min + (unsigned int)max) >> 1;
        cur = _intsetGet(is,mid);
        if (value > cur) {
            min = mid+1;
        } else if (value < cur) {
            max = mid-1;
        } else {
            break;
        }
    }
 // 修改 pos 指针
    if (value == cur) {
        if (pos) *pos = mid;
        return 1;
    } else {
        if (pos) *pos = min;
        return 0;
    }
}

pos 指针的作用有两个,如果查找到目标值, pos 记录目标值的位置;查找不到目标值,pos 记录的就是这个目标值插入到 intset 的位置。

3. 出招实战:共同好友

三国天下有限公司开发了一个名叫“三国恋”的社交 APP,想要实现共同好友功能,这个场景就能使用集合交集来实现。为每个用户创建一个 Sets 集合,账号名作为集合的 key,集合 value 存储该账号的好友。

如下指令构建刘备和曹操的好友集合。

SADD user:刘备 赵子龙 张飞 关羽 貂蝉
SADD user:曹操 貂蝉 夏侯惇 典韦 张辽

想要知道两个人的共同好友,也就是两个集合的交集,只需要使用 SINTERSTORE指令。

SINTERSTORE user:曹刘好友 user:刘备 user:曹操

命令执行后,刘备与曹操两个集合的交集数据就存储到了“user:曹刘好友”集合中。使用 SMEMBERS 查看曹操与刘备的共同好友。

redis> SMEMBERS user:曹刘好友
1) "貂蝉"

好家伙,他们都喜欢貂蝉,你喜不喜欢呢?


4bc11c95020a3852283b796639b0c382.png

关注我,提升技术能力


往期好文

Redis List 底层三种数据结构原理剖析

图解 Redis String 底层数据结构 SDS 与计数器实战

搞定 Redis 数据存储原理,别只会 set、get 了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/470568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【百问百答】可靠性基础知识第三期

1.电连接器的基本性能有哪些? 三个基本性能&#xff1a;机械性能、电气性能和耐环境性能。 电连接器机械性能测试包括&#xff1a;插拔力测试、端子保持力测试、端子正向力测试、耐久性测试。 电气特性测试包括&#xff1a;绝缘电阻测试、 耐电压测试、 低电平电阻测试(LLCR…

【YOLO v1】模型搭建 | model | 代码

YOLO V1 模型 import torch import torch.nn as nn from torchsummary import summarydef build_block(in_channel, out_channel, kernel_size, stride1, maxpoolFalse):padding kernel_size//2block nn.Sequential(nn.Conv2d(in_channel, out_channel, kernel_sizekernel_si…

数据包守恒 TCP 拥塞控制

数据包守恒是包括拥塞控制在内的合理利用带宽的方法之基石&#xff0c;它维持了有效网络传输的稳定&#xff0c;过去 40 年是&#xff0c;未来还是。数据包守恒可以描述为&#xff1a; 当带宽恰好满载时&#xff0c;receiver 收到 1 个数据包后 sender 才能发送 1 个数据包。当…

LeetCode链表OJ题目 代码+思路分享

目录 删除有序数组中的重复项合并两个有序数组移除链表元素 删除有序数组中的重复项 链接: link 题目描述&#xff1a; 题目思路&#xff1a; 本题使用两个指针dst和src一前一后 相同情况&#xff1a; 如果nums[dst]nums[src]&#xff0c;那么src 不相同情况&#xff1a; 此…

基于B/S架构SpringBoot+Bootstrap框架的中小医院信息系统

一、开源项目简介 基于B/S架构&#xff0c;SpringBootBootstrap框架的中小医院信息系统。简单实现了挂号收费&#xff0c;门诊管理&#xff0c;划价收费&#xff0c;药房取药&#xff0c;体检管理&#xff0c;药房管理&#xff0c;系统维护等基础功能。 二、功能概述 本系统是…

计算机网络学习04(应用层常见协议总结)

1、HTTP:超文本传输协议 超文本传输协议&#xff08;HTTP&#xff0c;HyperText Transfer Protocol) 是一种用于传输超文本和多媒体内容的协议&#xff0c;主要是为 Web 浏览器与 Web 服务器之间的通信而设计的。当我们使用浏览器浏览网页的时候&#xff0c;我们网页就是通过 …

电脑硬盘检测怎么操作?如何检查硬盘的健康情况?

案例&#xff1a;如何对电脑硬盘进行检测&#xff1f; 【我的电脑硬盘中有许多重要的数据&#xff0c;我想知道电脑硬盘的健康状况怎么样&#xff1f;有没有小伙伴知道电脑硬盘检测的方法&#xff1f;】 电脑硬盘是存储数据的关键组件&#xff0c;而随着时间的推移和使用频率…

JAVA IO 模型详解

什么是IO I/O&#xff08;Input/Outpu&#xff09; 即输入&#xff0f;输出 。 从计算机结构的视角来看的话&#xff0c; I/O 描述了计算机系统与外部设备之间通信的过程。 从应用程序的视角来看的话&#xff0c;我们的应用程序对操作系统的内核发起 IO 调用&#xff08;系统调…

RocketMq windows 安装

RocketMq安装步骤&#xff1a; 1、直接在官网下载。也可以从这里自取 https://rocketmq.apache.org/download/ 2、修改bin目录下的文件 runserver.cmd 和 runbroker.cmd文件。主要修改所占用内存的大小。 runserver.cmd 修改如下&#xff1a; runbroker.cmd 修改如下&#xff…

数据结构与算法(三):树论(树形结构、二叉树、二叉搜索树、红黑树、BtreeB+Tree、赫夫曼树、堆树)

树论&#xff08;树形结构、二叉树、二叉搜索树、红黑树、Btree、BTree、赫夫曼树、堆树&#xff09; 树形结构概念 在树形结构里面重要的术语&#xff1a; 结点&#xff1a;树里面的元素。 父子关系&#xff1a;结点之间相连的边 子树&#xff1a;当结点大于1时&#xff0…

牛客网CM11 链表分割

CM11 链表分割 描述示例解题思路以及代码解法1解法2 描述 现有一链表的头指针 ListNode* pHead&#xff0c;给一定值x&#xff0c;编写一段代码将所有小于x的结点排在其余结点之前&#xff0c;且不能改变原来的数据顺序&#xff0c;返回重新排列后的链表的头指针。 示例 解题…

JDBC详解(四):操作BLOB类型字段(超详解)

JDBC详解&#xff08;四&#xff09;&#xff1a;操作BLOB类型字段 前言一、MySQL BLOB类型二、向数据表中插入大数据类型三、修改数据表中的Blob类型字段四、从数据表中读取大数据类型 前言 本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识&#xff0c;有兴…

倾斜摄影超大场景的三维模型轻量化与三维展示效果的关系浅析

倾斜摄影超大场景的三维模型轻量化与三维展示效果的关系浅析 倾斜摄影超大场景的三维模型由于数据量庞大&#xff0c;直接进行渲染可能会导致计算资源和时间的浪费。因此&#xff0c;针对倾斜摄影超大场景的三维模型区域进行轻量化处理是一种有效的优化手段。但是&#xff0c;轻…

macOS 13.4Beta 3(22F5049e)With OpenCore 0.9.2开发版 and winPE双引导分区原版镜像

镜像特点 完全由黑果魏叔官方制作&#xff0c;针对各种机型进行默认配置&#xff0c;让黑苹果安装不再困难。系统镜像设置为双引导分区&#xff0c;全面去除clover引导分区&#xff08;如有需要&#xff0c;可以自行直接替换opencore分区文件为clover引导文件&#xff09;备注…

Python3语法笔记(后篇)

文章目录 前言函数输入参数返回值装饰器&#xff08;decorator&#xff09;Lambda表达式&#xff08;匿名函数&#xff09;文档和注解 类类和实例特殊方法继承枚举类 错误和异常后记 前言 这篇文章主要用于记录Python3相关语法&#xff0c;方便自己查阅使用。 Python3语法笔记…

rsync之include、exclude使用

rsync之include、exclude使用 注意&#xff1a;exclude可单独使用&#xff0c;include必须和exclude配合使用 环境&#xff1a; 服务端&#xff1a; 在做同步之前必须要知道的含义&#xff1a; --exclude* 排除所有文件&#xff0c;包括目录&#xff0c;因为在linux一切皆…

自动拣货仓库亮灯方案

方案目标概叙&#xff1a; 系统在美团平台下单后&#xff0c;骑手会收到取货码&#xff0c;凭借取货码到指定的智能仓库去取货&#xff0c;仓库标签系统调取相应订单信息&#xff0c;执行亮灯指令&#xff08;屏幕显示订单信息及拣货数量&#xff0c;并亮灯&#xff09;&#…

window server dos命令集合

1、 删除任务管理器服务里的服务项&#xff0c;用管理员运行cmd, 再运行&#xff1a;sc delete ServiceName ServiceName从服务属性里查。

爬虫为什么需要ip

爬虫需要使用爬虫ip主要是为了解决以下问题&#xff1a; 1、反爬虫机制&#xff1a;许多网站会设置反爬虫机制来防止爬虫程序的访问&#xff0c;例如限制IP地址的访问频率、检测访问来源等。使用爬虫ip可以绕过这些限制&#xff0c;使得爬虫程序更难被检测到。 2、访问限制&a…

Figma快速转换为Sketch文件格式的方法

由于Sketch允许第三方插件&#xff0c;例如应用程序集成和数据提供&#xff0c;许多设计和开发人员喜欢将Figma文件导入Sketch&#xff0c;并将其转换为Sketch格式。 但是令人头痛的是&#xff0c;Figma只支持Sketch文件的导入&#xff0c;而不支持Sketch文件的导出。这篇文章…