ElasticSearch学习篇13_《检索技术核心20讲》进阶篇之LSM树

news2025/1/22 15:59:14

背景

学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243,文档形式记录笔记。

内容

磁盘和内存数据读取特点

工业界中数据量往往很庞大,比如数据无法全部加载进内存,无法支持索引的高效实时更新,因此对于复杂的业务问题和业务场景,往往需要检索技术进行组合和升级。

内存称为随机访问存储器,只要给出内存地址就能直接访问数据。
磁盘是机械器件,访问数据的时候需要磁盘篇转到磁头下面,尽管磁盘旋转速度很快,但是和内存随机访问相比,一般来说会是10万到100万倍左右,但如果是顺序访问大批量数据的话,磁盘的性能和内存就是一个数量级的。

为何磁盘顺序读写比较快?磁盘的最小读写单位是扇区,较早期的磁盘的一个扇区是512字节,随着发展,目前常见的磁盘扇区是4K个字节,OS一次会读写多个扇区,所以OS的最小读写单位是块Block,也叫做簇Cluster,顺序读取磁盘这样OS就能一次读取多个块,这样相对下来效率会比较高。

PS:SSD硬盘和内存读写效率

  • 以机械硬盘举例,顺序读写比随机读写少了多次 seek 的时间。 HDD 的 seek 耗时是 10ms ,吞吐率是 100MB/s,也就是每秒能 write 100MB 的数据。 那么以 1KB 为单位,纯写入1KB数据只需要 10us ,是seek 的 1/1000。
  • SSD在seek时间上比HDD要快很多,但是SSD的工作原理如下:SSD最高层是 NAND Flash,每个 NAND Flash 里有多个 Block ,Block 里又包括很多 Page,SSD 的特点就是读和写都要以 Page 为单位,最少一个 Page。通常一个 Page 是 4k 或 8k。顺序读写只涉及到一个Page,随机读写涉及到多个Page,造成读写放大。

SSD是以page作为读写单位,以block作为垃圾回收单位,因此,批量顺序写性能依然大幅高于随机写!SSD的性能和内存相比依然有差距,因此,先在内存处理好,再批量写入SSD依然是高效的。

索引和数据分离之B+Tree

对磁盘的访问次数要尽可能少,假如使用一个数组存储信息到磁盘,这个大数组需要占用很多块,在查找的过程中,就需要多次加载块到内存进行二分查找,直到查询结束,这样一次查询可能需要访问很多次磁盘,这种情况应该尽量避免。索引和数据文件分离是一种常见的设计思路,B+Tree就是这莫设计的。

索引和数据文件分离就是假如需要存储的内容比较多,那么得放入磁盘,但是全部字段放入的话查询需要访问很多次磁盘,效率比较低,因此可以拆分一个索引文件,索引文件只存储数据项的唯一标识以及指向磁盘数据的指针,这样索引文件会小很多,某些情况下可以加载进内存进行查询。

这种称为线性索引,对于更新频繁的场景,有序数据并不是一个很好的选择,删除数据,更新数据的话需要进行索引调整,而数组是连续的内存空间,二叉树和Hash索引往往更具有普适性,但是Hash索引又不支持范围查询,因此使用二叉树做索引比较合适。

image.png

B+ Tree结构

B+ 树给出了将树形索引的所有节点都存在磁盘上的高效检索方案,使得索引技术摆脱了内存空间的限制,得到了广泛的应用。
B+ 树是一棵完全平衡的 m 阶多叉树。所谓的 m 阶,指的是每个节点最多有 m 个子节点,并且每个节点里都存了一个紧凑的可包含 m 个元素的数组。

B+树的一个关键设计就是节点的数据大小和OS块大小一致,这样可以提高磁盘的读取效率,节点存储的元素不是一个元素,而是m个元素的有序数组。

B+树的另外一个关键设计就是所有节点分为叶子节点、非叶子节点,结构是一样的,但是存储的内容是不同的。非叶子节点存储key以及树形结构的指针,而不是数据的指针,这样可以使用更少的节点组织数据。叶子节点可以存储指针 or 数据,像MySQL中的innoDB就是存储的数据,MyISAM存储的就是指针。

