高级数据结构——hash表与布隆过滤器

文章目录

  • hash表与布隆过滤器
    • 1. hash函数
    • 2. 选择hash函数
    • 3. 散列冲突
      • 3.1 负载因子
      • 3.2 冲突解决
      • 3. STL中的散列表
    • 4. 布隆过滤器
      • 4.1 背景
        • 1. 应用场景
        • 2. 常见的处理场景:
      • 4.2 布隆过滤器构成
      • 4.3 原理
      • 4.4 应用分析
      • 4.5 要点
    • 5. 分布式一致性hash
      • 5.1 缓存失效问题
    • 6. 大数据相关的面试题
    • 学习参考

hash表与布隆过滤器

本文讲述了hash表的原理与实现、hash冲突及解决方法、基于位图和hash函数实现的布隆过滤器、以及用于负载均衡的分布式一致性hash等。

1. hash函数

用来求hash值的一种映射函数,hash函数可能会把两个或者两个以上的不同key映射到同一地址,这种情况称之为冲突(或者hash碰撞);

与平衡二叉树比较

  • 平衡二叉树利用二分查找的思维,通过每次比较排除一半元素来达到快速查找的目的。时间复杂度为O(logn)。10万结点比较20次,10亿结点比较30次即可。
  • 散列表,通过简历key值到索引位置的映射关系来实现快速查找,实现这种映射的函数叫做哈希函数(或者散列函数),平均时间复杂度为O(1)。

2. 选择hash函数

  • 计算速度快
  • 强随机分布(等概率、均匀地分布在整个地址空间)
  • murmurhash1, murmurhash2, murmurhash3, siphash(redis6.0当中使用,也是rust等大多数语言选用的hash算法来实现hashmap),cityhash都具备强随机分布性。
  • siphash主要解决相近的字符串的随机分布性。
  • murmurhash2是使用最频繁的hash算法。

测试地址:https://github.com/aappleby/smhasher

3. 散列冲突

不同的key可能会被映射到同一个地址,这种情况就叫做散列冲突。

3.1 负载因子

​ 负载因子:是指哈希表中已存储元素的数量与哈希表中桶(bucket)数量之间的比例,能够描述存储密度或者散列冲突的激烈程度。

3.2 冲突解决

如果负载因子在合理范围内

  • 拉链法

    将冲突元素用一个链表链接起来,查找时在链表内进行线性查找。

    这样可能出现一种极端情况,当冲突的元素比较多,冲突链表过长时,会显著影响查询性能,这时可以将这个链表转换为红黑树、最小堆。可以采用超过256(经验值)个结点的时候将链表结构转换为红黑树或者堆结构。

  • 开放寻址法

    • 线性探查法:有hash聚集(也叫主聚集)问题,即当多个元素发生hash冲突后,会被集中插入到hash表中的相邻位置,从而导致后续的冲突更容易发生在这些已占用的区域,形成聚集效应。这样新插入的元素需要花费更多的时间探查空闲位置,最终导致插入和查找的效率下降。
    • 二次探查法,使用非线性方式进行探查,次聚集问题,即由于探查模式相同而导致不同的键分布在相邻的位置上。
    • 再散列法,相比线性探查法就能够使插入的结点分布更加均匀,次聚集问题。

如果负载因子不在合理范围内

  • 扩容:翻倍扩容
  • 缩容

然后进行rehash

3. STL中的散列表

  • unordered_map, unordered_set, unordered_multimap, unordered_multiset

在这里插入图片描述

  • STL中的hash表采用类似拉链法的方法解决散列冲突。不同的是,为了实现迭代器,STL中会将每个桶对应的链表串连起来,这样所有的键值对就被组织成了一个单链表,方便迭代器的实现。这种情况下,为了实现头插法,每个桶中保存的是上一个拉链的尾结点。

以下是STL中插入一个结点时的代码实现,每个桶中的指针指向的的是另一个桶中的最后一个结点。

 if (_M_buckets[__bkt])
 {
     // Bucket is not empty, we just need to insert the new node
     // after the bucket before begin.
     __node->_M_nxt = _M_buckets[__bkt]->_M_nxt;
     _M_buckets[__bkt]->_M_nxt = __node;
 }
