[HBase] LSM树 VS B+树

本文对比了HBase使用的LSM树与传统B+树。LSM树通过将大数拆分为小树并先写入内存来提高写性能,牺牲部分读性能换取更高的写入效率。B+树则在磁盘上构建有序结构以优化读取性能,但随机写入时会产生大量磁盘寻道。

LSM树是HBase里非常有创意的一种数据结构,它和传统的B+树不太一样,下面先说说B+树。

1 B+树

相信大家对B+树已经非常的熟悉,比如Oracle的普通索引就是采用B+树的方式,下面是一个B+树的例子:



根节点和枝节点很简单,分别记录每个叶子节点的最小值,并用一个指针指向叶子节点。

叶子节点里每个键值都指向真正的数据块(如Oracle里的RowID),每个叶子节点都有前指针和后指针,这是为了做范围查询时,叶子节点间可以直接跳转,从而避免再去回溯至枝和跟节点。

B+树最大的性能问题是会产生大量的随机IO,随着新数据的插入,叶子节点会慢慢分裂,逻辑上连续的叶子节点在物理上往往不连续,甚至分离的很远,但做范围查询时,会产生大量读随机IO。

对于大量的随机写也一样,举一个插入key跨度很大的例子,如7->1000->3->2000 ... 新插入的数据存储在磁盘上相隔很远,会产生大量的随机写IO.

从上面可以看出,低下的磁盘寻道速度严重影响性能(近些年来,磁盘寻道速度的发展几乎处于停滞的状态)。

2 LSM树

为了克服B+树的弱点,HBase引入了LSM树的概念,即Log-Structured Merge-Trees。

为了更好的说明LSM树的原理,下面举个比较极端的例子:

现在假设有1000个节点的随机key,对于磁盘来说,肯定是把这1000个节点顺序写入磁盘最快,但是这样一来,读就悲剧了,因为key在磁盘中完全无序,每次读取都要全扫描;

那么,为了让读性能尽量高,数据在磁盘中必须得有序,这就是B+树的原理,但是写就悲剧了,因为会产生大量的随机IO,磁盘寻道速度跟不上。

LSM树本质上就是在读写之间取得平衡,和B+树相比,它牺牲了部分读性能,用来大幅提高写性能

它的原理是把一颗大树拆分成N棵小树, 它首先写入到内存中(内存没有寻道速度的问题,随机写的性能得到大幅提升),在内存中构建一颗有序小树,随着小树越来越大,内存的小树会flush到磁盘上。当读时,由于不知道数据在哪棵小树上,因此必须遍历所有的小树,但在每颗小树内部数据是有序的。



以上就是LSM树最本质的原理,有了原理,再看具体的技术就很简单了。

1)首先说说为什么要有WAL(Write Ahead Log),很简单,因为数据是先写到内存中,如果断电,内存中的数据会丢失,因此为了保护内存中的数据,需要在磁盘上先记录logfile,当内存中的数据flush到磁盘上时,就可以抛弃相应的Logfile。

2)什么是memstore, storefile?很简单,上面说过,LSM树就是一堆小树,在内存中的小树即memstore,每次flush,内存中的memstore变成磁盘上一个新的storefile。

3)为什么会有compact?很简单,随着小树越来越多,读的性能会越来越差,因此需要在适当的时候,对磁盘中的小树进行merge,多棵小树变成一颗大树。