另外B+树还讲同一层的所有节点做成有序的双链表,这样可以进行很好的范围查询以及灵活调整能力。

B+ 树的一个节点就能存储一个包含 m 个元素的数组,这样的话,一个只有 2 到 4 层的 B+ 树,就能索引数量级非常大的数据了,因此 B+ 树的层数往往很矮。比如说,一个 4K 的节点的内部可以存储 400 个元素,那么一个 4 层的 B+ 树最多能存储 400^4,也就是 256 亿个元素。

另外可以前几层的节点数据加载到内存中做缓存,进一步减少索引文件磁盘访问次数。比如说,对于一个 4 层的 B+ 树,每个节点大小为 4K,那么第一层根节点就是 4K,第二层最多有 400 个节点,一共就是 1.6M;第三层最多有 400^2,也就是 160000 个节点,一共就是 640M。对于现在常见的计算机来说,前三层的内部节点其实都可以存储在内存中,只有第四层的叶子节点才需要存储在磁盘中。这样一来,我们就只需要读取一次磁盘即可。这也是为什么,B+ 树要将内部节点和叶子节点区分开的原因。通过这种只让内部节点存储索引数据的设计,我们就能更容易地把内部节点全部加载到内存中了。

B+Tree动态调整

插入删除元素,节点调整,比如以一个m=3的B+树需要插入ID=6的这个记录,首先需要二分查找到叶子节点,叶子节点未满的话,直接插入就行。

叶子节点满了的话需要进行叶子节点块分裂,比如插入ID=10的记录,就要进行分裂,逻辑就是生成一个新的节点,然后讲数据平分。叶子节点分裂完成以后,上一层的内部节点也需要修改。但如果上一层的父节点也是满的,那么上一层的父节点也需要分裂。
image.png

删除也需要动态调整节点,也需要进行判断,如果从一个元素已经满的叶子节点删除一个元素,直接删除即可。如果一个叶子节点删除某个元素后,有一半以上的空间为空,就需要移动兄弟的节点,可以成功转移的条件是,元素转移后该节点及其兄弟节点的空间必须都能维持在半满以上。如果无法满足这个条件,就说明兄弟节点其实也足够空闲,那我们直接将该节点的元素并入兄弟节点,然后删除该节点即可。

思考1:B+树的一个很大优势就是适合做范围查询。如果我们要检索值在 x 到 y 之间的所有元素,你会怎么操作呢?想到就是找出x值所在的节点,然后沿着叶子节点向后找直到找到y。这里需要注意B+树的叶子节点块之间也不是连续存储的,采用两次二分就不太适合了,不能将全部数据一次取出。

非关系型数据结构存储之LSM树

在关系型数据库之外,还有许多常见的大数据应用场景,比如,日志系统、监控系统。这些应用场景有一个共同的特点,那就是数据会持续地大量生成,而且相比于检索操作,它们的写入操作会非常频繁。另外,即使是检索操作,往往也不是全范围的随机检索,更多的是针对近期数据的检索。

如果是一个日志系统,每秒钟要写入上千条甚至上万条数据,使用B+树随机写入这样的磁盘操作代价会使得系统性能急剧下降,甚至无法使用。一种常见的设计思路和检索技术:LSM 树(Log Structured Merge Trees)。LSM 树也是近年来许多火热的 NoSQL 数据库中使用的检索技术。

LSM的设计思路

针对B+树随机写入慢的特点,可以考虑将数据按照 block块 写入,可以大幅减少写入次数。

LSM树就是根据这个思路设计的,**批量写入以批量顺序写入磁盘减少随机写入次数 **,先将数据放入内存的树中,积攒到一定量在写入磁盘,当数据写入时,延迟写磁盘,将数据先存放在内存中的树里,进行常规的存储和查询。当内存中的树持续变大达到阈值时,再批量地以块为单位写入磁盘的树中。

