当前位置: 首页 > news >正文

高级数据结构——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。

相关文章:

高级数据结构——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. 分布式一致性hash5.1 缓存失效问题 6. 大数…...

【网络】什么是交换机?switch

交换机(Switch)意为“开关”,是一种用于电(光)信号转发的网络设备。以下是关于交换机的详细解释: 一、交换机的基本定义 功能:交换机能为接入交换机的任意两个网络节点提供独享的电信号通路&am…...

软件测试 —— 自动化基础

目录 前言 一、Web 自动化测试 1.什么是 Web 自动化测试 2.驱动 3.安装驱动管理 二、Selenium 1.简单 web 自动化测试示例 2.工作原理 三、元素定位 1.cssSelector 2.XPath 四、操作测试对象 1.点击/提交对象 2.模拟按键输入 3.清除文本内容 4.获取文本信息 5.…...

深入解析 OpenHarmony 构建系统-4-OHOSLoader类

在OpenHarmony操作系统构建过程中,OHOSLoader类扮演着至关重要的角色。这个类负责加载和解析构建配置,生成必要的构建文件,并确保构建过程的顺利进行。本文将深入分析OHOSLoader类的实现细节,揭示其如何管理构建配置,并…...

【Android、IOS、Flutter、鸿蒙、ReactNative 】实现 MVP 架构

