算法基础——存储

引入

基础理论的进步,是推动技术实现重大突破,促使相关领域的技术达成跨越式发展的核心。

在发展日新月异的大数据领域,基础理论的核心无疑是算法。不管是技术设计,还是工程实践,都必须仰仗相关算法的支持,才能够真的落地应用。

下面我们就看看大数据相关领域有哪些核心的算法。

存储类算法

大数据存储相关的核心算法,主要是为了高效存储和管理海量数据,以及提升数据读写性能和存储利用率等。

以下我们来看看大数据领域最经典的存储类算法:

B树及其变种(B+树、B* 树)

原理

  • B树:是一种自平衡的多路搜索树,每个节点可以有多个子节点。它的所有叶子节点都在同一层,并且包含了所有的数据。
  • B+树:是 B树的一种变种,它的非叶子节点只存储索引信息,所有的数据都存储在叶子节点中,叶子节点之间通过指针相连,形成一个有序链表,便于范围查询。(最通用)
  • B*树:在 B+树的基础上,对节点的分裂规则进行了优化,提高了空间利用率。

应用场景:广泛应用于关系型数据库的索引结构,能够高效地支持点查询和范围查询。

优点:查询、插入和删除操作的时间复杂度都是 O (log n),性能稳定。

缺点:对于大规模数据的写入操作,可能会导致频繁的节点分裂和合并,影响性能。

B+树是一种平衡的、多叉的树形结构,能够支持O(logn)的插入和查询时间复杂度。B+树的整个结构是有序存储的,这使得B+树能够高效地支持范围查询;在空间放大维度,B+树能够达到70%的空间利用率。综上所述,B+树有较好的综合性能,在现代的诸多存储系统中,B+树索引很常见,例如关系数据库MySQL的默认存储引擎InnoDB。

在大数据领域是避免不了使用多线程与高并发场景的,所以需要对B+树索引进行并发控制。由于B+树的树形结构会不断动态调整,要实现一个正确的多线程B+树,存在着较大的设计挑战。

目前来说,实现B+树的并发,可以采用以下3种机制:

  1. 锁耦合
    锁耦合机制是B+树中应用最为广泛的一种加锁方式。锁耦合机制就是一种节点级别的加锁方式,但是路径上的节点的锁会更早地释放,同时能保证线程安全。在锁耦合机制中,每个线程同时最多拥有两个节点的锁,分别为父节点和孩子节点。父节点的节点可以在孩子节点的锁获取之后释放,这样可以充分减少每个节点加锁和释放的临界区大小,从而最大化多线程性能。
  2. 乐观锁机制
    采用锁耦合机制,每个读/写线程仍然是互相阻塞的,而乐观锁机制则是为了减少写线程对读线程的阻塞,并进一步减少加锁的数量。内部节点除节点内部的锁字段之外,还额外维护一个写版本号。每当写线程对节点完成修改之后,先对写版本号完成自增操作,随后释放写锁。每当读线程访问一个节点的时候,首先记录节点版本号,在完成对节点的访问之后检测节点版本号是否发生变化,如果节点写版本号发生变化,读线程重做对该节点的访问,否则意味着节点访问过程中该节点并未发生写操作,因此读节点操作成功执行。
  3. 无锁机制。
    通过无锁的方式来操作B+树,提升随机读和范围查询的性能。它的核心的思想是把B+树的页(page)通过page id(PID)映射到map,map的[key,value]变成[PID,page value]​,把直接对page的修改,变成一个修改的操作记录,加入到“page value”​。所以“page value”可能是一个“base page”​,即page原始的内容,和一串对page修改形成的记录的链表,而在修改记录链表中加入一个修改记录节点可以很容易变成一个无锁的方式来实现。另外,对B+树的split和merge操作也通过类似的原理,把具体的操作细化成好几个原子操作,避免传统的加锁方式。

SkipList(跳表)