图源:https://zhuanlan.zhihu.com/p/415799237

LSM的结构是一个森林,类似BKD-Tree,可以分为内存树、磁盘树,

  • 至于磁盘树,可以使用类似B+树实现,按照Block写入效率比较高磁盘树是一棵满二叉树(其实是一个SSTable结构,存储kv结构,为了好理解可以理解为B+树),也就是叶子节点都是满的。
  • 对于内存树,结构可以使用AVL、跳表等实现。数据首先写入到内存树,满了之后刷到磁盘中,在内存中的数据支持更快的检索和修改。

LSM的结构


主要有三部分组成:

  • _MemTable:_MemTable是在**内存**中的数据结构,用于保存最近更新的数据,会按照Key有序地组织这些数据,LSM树对于具体如何组织有序地组织数据并没有明确的数据结构定义,例如Hbase使跳跃表来保证内存中key的有序。因为数据暂时保存在内存中,内存并不是可靠存储,如果断电会丢失数据,因此通常会通过WAL(Write-ahead logging,预写式日志)的方式来保证数据的可靠性。
  • _Immutable MemTable:_当 MemTable达到一定大小后,会转化成Immutable MemTable。Immutable MemTable是将转MemTable变为SSTable的一种中间状态。写操作由新的MemTable处理,在转存过程中不阻塞数据更新操作。
  • _SSTable(Sorted String Table):磁盘树可以有很多层,每层可以有多个SSTable,有序键值对_集合,是LSM树组在**磁盘**中的数据结构。为了加快SSTable的读取,可以通过建立key的索引以及布隆过滤器来加快key的查找。一个key可以存在不同level的的不同SSTable中,但是上一层的level的key新鲜度总是高于下层的,同一层中的多个SSTable只能有一个key。

LSM的崩溃恢复

内存树的数据断电易丢失,LSM使用WAL(预写日志技术)来保证数据不丢失,在写入数据到内存的时候快速的写入日志到磁盘,具体步骤

  • 内存中的程序在处理数据时,会先将对数据的修改作为一条记录,顺序写入磁盘的 log 文件作为备份。由于磁盘文件的顺序追加写入效率很高,因此许多应用场景都可以接受这种备份处理。
  • 在数据写入 log 文件后,备份就成功了。接下来,该数据就可以长期驻留在内存中了。
  • 系统会周期性地检查内存中的数据是否都被处理完了(比如,被删除或者写入磁盘),并且生成对应的检查点(Check Point)记录在磁盘中。然后,我们就可以随时删除被处理完的数据了。这样一来,log 文件就不会无限增长了。
  • 系统崩溃重启,我们只需要从磁盘中读取检查点,就能知道最后一次成功处理的数据在 log 文件中的位置。接下来,我们就可以把这个位置之后未被处理的数据,从 log 文件中读出,然后重新加载到内存中。

通过这种预先将数据写入 log 文件备份,并在处理完成后生成检查点的机制,可以安全的使用内存来检索、存储数据了。

LSM的数据合并思路

内存树被写满之后,需要将数据合并到磁盘中的树中,称之为滚动合并(Rolling Merge)。
便于理解,同样磁盘树理解为B+树,参考链表的归并排序,将内存树和磁盘树的数据读取出来,放入内存进行归并,需要注意的是为了提高磁盘树的读写效率,对于磁盘树除了根节点外的节点,尽可能放入连续的块中,这样可以按块加载多个相邻的数据节点,这样的块叫做 多页块(Multi-Pages Block)。
合并步骤

  • 第一步,以多页块为单位,将 C1 树的当前叶子节点从前往后读入内存。读入内存的叫作清空块(Emptying Block),意思是处理完以后会被清空。
  • 第二步,将 C0 树的叶子节点和清空块中的数据进行归并排序,把归并的结果写入内存的一个新块中,叫作填充块(Filling Block)。
  • 第三步,如果填充块写满了,我们就要将填充块作为新的叶节点集合顺序写入磁盘。这个时候,如果 C0 树的叶子节点和清空块都没有遍历完,我们就继续遍历归并,将数据写入新的填充块。如果清空块遍历完了但是C1叶子节点还没遍历完,我们就去 C1 树中顺序读取新的多页块,加载到清空块中。
  • 第四步,重复第三步,直到遍历完 C0 树和 C1 树的所有叶子节点,并将所有的归并结果写入到磁盘。这个时候,我们就可以同时删除 C0 树和 C1 树中被处理过的叶子节点。