else
{
    // The bucket is empty, the new node is inserted at the
    // beginning of the singly-linked list and the bucket will
    // contain _M_before_begin pointer.
    __node->_M_nxt = _M_before_begin._M_nxt;
    _M_before_begin._M_nxt = __node;
    if (__node->_M_nxt)
        // We must update former begin bucket that is pointing to
        // _M_before_begin.
        _M_buckets[_M_bucket_index(__node->_M_next())] = __node;
    _M_buckets[__bkt] = &_M_before_begin;
}

4. 布隆过滤器

布隆过滤器(Bloom Filter)是一种基于哈希函数和哈希表实现的空间效率非常高的概率型数据结构,用于测试一个元素是否是一个集合的成员。它有一个重要的特点:可以判断元素是否属于某个集合,但不能确定元素不属于该集合。简单来说,布隆过滤器允许有一定的误判率,但不会漏判。

4.1 背景

1. 应用场景

内存有限,只想确定某个key是否存在,不想知道具体内容。

布隆过滤器通常用于判断某个key一定不存在的场景,同时允许判断存在时由误差的情况。

过滤对不存在的值的查询

  • 某个文件,如果要查询其中某条记录,可以先通过布隆过滤器判断该记录是否存在。
  • 某个数据库,在执行查询之前可以先通过布隆过滤器判断目标是否存在。
2. 常见的处理场景:
  1. 缓存穿透

在这里插入图片描述

缓存穿透的概念:

缓存穿透是指当查询的数据既不在缓存中也不在数据库中时,缓存系统没有对这些无效请求进行有效过滤,导致请求直接穿透缓存,频繁访问数据库,从而给数据库带来巨大的压力。这种情况通常出现在分布式系统中,特别是在使用缓存(如 Redis、Memcached)来优化数据访问的场景。

缓存穿透的成因:

  1. 恶意攻击:攻击者通过大量查询不存在的数据,绕过缓存系统,直接将请求压向数据库。这种行为可能会使数据库负载过大,影响系统性能。
  2. 自然请求:如果系统没有针对查询条件做有效过滤,用户可能请求不存在的数据(如查询一个从未有过的ID),也会直接穿透缓存,访问数据库。

缓存穿透的解决:

  1. 缓存空值,当某个键查询不到数据时,可以将空结果也存入缓存,并设置一个较短的过期时间。这样即使再次查询同样不存在的键,也会直接返回缓存中的空值,避免穿透到数据库。
  2. 布隆过滤器,使用布隆过滤器对所有可能存在的数据进行预先判断。在访问缓存之前,首先通过布隆过滤器判断查询的键是否可能存在。如果布隆过滤器判断该数据不可能存在,则直接返回空值,避免查询数据库。

总结:

缓存穿透是缓存系统中的一个常见问题,主要是由于缓存没有有效过滤无效请求,导致数据库承受过大的负载。通过缓存空值、使用布隆过滤器、参数校验等方法可以有效减少缓存穿透,提升系统整体性能。

  1. 热key限流

热key:

热Key指的是在缓存中某些特定键被大量并发请求访问的现象。因为请求集中于少数几个Key,缓存服务器的负载会非常高,当缓存失效时,可能会导致大量请求穿透到数据库,从而引发更大的压力,甚至导致数据库宕机。

热Key的影响:

  1. 缓存节点压力集中:缓存系统是分布式的,但由于热Key的访问量远超其他Key,某些缓存节点可能会成为性能瓶颈,导致这些节点比其他节点负载严重失衡。
  2. 缓存失效冲击数据库:在缓存失效时,所有的请求瞬间落到数据库,数据库处理不过来,可能会导致整个系统崩溃。