原理

  • SkipList 是一种可以用来快速查找数据的数据结构,它基于有序链表,并通过在链表节点上增加多层索引来提高查找效率。在 SkipList 中,每个节点都可能有多个指针,这些指针指向不同层次的下一个节点,高层的指针可以跳过更多的节点,从而加快查找速度。
  • 构建 SkipList 时,会按照一定的概率随机决定每个节点在不同层次出现的概率。例如,一个节点可能以 50% 的概率出现在第一层,以 25% 的概率出现在第二层,以 12.5% 的概率出现在第三层,以此类推。这样就形成了一个类似金字塔形状的多层结构,使得查找操作可以在对数时间内完成。

应用场景:由于 SkipList 在插入、删除和查找操作上都具有较高的效率,适合在内存中存储和操作大量的有序数据,能够快速地根据分数对元素进行排序和查找;在分布式哈希表(DHT)等分布式数据结构中,SkipList 可以用于实现节点之间的快速路由和数据查找。通过在不同节点上构建 SkipList 结构,可以高效地定位数据所在的节点,提高分布式系统的性能和可扩展性。

优点:SkipList 的插入、删除和查找操作的平均时间复杂度为 O (log n),与平衡树(如红黑树)等数据结构相当,但 SkipList 的实现相对简单,代码复杂度较低,易于理解和维护。而且 SkipList 支持动态扩展和收缩,能够方便地适应数据量的变化。

缺点:SkipList 的空间复杂度相对较高,因为每个节点可能包含多个指针,需要额外的空间来存储这些指针。此外,由于 SkipList 的节点层数是随机生成的,在极端情况下可能会出现查找性能下降的情况,但这种情况发生的概率较低。

跳跃表(SkipList)是一种能高效实现插入、删除、查找的内存数据结构,这些操作的期望复杂度都是O(logN)。与红黑树以及其他的二分查找树相比,跳跃表的优势在于实现简单,而且在并发场景下加锁粒度更小,从而可以实现更高的并发性。正因为这些优点,跳跃表广泛使用于KV数据库中,诸如Redis、LevelDB、HBase都把跳跃表作为一种维护有序数据集合的基础数据结构。

LSM树(Log-Structured Merge Tree)

原理:将数据的写入操作先记录在内存中(通常是一个有序的数据结构,如跳表),当内存中的数据达到一定阈值后,再批量地将数据写入磁盘,形成一个有序的数据文件(SSTable,Sorted String Table)。磁盘上的数据会按层级进行组织,不同层级的数据会定期进行合并操作,以减少数据冗余和提高查询效率。

应用场景:适用于写多读少的场景,如日志存储、时间序列数据存储等。

优点:写入性能高,能够快速处理大量的写入请求;

缺点:读取时可能需要合并多个 SSTable,读取性能相对较低,并且在合并过程中会产生一定的 I/O 开销。

2000年年初,Google发表了Bigtable的论文,论文中的创新点之一就是它所使用的文件组织方式,即LSM树。

算法的核心也是基于硬件特性来,才能真正的解决落地的问题。对于磁盘读写来说,顺序读写要远比随机读写快,LSM树通过将随机写转化为顺序写,消去随机的本地更新操作来提高写入性能,但查询(包括点查询和范围查询)性能会有一定程度的下降,因为一次查询操作可能要遍历磁盘中的许多个不同的SST文件。针对查询性能问题,在不同应用实现时会有一些优化,比如在HBase中设计了异步的compaction来降低文件个数,来提高读取性能。

LSM树本质上和B+树一样,是一种磁盘数据的索引结构。但和B+树不同的是,LSM树的索引对写入请求更友好。因为无论是何种写入请求,LSM树都会将写入操作处理为一次顺序写。

LSM树的索引一般由两部分组成,一部分是内存部分,一部分是磁盘部分。内存部分一般采用跳跃表来维护一个有序的KeyValue集合。磁盘部分一般由多个内部KeyValue有序的文件组成。

哈希算法(Hash Tables)