image.png
这样就完成了滚动归并的过程。在 C0 树到 C1 树的滚动归并过程中,你会看到,几乎所有的读写操作都是以多页块为单位,将多个叶子节点进行顺序读写的。而且,因为磁盘的顺序读写性能和内存是一个数量级的,这使得 LSM 树的性能得到了大幅的提升。

LSM树的检索之删除标记

先查内存树,如果内存有,直接作为结果返回,内存中的数据是最新的。
如果内存树没有,查询磁盘树,先以单棵B+树为例,这时候查到数据需要检查数据的时效性,因为LSM批量写入的特点,可能存在磁盘脏数据问题。
如一个数据已经被写入系统了,并且我们也把它写入 C1 树了。但是,在最新的操作中,这个数据被删除了,那我们自然不会在 C0 树中查询到这个数据。可是它依然存在于 C1 树之中。LSM解决办法是依然采用延迟写入,尽量避免随机访问磁盘树,对于被删除的数据,同样将这个数据key插入到内存树,只不过key上加了删除标志。
这样一来,当我们在 C0 树中查询时,如果查到了一个带着删除标志的 key,就直接返回查询失败,我们也就不用去查询 C1 树了。在滚动归并的时候,我们会查看数据在 C0 树中是否带有删除标志。如果有,滚动归并时就将它放弃。这样 C1 树就能批量完成“数据删除”的动作。

LSM树的两种合并策略

合并时机

  • 因为内存不是无限大,Level 0树达到阈值时,需要将数据从内存刷到磁盘中,这是合并操作的第一个场景;
  • 需要对磁盘上达到阈值的顺序文件进行归并,并将归并结果写入下一层,归并过程中会清理重复的数据和被删除的数据(墓碑标记)。我们分别对上述两个场景进行分析:

为了防止SSTable的数量不断增加,合并策略很关键,主要介绍两种基本策略:size-tiered和leveled

  • size-tiered 策略:size-tiered策略保证每层SSTable的大小相近,同时限制每一层SSTable的数量。如上图,每层限制SSTable为N,当每层SSTable达到N后,则触发Compact操作合并这些SSTable,并将合并后的结果写入到下一层成为一个更大的sstable。由此可以看出,当层数达到一定数量时,最底层的单个SSTable的大小会变得非常大。并且size-tiered策略会导致空间放大比较严重**。**即使对于同一层的SSTable,每个key的记录是可能存在多份的,只有当该层的SSTable执行compact操作才会消除这些key的冗余记录。
  • leveled策略:限制每一层的SSTable总文件大小。

LSM的检索

如果内存树没有,查询磁盘树,以SSTable为例,先查level1的多个SSTable,没有就查level2的,一次类推,如果在level2的SSTabel都找到了key,就返回结果。

综上我们可以给出LSM树的优缺点:

  • 优:增、删、改操作飞快,写吞吐量极大。
  • 缺:对于SSTable实现的磁盘树,读操作性能相对被弱化;不擅长区间范围的读操作; 归并操作较耗费资源。

如果说B/B+树的读写性能基本平衡的话,LSM树的设计原则通过舍弃部分读性能,换取了无与伦比的写性能。该数据结构适合用于写吞吐量远远大于读吞吐量的场景,得到了NoSQL届的喜爱和好评。

思考:LSM磁盘树可以很多层,每层可有多个SSTable,多个SSTable不像单个B+树那样可以直接按照key查找值,而是一个key可以存在不同层的不同SSTable中(一层多个SSTable只能有一个相同key),这样查时间比较久的数据的时候如何保证找到最新的key以及值?一个key可以存在不同level中,但是上一层的level的key新鲜度总是高于下层的,同一层中多个SSTable只有一个key。

