随着数据量的增大,传统关系型数据库越来越不能满足对于海量数据存储的需求。对于分布式关系型数据库,我们了解其底层存储结构是非常重要的。本文将介绍下分布式关系型数据库 TiDB 所采用的底层存储结构 LSM 树的原理。
LSM 树(Log-Structured-Merge-Tree) 日志结构合并树由 Patrick O’Neil 等人在论文《The Log-Structured Merge Tree》(https://www.cs.umb.edu/~poneil/lsmtree.pdf)中提出,它实际上不是一棵树,而是2个或者多个不同层次的树或类似树的结构的集合。
LSM 树的核心特点是利用顺序写来提高写性能,代价就是会稍微降低读性能(读放大),写入量增大(写放大)和占用空间增大(空间放大)。
LSM 树主要被用于 NoSql 数据库中,如 HBase、RocksDB、LevelDB 等,知名的分布式关系型数据库 TiDB 的 kv 存储引擎 TiKV 底层存储就是用的上面所说的 RocksDB,也就是用的 LSM 树。
LSM 树由两个或多个树状的结构组成。
这一节我们以两个树状的结构构成的简单的双层 LSM 树举例,来简单说下 LSM 树大概思路,让大家对 LSM 树实现有个整体的认识。
原论文中的图
双层 LSM 树有一个较小的层,该层完全驻留在内存中,作为 C0 树(或 C0 层),以及驻留在磁盘上的较大层,称为 C1 树。
尽管 C1 层驻留在磁盘上,但 C1 中经常引用的节点将保留在内存缓冲区中,因此C1经常引用的节点也可以被视为内存驻留节点。
写入时,首先将记录行写入顺序日志文件 WAL 中,然后再将此记录行的索引项插入到内存驻留的 C0 树中,然后通过异步任务及时迁移到磁盘上的 C1 树中。
任何搜索索引项将首先在 C0 中查找,在 C0 中未找到,然后再在 C1 中查找。
如果存在崩溃恢复,还需要读取恢复崩溃前未从磁盘中取出的索引项。
将索引条目插入驻留在内存中的 C0 树的操作没有 I/O 成本,然而,与磁盘相比,容纳 C0 组件的内存容量成本较高,这对其大小施加了限制。达到一定大小后,我们就需要将数据迁移到下一层。
我们需要一种有效的方法将记录项迁移到驻留在成本较低的磁盘介质上的 C1 树中。为了实现这一点,当插入达到或接近每一层分配的最大值的阈值大小,将进行一个滚动合并(Compact)过程,用于从 C0 树中删除一些连续的记录项,并将其合并到 C1 中。
Compact 目前有两种策略,size-tiered 策略,leveled策略,我们将在下面的内容里详细介绍这两种策略。
在 C0 树中的项迁移到驻留在磁盘上的C1树之前,存在一定的延迟(延迟),为了保证机器崩溃后C0树中的数据不丢失,在生成每个新的历史记录行时,首先将用于恢复此插入的日志记录写入以常规方式创建的顺序日志文件 WAL 中,然后再写入 C0 中。
LSM树有三个重要组成部分,MemTable,Immutable MemTable,SSTable(Sorted String Table),如下图。
这张经典图片来自 Flink PMC 的 Stefan Richter 在Flink Forward 2018演讲的PPT
这几个组成部分分别对应 LSM 树的不同层次,不同层级间数据转移见下图。这节就是介绍 LSM 树抽象的不同层的树状数据结构的某个具体实现方式。
MemTable 是在内存中的数据结构,用于保存最近更新的数据,会按照 Key 有序地组织这些数据。LSM 树对于具体如何组织有序地组织数据并没有明确的数据结构定义,例如你可以任意选择红黑树、跳表等数据结构来保证内存中 key 的有序。
为了使内存数据持久化到磁盘时不阻塞数据的更新操作,在 MemTable 变为 SSTable 中间加了一个 Immutable MemTable。
当 MemTable 达到一定大小后,会转化成 Immutable MemTable,并加入到 Immutable MemTable 队列尾部,然后会有任务从 Immutable MemTable 队列头部取出 Immutable MemTable 并持久化磁盘里。
有序键值对集合,是 LSM 树组在磁盘中的数据结构。
其文件结构基本思路就是先划分为数据块(类似于 mysql 中的页),然后再为数据块建立索引,索引项放在文件末尾,并用布隆过滤器优化查找。
当某层数据量大小达到我们预设的阈值后,我们就会通过 Compact 策略将其转化到下一层。
在介绍 Compact 策略前,我们先想想如果让我们自己设计 Compact 策略,对于以下几个问题,我们该如何选择。
不同的选择会造成不同的读写策略,基于以上 3 个问题,又带来了 3 个概念:
不同的策略实际就是围绕这三个概念之间做出权衡和取舍,我们主要介绍两种基本策略:size-tiered 策略和 leveled 策略,这两个策略对于以上 3 个概念做了不同的取舍。
由此可以看出 size-tiered 策略几个特点:
由此可以看出 leveled 策略几个特点:
从 LSM 树的名字,Log-Structured-Merge-Tree 日志结构合并树中我们大概就能知道 LSM 树的插入、修改、删除的方法了——顺序追加而非修改(对磁盘操作而言)。
LSM 树特点:顺序写入、Compact 操作、读、写和空间放大。
LSM 树适用场景:对于写操作吞吐量要求很高、读操作吞吐量要就较高的场景,目前主要在 NoSql 数据库中用的比较多。
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
您将如何构建一个简单的Sinatra应用程序?我正在制作,我希望该应用具有以下功能:“应用程序”更像是一个包含所有信息的管理仪表板。然后另一个应用程序将通过REST访问信息。我还没有创建仪表板,只是从数据库中获取东西session和身份验证(尚未实现)您可以上传图片,其他应用可以显示这些图片我已经使用RSpec创建了一个测试文件通过Prawn生成报告目前的设置是这样的:app.rbtest_app.rb因为我实际上只有应用程序和测试文件。到目前为止,我已经将Datamapper用于ORM,将SQLite用于数据库。这是我的第一个Ruby/Sinatra项目,所以欢迎任何和所有建议-我应
我去了这个website查看Rails5.0.0和Rails5.1.1之间的区别为什么5.1.1不再包含:config/initializers/session_store.rb?谢谢 最佳答案 这是删除它的提交:Setupdefaultsessionstoreinternally,nolongerthroughanapplicationinitializer总而言之,新应用没有该初始化器,session存储默认设置为cookie存储。即与在该初始值设定项的生成版本中指定的值相同。 关于
我正在关注Hartl的railstutorial.org并已到达11.4.4:Imageuploadinproduction.我做了什么:注册亚马逊网络服务在AmazonIdentityandAccessManagement中,我创建了一个用户。用户创建成功。在AmazonS3中,我创建了一个新存储桶。设置新存储桶的权限:权限:本教程指示“授予上一步创建的用户读写权限”。但是,在存储桶的“权限”下,未提及新用户名。我只能在每个人、经过身份验证的用户、日志传送、我和亚马逊似乎根据我的名字+数字创建的用户名之间进行选择。我已经通过选择经过身份验证的用户并选中了上传/删除和查看权限的框(而不
我想编写一个ruby脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"
我正在使用mechanize登录网站,然后检索页面。我遇到了一些问题,我怀疑这是由于cookie中的某些值造成的。当Mechanize登录网站时,我假设它存储了cookie。如何通过Mechanize打印出存储在cookie中的所有数据? 最佳答案 代理有一个cookie方法。agent=Mechanize.newpage=agent.get("http://www.google.com/")agent.cookiesagent.cookies.to_scookie返回一个Mechanize::Cookiesobject