原理:通过哈希函数将键映射到一个固定大小的数组中,数组中的每个位置称为一个槽(Slot)。当插入、查找或删除数据时,先计算键的哈希值,然后根据哈希值找到对应的槽。如果发生哈希冲突(即不同的键映射到了同一个槽),可以采用开放寻址法、链地址法等方法来解决。

应用场景:适用于快速查找和插入的场景,如缓存系统、分布式哈希表(DHT)等。在分布式系统中,一致性哈希算法是一种常用的哈希算法,用于实现数据的均匀分布和节点的动态扩展。

优点:平均查找、插入和删除操作的时间复杂度为 O (1),性能高效;

缺点:哈希函数的设计比较关键,如果哈希函数设计不当,可能会导致哈希冲突频繁,影响性能。并且哈希表不支持范围查询。

哈希表是一种无序的数据结构,它提供了快速的插入操作和查找操作。

一个好的哈希表能够保证插入和查找的时间复杂度为O(1),即插入和查询性能与哈希表中的数据量无关。这种设计可以实现高效的写性能和查询性能,但是它牺牲了范围查询性能。

哈希表结构设计中最关键的问题是:

  1. 如何选择合适的哈希函数;
  2. 如何选择合适的哈希冲突处理机制。

常见的哈希冲突解决机制有四种:

  1. 链地址法。
    在链地址法下,哈希表的每个桶由一个链表构成。链表中存储的是所有哈希值相同的键值对。因此在进行查询操作时,可以通过遍历该链表查询对应的键值对。
  2. 线性探测法。
    在线性探测法下,哈希表是一个连续的桶数组,对于任意一个哈希键,根据哈希函数定位到一个映射位置,插入和查找都基于该地址进行向后探测。当插入一个键值时,判断映射地址是否为空,如果该地址为空,则在映射地址插入键值对,否则向后探测直到找到空桶,并将该键值对放入该空桶。查询操作则从映射地址开始向后扫描所有键值对,直到找到待查询键值对或者遇到一个空桶。
  3. 双选择法。
    双选择法采用两个独立的哈希函数,对于每个键值对,都有两个可插入的桶。当执行插入的时候,根据两个哈希函数分别将哈希键映射到两个桶a和b中。根据桶a和桶b的填充度,选择填充度更低的桶插入键值对。同样,执行查询操作时,只需要遍历两个桶即可定位到查询键值。
  4. 布谷鸟探测法。
    布谷鸟探测法是双选择法的一种变种。它同样采用两个哈希函数。当执行键值对插入时,根据两个哈希函数分别将哈希键映射到两个桶a和b中。如果桶a和b存在空闲位置,则将键值对插入到空闲位置中;否则,随机挑选一个桶中的键值对,将其踢出该桶,并存入待插入键值对,被踢出的键值对则尝试插入到其对应的另一个桶中。

采用不同哈希冲突解决方式,在查询性能、插入性能、哈希表填充度三个维度会有不同的表现,解决哈希冲突的方案也是没有“银弹”。

链地址法的插入性能更优,并且对于空间的占用是逐渐增长的;线性探测法的填充度可以做到最优,但是这是以牺牲查询和插入性能为前提的;在查询性能上,布谷鸟和双选择法会比其他方法更优。在实际的键值数据库中,不同的设计会采用不同的哈希函数和哈希冲突解决机制。Redis采用的就是链地址法,这使得Redis的空间占用更为缓慢,空间管理也更为灵活。

LRU(Least Recently Used)和 LFU(Least Frequently Used)缓存算法

原理

  • LRU:基于 “最近最少使用” 的原则,当缓存空间满时,优先淘汰最近最少使用的数据。通常使用双向链表和哈希表来实现,双向链表用于维护数据的访问顺序,哈希表用于快速查找数据。
  • LFU:基于 “最不经常使用” 的原则,当缓存空间满时,优先淘汰使用频率最低的数据。可以使用多个链表和哈希表来实现,每个链表存储相同使用频率的数据。