思考1:内存树可以使用什么结构?取决于应用场景,可以使用哈希表、红黑树、跳表等,比如需要范围扫描,可以使用跳表或者红黑树、AVL比较合适。

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

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

相关文章

QT day04

一、思维导图 二、登录界面优化 代码: 界面: *{background-color: rgb(255, 255, 255); }QFrame#frame{border-image: url(:/Logo/shanChuan.jpg);border-radius:15px; }#frame_2{background-color: rgba(110, 110, 110, 120);border-radius:15px; }Q…

ionic 项目通过 android studio 打开报错 capacitor.settings.gradle 文件不存在

问题出现 原因分析 在程序相应的目录上面,没有找到对应的配置文件,但是这个文件不是我们自己生成的,而是通过 ionic 编译之后生成。 处理方案 先执行 ionic build,将 ionic 项目打包出来然后执行 npx cap sync 再使用 Android…

【小白专用 已验证24.6.18】C# SqlSugar操作MySQL数据库实现增删改查

【小白专用24.6.18】C# SqlSugar:连接数据库实现简单的,增、删、改、查-CSDN博客 SqlSugar .Net ORM 5.X 官网 、文档、教程 - SqlSugar 5x - .NET果糖网 SqlSugar项目创建 通过NuGet包管理器搜索SqlSugar(MySql还要安装MySql.Data、Newton…

C++ | Leetcode C++题解之第165题比较版本号

题目&#xff1a; 题解&#xff1a; class Solution { public:int compareVersion(string version1, string version2) {int n version1.length(), m version2.length();int i 0, j 0;while (i < n || j < m) {long long x 0;for (; i < n && version1[…

HTML+CSS+PHP实现网页留言板功能(需要创建数据库)

话说前头&#xff0c;我这方面很菜滴。这是我网页作业的一部分。 1.body部分效果展示&#xff08;不包括footer&#xff09; 2、代码 2.1 leaving.php&#xff08;看到的网页&#xff09; <!DOCTYPE html> <html lang"en"> <head> <met…

Flask之模板

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、模板的基本用法 1.1、创建模板 1.2、模板语法 1.3、渲染模板 二、模板辅助工具 2.1、上下文 2.2、全局对象 2.3、过滤器 2.4、测试…

低市值Pow赛道解析,探寻百倍潜力项目

随着铭文的火爆出圈&#xff0c;比特币减半的到来&#xff0c;关于Pow赛道的讨论也在变得火热&#xff0c;不少投资机构都将Pow赛道作为2024年分析的重点。Pow赛道又来已久&#xff0c;不少项目的市值都超过10亿美元&#xff0c;而对于大多数投资者来说&#xff0c;低市值高回报…

Mp3文件结构全解析(一)

Mp3文件结构全解析(一) MP3 文件是由帧(frame)构成的&#xff0c;帧是MP3 文件最小的组成单位。MP3的全称应为MPEG1 Layer-3 音频 文件&#xff0c;MPEG(Moving Picture Experts Group) 在汉语中译为活动图像专家组&#xff0c;特指活动影音压缩标准&#xff0c;MPEG 音频文件…

Spring事务 和 事务传播机制

这里的 事务 和之前 MySQL的事务 一样&#xff0c;都是表示将⼀组操作封装成⼀个执⾏单元&#xff08;封装到⼀起&#xff09;&#xff0c;要么全部成功&#xff0c;要么全部失败。 Spring 中事务的实现 1. 编程式事务&#xff08;手动档&#xff09;。 package com.example.…

图论之岛屿系列

图论之岛屿系列 形成模板进行学习&#xff0c;加快学习效率 深度优先遍历 # 可以直接改原始grid的采用直接改的方案来完成修改&#xff0c;减少了内存开支 def dfs(self, grid, i, j):if i < 0 or j < 0 or i > len(grid) or j > len(grid[0]) or grid[i][j] &…