Android Studio 版本 Android Java MVP 模式 参考 模型层 model public class User {private String email;private String password;public User(String email, String password) {this.email = email;this.password = password;}public String getEmail() {return email;}…...

排序算法(基础)大全

一、排序算法的作用: 排序算法的主要作用是将一组数据按照特定的顺序进行排列,使得数据更加有序和有组织。 1. 查找效率:通过将数据进行排序,可以提高查找算法的效率。在有序的数据中,可以使用更加高效的查找算法&…...

Pytest从入门到精通

一、pytest单元测试框架 (1)什么是单元测试框架 单元测试是指在软件开发当中,针对软件的最小单位(函数,方法)进行正确性的检查测试。 (2)单元测试框架 java : junit和testng python : unittest和pytest (3)单元测试框架主要做什么? 1.测试发现:从多个文件里面去找到我们测试…...

《C++ 实现生成多个弹窗程序》

《C 实现生成多个弹窗程序》 在 C 编程中,我们可以利用特定的系统函数来创建弹窗,实现向用户展示信息等功能。当需要生成多个弹窗时,我们可以通过循环结构等方式来达成这一目的。 一、所需头文件及函数介绍 在 Windows 操作系统环境下&#…...

react 中 useRef Hook 作用

useRef是一个非常实用的钩子函数 一、访问和操作 DOM 元素 1. 获取 DOM 元素引用 1.1 基本原理 通过 useRef 我们可以直接操作 DOM 元素 1.2 代码示例 import React, { useRef, useEffect } from "react";const InputFocusComponent () > {const inputRef …...

Scala-键盘输入(StdIn)-用法详解

Scala 在 Scala 中,进行 键盘输入 主要通过 scala.io.StdIn 包来实现。 StdIn 提供了几个方法,用于从用户的键盘输入中读取不同类型的数据,如字符串、整数、浮点数等。 常用的输入方法有 readLine()、readInt()、readDouble()、readShort(…...

力扣(LeetCode)283. 移动零(Java)

White graces:个人主页 🙉专栏推荐:Java入门知识🙉 🐹今日诗词:雾失楼台,月迷津渡🐹 ⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏 ⛳️点赞 ☀️收藏⭐️关注💬卑微小博主…...

ESP32C3单片机使用笔记---烧录MicroPython

使用MicroPython在ESP32C3单片机上编程,首先需要将MicroPython运行环境烧录到ESP32C3的Flash中去,步骤如下: 1.下载esptool烧录工具,下载地址: https://github.com/espressif/esptool 直接使用git clone git clone…...

Matter1.4重磅来袭,智能家居进入“互联”新纪元

近日,连接标准联盟(CSA)正式宣布推出最新的Matter1.4标准版本,并更新了一系列“史诗级”的增强功能,旨在提升现有智能家居之间的互操作性与兼容性,为智能家居用户带来更流畅的使用体验。 华普微&#xff0c…...

tdengine学习笔记

官方文档:用 Docker 快速体验 TDengine | TDengine 文档 | 涛思数据 整体架构 TDENGINE是分布式,高可靠,支持水平扩展的架构设计 TDengine分布式架构的逻辑结构图如下 一个完整的 TDengine 系统是运行在一到多个物理节点上的,包含…...

机器学习-36-对ML的思考之机器学习研究的初衷及科学研究的期望

文章目录 1 机器学习最初的样子1.1 知识工程诞生(专家系统)1.2 知识工程高潮期1.3 专家系统的瓶颈(知识获取)1.4 机器学习研究的初衷2 科学研究对机器学习的期望2.1 面向科学研究的机器学习轮廓2.2 机器学习及其应用研讨会2.3 智能信息处理系列研讨会2.4 机器学习对科学研究的重…...

Linux 进程信号的产生

目录 0.前言 1. 通过终端按键产生信号 1.1 CtrlC:发送 SIGINT 信号 1.2 Ctrl\:发送 SIGQUIT 信号 1.3 CtrlZ:发送 SIGTSTP 信号 2.调用系统命令向进程发信号 3.使用函数产生信号 3.1 kill 函数 3.2 raise 函数 3.3 abort 函数 4.由软件条件产…...

CentOS8 在MySQL8.0 实现半同步复制

#原理 MySQL默认是异步的,不要求必须全部同步到从节点才返回成功结果; 同步复制: 用户发请求到代理, 代理收到请求后写/更新数据库写入到二进制日志bin_log, 然后必须等数据发到所有的从节点, 从节点全部收到数据后, 主节点才返回给客户端的成功结果。 弊端: 客…...

数据分析——Python绘制实时的动态折线图

最近在做视觉应用开发,有个需求需要实时获取当前识别到的位姿点位是否有突变,从而确认是否是视觉算法的问题,发现Python的Matplotlib进行绘制比较方便。 目录 1.数据绘制2.绘制实时的动态折线图3.保存实时数据到CSV文件中 import matplotlib.…...

【Redis】Redis的一些应用场景及使用策略

应用的场景 Redis 是一个高性能的内存数据库,广泛用于各种应用场景,以下是一些常见的应用场景: 缓存:Redis 的高读写性能使其非常适合作为缓存层,存储频繁访问的数据以减少数据库负载和加快响应时间。例如&#xff0c…...

CentOS 8 安装 chronyd 服务

操作场景 目前原生 CentOS 8 不支持安装 ntp 服务,因此会发生时间不准的问题,需使用 chronyd 来调整时间服务。CentOS 8以及 TencentOS 3.1及以上版本的实例都使用 chronyd 服务实现时钟同步。本文介绍了如何在 CentOS 8 操作系统的腾讯云服务器上安装并…...

HarmonyOS ArkUI(基于ArkTS) 常用组件

一 Button 按钮 Button是按钮组件,通常用于响应用户的点击操作,可以加子组件 Button(我是button)Button(){Text(我是button)}type 按钮类型 Button有三种可选类型,分别为胶囊类型(Capsule)、圆形按钮(Circle&#xf…...

不用来回切换,一个界面管理多个微信

你是不是也有多个微信号需要管理? 是不是也觉得频繁切换账号很麻烦? 是不是也想提升多账号管理的效率? 在工作中,好的辅助工具,能让我们的效率加倍增长! 今天, 就给大家分享一个多微管理工具…...

MySQL系统优化

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

若依笔记(八):芋道的Docker容器化部署

目录 增加环境变量 DockerFile与镜像制作 nginx配置 vue3前端工程 首先搞个ECS阿里主机,1核4g足够,最大程度保证是docker运行来减少主机资源占用,同时因为是公有云,端口策略安全很重要,每个对外服务的端口要通过安全组放开; mysql的docker使用8版本,启动时候给my.cn…...

前端隐藏元素的方式有哪些?HTML 和 CSS 中隐藏元素的多种方法

当面试官突然问你:“前端隐藏元素的方式有哪些?”你还是只知道 display: none 吗? 其实,在前端开发的世界里,隐藏元素的方法非常多。每种方法都有自己的小技巧和使用场景,了解它们不仅能让你应对自如&…...

sqli—labs靶场 5-8关 (每日4关练习)持续更新!!!

Less-5 上来先进行查看是否有注入点,判断闭合方式,查询数据列数,用union联合注入查看回显位,发现到这一步的时候,和前四道题不太一样了,竟然没有回显位??? 我们看一下源…...

【Java】异常处理实例解析

文章目录 Java异常处理实例解析Example01_2023yang:未处理的异常Example02_2023yang:捕获并处理异常Example03_2023yang:finally块的使用Example04_2023yang:自定义异常Example05_2023yang:忽略异常信息Example06_2023…...

flutter调试

上面的调试The following FormatException was thrown while handling a gesture: Invalid double -Infinity874When the exception was thrown, this was the stack: #0 double.parse (dart:core-patch/double_patch.dart:113:28) #1 _CalculatorScreenState._butt…...

使用Web Workers提升JavaScript的并行处理能力

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 使用Web Workers提升JavaScript的并行处理能力 使用Web Workers提升JavaScript的并行处理能力 使用Web Workers提升JavaScript的…...

【含开题报告+文档+PPT+源码】基于Spring Boot智能综合交通出行管理平台的设计与实现

开题报告 随着城市规模的不断扩大和交通拥堵问题的日益严重,综合交通出行管理平台的研究与实现显得尤为重要。现代城市居民对于出行的需求越来越多样化,对于交通信息的获取和处理能力也提出了更高的要求。传统的交通管理方式已经难以满足这些需求&#…...