热key限流策略:

  1. 请求合并(Batching):当大量请求访问某个热Key且缓存过期时,可以使用请求合并策略,只让第一个请求去加载数据,其他请求等待第一个请求的结果。这种方式可以有效避免瞬间大量请求同时去查询数据库。

  2. 限流保护:对于访问量过高的Key,可以对其进行限流,控制每秒对该Key的最大请求次数令牌桶算法漏桶算法常被用于限流实现。

  3. 多副本缓存:在不同的缓存节点上放置同一个热Key的数据,这样可以将访问压力分散到多个缓存节点上。可以通过一致性哈希算法和副本策略来实现。

  4. 数据预热:在某些特定场景中(如系统重启、缓存失效后),可以提前预加载一些热点数据到缓存中,避免缓存初始化时发生大量请求直接穿透到数据库。

  5. 动态调整TTL:对于热Key,可以设置相对较长的缓存过期时间(TTL),这样可以避免频繁的缓存失效和更新。甚至可以根据热Key的访问频率动态调整其TTL,使得热Key在高峰期保持长时间不失效。

  6. 降级策略:在极端情况下,可以采用降级策略,对于热Key的请求进行降级处理,例如返回默认值或静态页面,确保系统的可用性不受单个热Key的影响。当缓存和数据库压力都很大时,可以设置简单的降级逻辑,避免系统崩溃。

总结:

热Key限流是一种应对高频访问缓存键的策略,主要目的是为了避免缓存过载和缓存失效后对数据库的冲击。常见的限流措施包括请求合并、限流保护、多副本缓存、数据预热和分级缓存等,这些方法能够有效减轻系统负担,保证系统的稳定性和高可用性。

4.2 布隆过滤器构成

布隆过滤器由一个位示图和k个哈希函数构成。位图的大小m和哈希函数的个数k由预期存储的最大元素数和最大假阳率决定。

  • 位图

在这里插入图片描述

  • n个hash函数

4.3 原理

在这里插入图片描述

  • 如何判断某个key不存在?

    bitmap[hash(key) % bit_size] == 0 ? 只要一个位置为0,就说明不存在。

  • 如果判断某个key存在?

    无法判断某个key一定存在,只能判断某个key一定不存在。

  • 布隆过滤器不支持删除操作

    bitmap中的一个位可能对应多个key

  • 假阳率?

    布隆过滤器判断一个key存在,但实际上不存在的情况。

4.4 应用分析

  • n,预期存入的元素个数
  • p,最大的假阳率,即判断hash值对应的位置都为1,判断为存在,而实际不存在的情况占比
  • m,位图的大小
  • k,hash函数的个数

从n和p计算m和k:https://hur.st/bloomfilter/

在这里插入图片描述

结论:当k=31时,假阳率(hash冲突率)是最低的。

4.5 要点

  • 能确定一个key一定不存在,可控假阳率确定存在

  • 不能删除

  • 先根据n和p算出m和k

5. 分布式一致性hash

一致性哈希的核心思想是通过将数据和节点映射到一个环形的哈希空间中。每个数据和节点都通过哈希函数映射到这个空间,然后通过哈希值来确定数据与节点的对应关系。

5.1 缓存失效问题

假设要对请求做负载均衡,原理是通过hash函数将请求的key值映射到一台缓存服务器中,因为hash值的分布是均匀的,所以每台服务器的负载应该也是均匀的。但是当服务器的数量发生变化时,就会导致所有请求的映射关系都被破坏,所有服务器中的缓存全部失效。

在这里插入图片描述

解决办法

引入哈希环,将数据和服务器结点都映射到该环上,距离数据顺时针方向上最近的服务器结点保存该节点的缓存。

  • 固定算法 hash(key) % 2 ^ 32 = index
  • hash迁移,改变节点的映射关系,解决大面积缓存失效,变为局部缓存失效
  • 增加虚拟结点,使迁移数据量减少

如图所示,在哈希环上,每个数据被映射到顺时针方向距离其最近的服务器结点。

在这里插入图片描述

  1. hash偏移

该问题是指当服务器结点较少时,可能服务器结点的间隔十分不均匀,导致服务器负载不均衡。