<think> 嗯,用户这次想了解B和B+的原理、特点和应用等知识。从对话历史看,我之前提供的系统级指令要求技术回答必须规范使用数学公式格式,且需要生成相关问题。用户特别提到站内引用[1]的内容,其中对比了B+相对于B的查询优势,包括节点结构、查找稳定性和范围查询效率。 用户应该是数据库或文件系统领域的学习者,可能正在研究索引结构。ta明确要求"原理、特点、应用等相关知识",说明需要体系化的讲解。有趣的是,引用[1]提到B+"更矮胖"这个生动表述,可以在回答中保留这种形象说明。 我需要组织三部分核心内容: 1. 原理部分要解释两种的分裂/合并规则,必须用$$格式展示最小度数t的定义公式 2. 特点对比需突出B+的所有数据在叶子层的特性,可以画ASCII示意图辅助说明 3. 应用场景要关联数据库实例,比如InnoDB的聚簇索引就用B+实现 特别注意要纠正引用[1]的小瑕疵——B查找并非"不用管元素位置",实际上非叶子节点也需要顺序搜索关键字。范围查询部分则要强调B+的链表结构优势,这里可以用->符号表示指针优化。 最后生成的问题要覆盖实现细节(如节点合并算法)和新型数据结构对比(LSM),帮助用户延伸学习。磁盘I/O优化这个点特别值得展开,毕竟这是B系存在的根本原因。 </think> 以下是关于B和B+的详细解析,包含原理、特点和应用场景: --- ### **一、核心原理** #### **1. B(平衡多路搜索)** - **结构特性**: 每个节点最多包含 $m$ 个子节点($m$ 阶),节点存储 **键值+数据**,键值升序排列 满足约束:根节点子节点数 $\in [2, m]$,非根节点子节点数 $\in [\lceil m/2 \rceil, m]$ - **查询流程**: 从根节点开始二分查找,匹配到键值即返回数据;未匹配则进入对应子递归查找 #### **2. B+(B优化版)** - **核心差异**: - 非叶子节点仅存 **键值**(不存数据),数据全部存储在叶子节点 - 叶子节点通过双向链表连接,形成有序序列 - 非叶子节点的键值会重复出现在子节点中(作为分隔键) - **查询流程**: 必须查找到叶子节点才能获取数据,非叶子节点仅起索引作用 --- ### **二、关键特性对比** | **特性** | **B** | **B+** | |------------------|----------------------------------|-----------------------------------| | **数据存储位置** | 所有节点均可存数据 | 仅叶子节点存数据 | | **高度** | 较高(节点存数据导致容量减小) | 更矮胖(节点纯索引,容纳更多键值)| | **查询稳定性** | 非叶子节点命中即可返回(不稳定) | 必须到叶子节点(稳定) | | **范围查询** | 需中序遍历多节点 | 叶子节点链表直接遍历 → **高效** | | **磁盘I/O** | 随机访问可能较多 | 顺序访问为主 → **更适合磁盘** | > 💡 **查询稳定性示例**: > B若在根节点匹配键值 $k_5$ 可直接返回,但B+必须向下查找到叶子节点。 > 虽然单次查询B可能更快,但B+由于**高更低**,整体磁盘I/O更少。 --- ### **三、应用场景** 1. **数据库索引**(主流选择) - **MySQL InnoDB**:使用B+实现聚簇索引 - 叶子节点直接存储行数据,范围查询如 `WHERE id BETWEEN 100 AND 200` 效率极高 - **MongoDB**:B用于索引(支持单点快速查询) 2. **文件系统** - **NTFS/HFS+**:用B+管理磁盘块分配,高效支持大文件连续读取 3. **大数据存储** - **HBase/Cassandra**:LSM(Log-Structured Merge-Tree)底层使用B+结构合并SSTable --- ### **四、性能优势数学解释** 设磁盘页大小 $B$ 字节,键值大小 $K$ 字节,数据大小 $D$ 字节: - **B节点容量**: $\approx \frac{B}{K+D}$ - **B+节点容量**: $\approx \frac{B}{K}$ (无数据占用) - **高度对比**: $$h_{B^+} = \log_{\lceil m/2 \rceil} N \ll h_B = \log_{\lceil m/2 \rceil} N \quad (N \text{为数据总量})$$ → B+层级更低,**查询I/O次数更少**[^1] --- ### **五、B+范围查询示例** ```lua 叶子节点链表: [10->20]->[20->30]->[30->40] (双向指针) 查询 [15,35] 只需: 1. 定位到包含15的叶子节点 2. 向右遍历链表至35所在节点 ``` > 无需回溯父节点,**时间复杂度 $O(\log n + k)$**($k$ 为结果数量) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值