应用场景:常用于缓存系统中,如数据库缓存、Web 服务器缓存等,以提高数据的访问速度。

优点

  • LRU:实现简单,能够较好地反映数据的访问局部性。
  • LFU:能够更好地适应数据的使用频率。

缺点

  • LRU:对于某些特殊的访问模式,可能会导致性能下降。
  • LFU:实现相对复杂,并且在数据访问模式发生变化时,需要一定的时间来调整。

总结

今天提到的都是存储相关最核心的算法,本文主要是抛砖引玉,后续在分享大数据相关组件底层实现原理时,有涉及到相关算法的时候,我们再深入看看。

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

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

相关文章

正则表达式入门

入门 1、提取文章中所有的英文单词 //1.先创建一个Pattern对象,模式对象,可以理解成就是一个正则表达式对象 Pattern pattern Pattern.compile("[a-zA-Z]"); //2.创建一个匹配器对象 //理解:就是 matcher匹配器按照p…

分布式架构中的事务管理:需要了解的常见解决方案

前言 在现代互联网应用中,分布式架构越来越常见。随着系统规模的扩大,越来越多的业务和数据被分布到不同的服务和数据库中。虽然分布式架构带来了诸多优势,但也引入了一个新的问题:分布式事务。 一、什么是分布式事务&#xff1…

《TCP 网络编程实战:开发流程、缓冲区原理、三次握手与四次挥手》

一、 TCP 网络应用程序开发流程 学习目标 能够知道TCP客户端程序的开发流程1. TCP 网络应用程序开发流程的介绍 TCP 网络应用程序开发分为: TCP 客户端程序开发TCP 服务端程序开发说明: 客户端程序是指运行在用户设备上的程序 服务端程序是指运行在服务器设备上的程序,专门…

新年新挑战:如何用LabVIEW开发跨平台应用

新的一年往往伴随着各种新的项目需求,而跨平台应用开发无疑是当前备受瞩目的发展趋势。在众多开发工具中,LabVIEW 以其独特的图形化编程方式和强大的功能,为开发跨平台应用提供了有效的途径。本文将深入探讨如何运用 LabVIEW 开发能够在不同操…

C 语言实现计算一年中指定日期是第几天 题】

引言 在编程的世界里,处理日期和时间相关的问题是非常常见的。比如在日历应用、任务管理系统、数据分析等场景中,经常需要计算某个日期在一年中是第几天。本文将详细介绍如何使用 C 语言来实现这一功能,通过分析代码的结构、逻辑以及可能存在…

rsync安装与使用-linux015