无引擎底层游戏开发(1):EasyX图形库引入 + 跟随鼠标移动的小球

来自bilibili up主的Voidmatrix的视频教程&#xff1a;【从零开始的C游戏开发】 一、图形库引入 EasyX在国内文档最多&#xff0c;而且功能函数齐全&#xff0c;最适合入门。 环境配置&#xff1a;vs2022 &#xff08;官网下载免费版&#xff09; 百度搜EasyX官方&#xff0…

AI搜索工具,提升你的工作效率

当我们需要查询某个内容的时候&#xff0c;除了可以通过搜索引擎来查找&#xff0c;还可以通过AI搜索工具来查找。AI搜索工具会智能的从网络信息中查找答案并整理后给我们结果。除了搜索结果&#xff0c;AI搜索工具还可以用来帮我们创作内容&#xff0c;比如你需要写一篇文章&a…

【博弈】843. 猜猜这个单词

本题涉及知识点 博弈 LeetCode843. 猜猜这个单词 给你一个由 不同 字符串组成的单词列表 words &#xff0c;其中 words[i] 长度均为 6 。words 中的一个单词将被选作秘密单词 secret 。 另给你一个辅助对象 Master &#xff0c;你可以调用 Master.guess(word) 来猜单词&…

Harbor本地仓库搭建003_Harbor常见错误解决_以及各功能使用介绍_镜像推送和拉取---分布式云原生部署架构搭建003

首先我们去登录一下harbor,但是可以看到,用户名密码没有错,但是登录不上去 是因为,我们用了负债均衡,nginx会把,负载均衡进行,随机分配,访问的 是harbora,还是harborb机器. loadbalancer中 解决方案,去loadbalance那个机器中,然后 这里就是25机器,我们登录25机器 然后去配置…

CentOS 7x 使用Docker 安装oracle11g完整方法

1.安装docker-ce 安装依赖的软件包 yum install -y yum-utils device-mapper-persistent-data lvm2添加Docker的阿里云yum源 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo更新软件包索引 yum makecache fast查看docker…

MacOS - command not found: brew

问题描述 command not found: brew 原因分析 没有安装 Homebrew&#xff0c;安装后即可使用~ 解决方案 打开终端&#xff0c;输入&#xff1a;/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"&#xff0c;点击回车 在弹出…

FlinkCDC介绍及使用

CDC简介 什么是CDC&#xff1f; cdc是Change Data Capture(变更数据获取)的简称。核心思想是&#xff0c;监测并捕获数据库的 变动(包括数据或数据表的插入&#xff0c;更新以及删除等)&#xff0c;将这些变更按发生的顺序完整记录下来&#xff0c;写入到消息中间件以供其它服…

归并排序 (递归实+非递归)

前言 归并排序是一种逻辑很简单&#xff0c;但是实现有点难度的排序&#xff0c;尤其是非递归&#xff0c;对于区间的把握更是需要一点逻辑的推导&#xff0c;但是没有关系&#xff0c;轻松拿捏 归并排序gif 归并排序单趟实现 1&#xff0c;创建tmp数组&#xff0c; 2&#xff…

《Windows API每日一练》4.6 矩形、区域和裁剪

在前面的4.3节中我们讲述了绘制矩形的API函数Rectangle和RoundRect。本节我们将介绍另外一组使用RECT矩形结构和区域的绘图函数。 本节必须掌握的知识点&#xff1a; 矩形 第28练&#xff1a;绘制随机矩形 矩形与区域的裁剪 第29练&#xff1a;区域裁剪 4.6.1 矩形 ■FillRe…

HarmonyOS开发知识 :扩展修饰器,实现节流、防抖、权限申请

引言 防重复点击&#xff0c;利用装饰器面向切面&#xff08;AOP&#xff09;的特性结合闭包&#xff0c;实现节流、防抖和封装权限申请。 节流 节流是忽略操作&#xff0c;在触发事件时&#xff0c;立即执行目标操作&#xff0c;如果在指定的时间区间内再次触发了事件&…