如图所示,s1服务器结点负载相对过大。
在这里插入图片描述

解决办法

增加虚拟结点,即一台服务器可以对应多个服务器结点,这样就增加了总的服务器结点数量,使各个服务器分布的更加均匀。

6. 大数据相关的面试题

在这里插入图片描述
题目
只用2G内存找到20亿个整数中出现次数最多的数

  • 思路:将大文件用hash拆成小文件,确保相同得key值被放入同一个文件(映射),并且每个每个文件中得数据量大致相同(利用其强随机特性)。
  • 单台机器通过hash分流到多台机器

学习参考

学习更多相关知识请参考零声 github。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小程序19-微信小程序的样式和组件介绍

在小程序中不能使用 HTML 标签,也就没有 DOM 和 BOM,CSS 也仅支持部分选择器 小程序提供了 WXML 进行页面结构的编写,WXSS 进行页面的样式编写 WXML 提供了 view、text、image、navigator等标签构建页面结构,小程序中标签称为组件…

[Linux]多线程详解

多线程 1.线程的概念和理解1.1线程的优点1.2线程的缺点1.3线程的设计1.4线程 VS 进程 2.线程控制2.1线程等待2.2 线程终止2.3 线程分离 3.线程互斥3.1背景3.2抢票代码演示3.3保护公共资源(加锁)3.3.1创建锁/销毁锁3.3.2申请锁/尝试申请锁/解锁 3.4解决抢…

CSP-J 2024题解

省流&#xff1a;300->260 乐。 Poker: 我考场上寻思着会不会有人写成了 joker.in joker.out&#xff0c;结果真的有 joker Sol EZ problem&#xff0c;拿 set 搞一下就行了&#xff08;虽然我赛事没想到&#xff0c;用了 map&#xff09; Code #include <bits/std…

【miniMax开放平台-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…

python机器人Agent编程——多Agent框架的底层逻辑(上)

目录 一、前言二、两个核心概念2.1 Routines&#xff08;1&#xff09;清晰的Prompt&#xff08;2&#xff09;工具调用json schema自动生成&#xff08;3&#xff09;解析模型的toolcall指令&#xff08;4&#xff09;单Agent的循环决策与输出 PS.扩展阅读ps1.六自由度机器人相…

达梦 DG

监视器 switchover 关于达梦DG switchover的细节&#xff0c;以下是一些关键步骤和注意事项&#xff1a; • 切换前检查确认&#xff1a; • 确认数据库版本和DG架构&#xff0c;包括IP信息及切换角色前后的情况。 • 检查DG切换方式&#xff0c;是switch over还是fail ove…

blind-watermark - 水印绑定

文章目录 一、关于 blind-watermark安装 二、bash 中使用三、Python 调用1、基本使用2、attacks on Watermarked Image3、embed images4、embed array of bits 四、并发五、相关 Project 一、关于 blind-watermark Blind watermark 基于 DWT-DCT-SVD. github : https://githu…

从零开始的c++之旅——二叉搜索树

1、二叉搜索树概念 1. ⼆叉搜索树的概念 ⼆叉搜索树⼜称⼆叉排序树&#xff0c;它或者是⼀棵空树&#xff0c;或者是具有以下性质的⼆叉树: • 若它的左⼦树不为空&#xff0c;则左⼦树上所有结点的值都⼩于等于根结点的值 • 若它的右⼦树不为空&#xff0c;则右⼦树上所有结…

类与对象;

目录 一、认识类&#xff1b; 1、类的引入&#xff1b; 2、类的定义&#xff1b; 类的两种定义方式&#xff1a; 3、类的访问限定符及封装&#xff1b; 4、类的作用域&#xff1b; 5、类的实例化&#xff1b; 6、类对象模型&#xff1b; 计算类对象的大小&#xff1b; …

ceph的集群管理

