数据的存储结构浅析LSM-Tree和B-tree
本篇主要讨论的是不同存储结构(主要是LSM-tree和B-tree),它们应对的不同场景,所采用的底层存储结构,以及对应用以提升效率的索引。
所谓数据库,最基础的功能,就是保存数据,并且在需要的时候可以方便地检索到需要的数据。在这个基础上,演化出了不同的数据库系统,以及多种索引机制帮助检索数据。这篇我们就来讨论几种常见的数据存储和索引机制,主要是B-tree,LSM-Tree,以及它们对应的优缺点。
顺序存储与哈希索引
试想一下,如果按照保存数据,并且在需要的时候可以方便地检索到需要的数据这一标准,设计一个简单的数据库,那么最简单的做法应该怎么做呢?
最简单的做法,就是通过顺序保存数据到一个日志文件中。然后通过索引,这里以哈希索引为例(比如java的hashMap),记录每条数据的key以及对应的位移,将其保存到内存中,避免随机检索巨大的开销。值得注意的是,引入索引,虽然会显著提高查询效率,但会略微降低写入速度。因为每次写入的时候都需要额外写入到哈希索引中,这一点对大部分索引都是适用的。
上图为哈希索引示例,下面是顺序存储在磁盘上是日志数据,上面的内存中的哈希索引。哈希索引是很多复杂索引的基础,比如在mysql中就有提供哈希索引的选项,当然哈希索引并不常用,因为它最基础,同时也意味着它最容易被优化。
上述形式的顺序存储+哈希索引中,增加数据和查找数据相对容易理解,而修改数据则可以通过将新数据追加到文件尾部,重新生成索引实现,删除操作则可以给与哈希索引一个标识符实现(如对应key置为-1)。
但这样有一个问题,可能会出现磁盘耗尽的情况。针对这一个问题,我们可以将日志文件拆分成多个一定大小的文件段(这里的文件段可以理解为接受统一管理的数据文件)。当一个文件段达到一定大小,比如4kb的时候,就关闭它,新建一个文件段。而旧的文件段可以进行压缩,前面提到过,删除和修改都是通过追加日志相同的key-value实现的,那么早先的数据其实就已经没用的,所以压缩的时候只保留最新的key数据。压缩到过程如下面这张图所示:
图中上面的部分就是顺序存储的数据,可以发现其中有很多的key都是相同的,这是因为顺序存储情况下,修改数据就是不断新写入相同的key。这种情况我们要的只有相同key的最新的value。所以压缩过程也是一个清理磁盘的过程。
压缩合并过程可以由后台进行默默进行,所以不必担心这个过程影响查询性能。上图中只有一个数据文件段,但实际上可以有多个文件段,多个文件段也可以合并(类似于Hbase中多个文件的merge操作)。
当然这样的优化可以极大程度节省空间,但必不可少得会给检索带来时间上的损耗。在多个文件段的情况,每个文件段都有自己的哈希索引,故而要查找数据会首先根据key查找内存中最新文件段的哈希索引,如果找不到,那么找次新文件段的哈希索引,接着找次新的哈希索引,直到遍历所有文件段的哈希索引。
综上,顺序存储+哈希索引优点明显,简单,高效。缺点是哈希索引需全部存到内存(如果将哈希索引放到磁盘那相当于放弃了检索的高效),并且难以实现区域查询。
为了解决它的这些问题,我们可以将哈希索引做一些小小的改变。具体来说,就是让文件段的数据,按key进行排序存储。这样会带来哪些改变呢?
SSTable和LSM tree
将数据文件段中的数据按key进行排序,并且保证相同的key只出现一次(在压缩的时候保证),这种格式就称之为排序字符串表,简称SStable(Sorted String Table)。
将数据按Key进行排序后有以下几个好处:
- 合并更加简单高效,即使数据文件段大于内存,也可以使用类似归并排序算法进行数据段的压缩,即将一个大文件拆成多个小数据进行压缩。如果多个文件段中有相同的key,那么以最新的文件段的key为准。
- 缓解哈希索引需要整个hashMap存储到内存的窘境。因为key是排序的,所以可以在内存中维持一个稀疏索引,存储每个key的范围,具体见下图。并且这个稀疏索引所需的内存空间是很小的。
通过稍微改变一下文件段的结果,就获得如此多的好处。但还有一个问题,前面的哈希索引是基于顺序存储的日志文件的,要让SStable按key排序,那就不能顺序存储磁盘了呀(即无法存储的时候立即写入磁盘)!!的确是这样,虽然也可以使用类似B-tree来实现磁盘上的排序存储,但转换下思路,其实将数据先保存在内存中其实更加方便。
具体实现流程,是在内存中维护一个类似TreeMap的数据结构用于存储数据(TreeMap底层是基于红黑树对存储的key进行排序的。无论我们按照什么样的顺序存储数据,TreeMap总是会将数据按照key进行排序)。这个TreeMap称为内存表,当内存表超过一定阈值的时候,就将其写入到磁盘中,成为SStable,因为已经排好序,所以写入的效率其实比想象的要高。后期再对磁盘中的SStable进行压缩与合并操作。
当需要根据key检索的时候,会先去内存表中检索,找不到再去最新的SStable,再去次新的SStable,直到遍历完全部。
上述这种索引结构被称为之LSM-Tree,全称是Log-Structured Merge-Tree,即日志合并树。而这种基于合并和压缩文件原理的存储引擎被称为LSM存储引擎,其中比较为人所知的是Hbase。
B-Tree
最后,我们再来讨论流传最久的数据库村粗结构。与LSM-Tree这几年才逐渐为人所知不同,B-tree存储结构担得起经久不衰这四个字。
B-tree本身是一种树形的数据结构,更具体点说是一颗平衡查找树,它也是通过存储顺序的key存储数据(这一点和SStable有相似之处)。不同于前面的LSM-tree的文件段,B-tree将数据库分解成固定大小的块或页,通常一个页大小是4kb。这种分配方法更加贴合底层的磁盘。
当需要进行查找的时候,总是从根开始,根据范围跳转到对应的key,而其对应的value可以是值本身,也可以是指向存储对应数据的磁盘地址。下图是一个具体的例子:
而在更新或插入的时候,有可能会出现没有足够空间来容纳新key的问题,这时候就会发生分裂。分裂操作是比较危险的,在分裂的时候如果数据库崩溃,可能会导致索引被破坏。为了防止这个问题,可以引入预写日志(write-ahead log,WAL)机制。mysql的binlog就是这样的东西。具体说就是在执行操作的时候,将此次操作写入一个只允许追加的文件中,这样一来当崩溃的时候就可以检查日志并进行恢复。
存储结构的比对
从使用的角度上来说,B-tree等索引存储结构多用于OLTP型的数据库,因为这类数据库主要以事务,或是行级别的读取和存储为主的(比如Mysql)。换句话说,这种类型的数据库更多的操作是小批量或单行级别的更新或读取,并且可能还有事务方面的需求,这种类型正是B-tree结构所擅长的。
而 LSM-tree则多用于大规模数据情况下的检索分析和快速写入的情况。在写入的性能上,因为上直接写入内存再定期刷入到磁盘中,所以写入操作对用户的感知而言上非常迅速的。而检索速度也因为key顺序存储,可以快速定位到key对应的位置,因而具有较好的检索性能。
但是LSM-tree比较显著的应用方向还是在大规模分析这方面,在大规模分析(OLAP)场景下,数据通常都是列式存储,并且需要全表扫描。其中磁盘数据可以使用二进制进行压缩,读取的时候可以有效减少磁盘IO的处理时间(与之相比,B-tree等存储结构就无法充分压缩,因为每次都只处理小部分数据)。同时在存储文件中还能再进一步切分,比如将列式数据按照水平切分成不同的Page,同时存储一些简单的索引,用来指定不同Page大概范围,Hadoop的存储数据格式Parquet就是类似的设计。
小结
本篇主要讨论了几种基础的存储结构和索引以及其对应使用场景,限于篇幅,更多索引的变种无法多加讨论,比如B-tree的优化版B+tree,多列索引等。
其实大部分数据库或者说存储引擎,都是针对不同的场景下,在旧有的基础上进行一定程度的微改造创新,但大体的结构依旧是以上述两三种为准,了解了上述几种结构,对数据存储方面应该能够有一个感性的认知了。
此外本章多参考自《DDIA》第三章节,对分布式系统感兴趣的童鞋可以看看此书,肯定不会失望的。
以上~
数据的存储结构浅析LSM-Tree和B-tree的更多相关文章
- 【SQL SERVER重新认识】数据内部存储结构简单探索
数据库经常需要打交道,但是从来没想过数据库内部是如何存储数据. 今天探索一下数据库内部如何存储数据,从下面几个方面探索 数据库内部如何存储数据 索引数据如何存储 操作数据对存储影响 总结 数据库内部如 ...
- SQL SERVER大话存储结构(3)_数据行的行结构
一行数据是如何来存储的呢? 变长列与定长列,NULL与NOT NULL,实际是如何整理存放到 8k的数据页上呢? 对表格进行增减列,修改长度,添加默认值等DDL SQL ...
- oracle中的rowid和数据行的结构
在oracle数据库系统中每一行都有一个rowid,oracle数据库系统就是利用rowid来定位数据行的.rowid也是oracle中内置的一个标量数据类型 rowid有一下特点; 是数据库中每一行 ...
- Oracle 体系结构四 逻辑和物理存储结构之间的关系
Oracle数据库从物理存储中完全抽象出逻辑存储.逻辑数据存储采用“段”的形式.段的类型有很多种:典型的段是“表”.这些段以物理形式存储在数据文件中.通过表空间将逻辑存储从物理存储中抽象出来.逻辑结构 ...
- B-Tree和 B+Tree的数据存储结构
B+树索引是B+树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引.B+树中的B代表平衡(balance),而不是二叉(binary),因为B+树是从最早的平衡二叉树演化而来的.在讲B ...
- Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构
Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构 1. 索引的分类1 1.1. 按照存储结构划分btree,hash,bitmap,fulltext1 1.2. 索引的类型 按查找 ...
- Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构
Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构 1. 索引的分类1 1.1. 索引的类型 按查找方式分,两种,分块索引 vs编号索引1 1.2. 按索引与数据的查找顺序可分为 正 ...
- 0809MySQL实战系列:大字段如何优化|数据存储结构
转自https://yq.aliyun.com/articles/59256?spm=5176.100239.blogcont59257.9.5MLR2d 摘要: 背景 线上发现一张表,1亿的数据量, ...
- 联合索引在B+树上的存储结构及数据查找方式
能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! 引言 上一篇文章<MySQL索引那些事>主要讲了MySQL索引的底层原理,且对比了B ...
随机推荐
- SunOS下root账户无法执行crontab问题
SunOS下root账户无法执行crontab问题 直接步入正题,处理方法如下: 1.查看可执行crontab的用户: more /etc/cron.d/cron.deny 2.修改crontab ...
- 题解 洛谷P2959 【[USACO09OCT]悠闲漫步The Leisurely Stroll】
原题:洛谷P2959 不得不说这道题的图有点吓人,但实际上很多都没有用 通过题上说的“三岔路口”(对于每一个节点有三条连接,其中一条连接父节点,另外两条连接子节点)和数据,可以那些乱七八糟的路和牧场看 ...
- Python中的时间与日期
本文简要介绍datetime,time模块的简要用法. datetime模块 datetime模块主要有四个主要的对象. date 处理年.月.日 time处理时.分.秒.微秒 datetime处理日 ...
- PAT 1028 List Sorting (25分) 用char[],不要用string
题目 Excel can sort records according to any column. Now you are supposed to imitate this function. In ...
- zsy后台管理系统-架构设计
Zsy框架总体架构设计 1.Mysql数据库,存储所有表的数据. 2.Zsy-基础项目(Zsy-Model,Zsy-Dao,Zsy-Service,Zsy-Web),基于SSM框架.项目功能包含基本的 ...
- webpack指南(四)shimming
shimming 将一个新的 API 引入到一个旧的环境中,而且仅靠旧的环境中已有的手段实现. ProvidePlugin 我们在程序中暴露一个变量,通知webpack某个库被使用,webpack将在 ...
- js性能优化之---防抖函数
使用场景 input的时时触发搜索功能 scroll事件的滚动条位置的监测 resize事件监听窗口变化等 举个栗子(input框时时触发搜索功能) 普通未防抖款 var textElement = ...
- ConcurrentHashMap.Segment源码解析
ConcurrentHashMap通过将完整的表分成若干个segment的方式实现锁分离,每个segment都是一个独立的线程安全的Hash表,当需要操作数据时,HashMap通过Key的hash值和 ...
- poj2823单调队列认知
Sliding Window Time Limit: 12000MS Memory Limit: 65536K Total Submissions: 62930 Accepted: 17963 ...
- 四、HTML属性—— HTML 元素提供的附加信息
HTML属性 (1)属性一般描述于开始标签 (2)属性总是以名称/值对的形式出现,比如:name="value" (3)使用小写属性 HTML属性值 应该始终被包括在引号内. —— ...