使用 rsync 可以非常高效地将文件或目录从一个服务器传输到另一个服务器。 能力: 支持 64 位文件、64 位 inode、64 位时间戳、64 位长整型支持套接字对、符号链接、符号链接时间、硬链接、硬链接特殊文件、硬链接符号链接支持 IPv6、访问时间(atimes&…

UE5.3 C++ CDO的初步理解

一.UObject UObject是所有对象的基类,往上还有UObjectBaseUtility。 注释:所有虚幻引擎对象的基类。对象的类型由基于 UClass 类来定义。 这为创建和使用UObject的对象提供了 函数,并且提供了应在子类中重写的虚函数。 /** * The base cla…

Pandas基础06(异常值的检测与过滤/抽样/常用聚合函数/数据聚合)

Pandas基础06 异常值的检测与过滤 在数据分析中,异常值(Outliers)是指与其他数据点显著不同的值。这些值可能由于数据录入错误、设备故障或极端情况而产生,因此在进行数据分析之前,需要对其进行检测与过滤。本文将介绍…

【PyTorch】4.张量拼接操作

个人主页:Icomi 在深度学习蓬勃发展的当下,PyTorch 是不可或缺的工具。它作为强大的深度学习框架,为构建和训练神经网络提供了高效且灵活的平台。神经网络作为人工智能的核心技术,能够处理复杂的数据模式。通过 PyTorch&#xff0…

jstat命令详解

jstat 用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据。 命令的使用格式如下。 jstat [option] LVMID [interval] [count]各个参数详解: option:操作参数LVMID:本…

App.Current.Services.GetService<UserView>()无限循环

代码无线循环 public partial class UserView : UserControl{public UserView(){InitializeComponent();InitData();}private void InitData(){DataContext App.Current.Services.GetService<UserView>();}} } DataContext App.Current.Services.GetService<User…

(动态规划路径基础 最小路径和)leetcode 64

视频教程 1.初始化dp数组&#xff0c;初始化边界 2、从[1行到n-1行][1列到m-1列]依次赋值 #include<vector> #include<algorithm> #include <iostream>using namespace std; int main() {vector<vector<int>> grid { {1,3,1},{1,5,1},{4,2,1}…

松灵机器人 scout ros2 驱动 安装

必须使用 ubuntu22 必须使用 链接的humble版本 #打开can 口 sudo modprobe gs_usbsudo ip link set can0 up type can bitrate 500000sudo ip link set can0 up type can bitrate 500000sudo apt install can-utilscandump can0mkdir -p ~/ros2_ws/srccd ~/ros2_ws/src git cl…

pytorch基于GloVe实现的词嵌入

PyTorch 实现 GloVe&#xff08;Global Vectors for Word Representation&#xff09; 的完整代码&#xff0c;使用 中文语料 进行训练&#xff0c;包括 共现矩阵构建、模型定义、训练和测试。 1. GloVe 介绍 基于词的共现信息&#xff08;不像 Word2Vec 使用滑动窗口预测&…

C++ 堆栈分配的区别

这两种声明方式有什么区别 1.使用 new 关键字动态分配内存 动态分配&#xff1a;使用 new 关键字会在堆&#xff08;heap&#xff09;上分配内存&#xff0c;并返回一个指向该内存位置的指针。生命周期&#xff1a;对象的生命周期不会随着声明它的作用域结束而结束&#xff0…

深入解析 Linux 内核中的页面错误处理机制

在现代操作系统中,页面错误(Page Fault)是内存管理的重要组成部分。当程序试图访问未映射到物理内存的虚拟内存地址时,CPU 会触发页面错误异常。Linux 内核通过一系列复杂的机制来处理这些异常,确保系统的稳定性和性能。本文将深入解析 Linux 内核中处理页面错误的核心代码…

MATLAB-Simulink并行仿真示例

一、概述 在进行simulink仿真的过程中常常遇到CPU利用率较低&#xff0c;仿真缓慢的情况&#xff0c;可以借助并行仿真改善这些问题&#xff0c;其核心思想是将参数扫描、蒙特卡洛分析或多工况验证等任务拆分成多个子任务&#xff0c;利用多核CPU或计算集群的并行计算能力&…

Workbench 中的热源仿真

探索使用自定义工具对移动热源进行建模及其在不同行业中的应用。 了解热源动力学 对移动热源进行建模为各种工业过程和应用提供了有价值的见解。激光加热和材料加工使用许多激光束来加热、焊接或切割材料。尽管在某些情况下&#xff0c;热源 &#xff08;q&#xff09; 不是通…

I2C基础知识

引言 这里祝大家新年快乐&#xff01;前面我们介绍了串口通讯协议&#xff0c;现在我们继续来介绍另一种常见的简单的串行通讯方式——I2C通讯协议。 一、什么是I2C I2C 通讯协议&#xff08;Inter-Integrated Circuit&#xff09;是由Phiilps公司在上个世纪80年代开发的&#…

深度学习 DAY3:NLP发展史

NLP发展史 NLP发展脉络简要梳理如下&#xff1a; (远古模型&#xff0c;上图没有但也可以算NLP&#xff09; 1940 - BOW&#xff08;无序统计模型&#xff09; 1950 - n-gram&#xff08;基于词序的模型&#xff09; (近代模型&#xff09; 2001 - Neural language models&am…