0 环境说明 ip地址主机名额外硬盘是否加入ceph集群10.0.0.141ceph141sdb 300G&#xff0c;sdc 500G是10.0.0.142ceph142sdb 300G&#xff0c;sdc 500G, sdd 1000G否10.0.0.143ceph143sdb 300G&#xff0c;sdc 500G否 在上一篇文章中&#xff0c;已经成功地初始化了一个ceph管…

企业无线解决方案

前言 无线广域网 无线广域网WWAN&#xff08;Wireless Wide Area Network&#xff09;目前已经成为了全球通信系统的核心组成部分&#xff0c;我们所熟悉的2G网络、3G网络和4G网络&#xff08;LTE&#xff09;等等都是WWAN的典型代表。通过WWAN&#xff0c;用户几乎可以在任何…

Springboot集成ElasticSearch实现minio文件内容全文检索

一、docker安装Elasticsearch &#xff08;1&#xff09;springboot和Elasticsearch的版本对应关系如下&#xff0c;请看版本对应&#xff1a; 注意安装对应版本&#xff0c;否则可能会出现一些未知的错误。 &#xff08;2&#xff09;拉取镜像 docker pull elasticsearch:7…

前后端请求响应

引入 在之前的例子中&#xff0c;我们编写了一个简单的web类&#xff0c;我们运行启动类&#xff0c;启动内嵌的tomcat后就可以在浏览器通过特定的路径访问tomcat中的应用程序。 但之前编写的程序仅仅是个简单的java类&#xff0c;其并未实现某个接口或继承某个类&…

VS2022编译32位OpenCV

使用环境 Visual Studio 2022 OpenCV: 4.7.0 cmake: 3.30.2一、使用CMake工具生成vs2022的openCV工程解决方案 打开cmake&#xff0c;选择opencv的源代码目录&#xff0c;创建一个文件夹&#xff0c;作为VS工程文件的生成目录 点击configure构建项目&#xff0c;弹出构建设置…

电子应用设计方案-12:智能窗帘系统方案设计

一、系统概述 本设计方案旨在打造便捷、高效的全自动智能窗帘系统。 二、硬件选择 1. 电机&#xff1a;选用低噪音、扭矩合适的智能电机&#xff0c;根据窗帘尺寸和重量确定电机功率&#xff0c;确保能平稳拉动窗帘。 2. 轨道&#xff1a;选择坚固、顺滑的铝合金轨道&…

MySQL系统优化

文章目录 MySQL系统优化第一章&#xff1a;引言第二章&#xff1a;MySQL服务架构优化1. 读写分离2. 水平分区与垂直分区3. 缓存策略 第三章&#xff1a;MySQL配置优化1. 内存分配优化Buffer Pool 的优化查询缓存与表缓存Key Buffer 2. 连接优化最大连接数会话超时连接池 3. 日志…

LLM - 计算 多模态大语言模型 的参数量(Qwen2-VL、Llama-3.1) 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/143749468 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 影响 (…

光耦选型指南

一、指南说明 针对光偶选型和实际使用过程中出现因光偶特性变化引起的产品失效问题&#xff0c;提供指导。光耦属于易失效器件&#xff0c;选型和使用过程中要特别的小心。目前发现&#xff0c;因光偶的选型&#xff0c;光偶工作电流&#xff0c;CTR参数选型不合适&#xff0c…

git创建远程仓库,以gitee码云为例GitHub同理

git远程Remote服务端仓库构建的视频教程在这 Git建立服务端Remote远程仓库&#xff0c;gitee码云例&#xff0c;Github_哔哩哔哩_bilibili 1、登gitee码云/Github 登录 - Gitee.com https://github.com/ &#xff08;没账号的注册一下就行&#xff09; 点击如下图位置的创…

19.UE5道具掉落

2-21 道具掉落&#xff0c;回血、回蓝、升级提升伤害_哔哩哔哩_bilibili 目录 1.道具的创建&#xff0c;道具功能的实现 2.随机掉落 1.道具的创建&#xff0c;道具功能的实现 新建Actor蓝图&#xff0c;并命名为道具总类&#xff0c;添加一个Niagara粒子组件和一个碰撞箱bo…