LSM 树与 hbase 的读写
Posted technologyDaily
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LSM 树与 hbase 的读写相关的知识,希望对你有一定的参考价值。
hbase 是一款优秀的 nosql 模型,分布式存储,且拥有很好的写入性能。本文主要讨论两个问题:hbase 为什么可以高性能写入,为了获得高性能写入它付出了什么代价?什么业务场景可以考虑使用 hbase?
几种常见的存储引擎
hbase 的高性能写入和它的存储引擎是分不开的,所以我们先来看下最常见的几种存储引擎。
哈希存储
哈希表的实现,对于插入和随机查询,时间复杂度都是 O(1),可以说效率很高,但是不支持范围查找以及排序操作。mysql 中的哈希索引基于此实现,我们来看一个例子,某张数据表有 A、B、C 三个字段,我们在 A、B 两列上建立了一个哈希索引,当我们插入一条记录的时候,会计算下 A、B 两列值的一个哈希值,并保存一下哈希值到具体记录指针的映射,当我们下次要去查找某行的时候,如果指定了 A、B,可以先计算哈希值,然后可以直接定位到具体的行,效率比较高。因为每次查询都要计算哈希值,自然没法很好的支持排序操作和范围查找。可以看到哈希存储效率很高,但是支持的场景是有限的。
变种 B 树存储
变种 B 树的持久化实现,如 mysql 的 B 树索引,B 树出现,解决了哈希索引的一些问题,它能支持排序、范围查找,且效率比较稳定,网上相关资料很多,不再重复。B 树可能的问题是维护 B 树索引是有开销的,每次写入都要同步相应的索引,随着数据增多,叶子节点会分裂,插入的不同数据可能逻辑上是在一起的,物理上却相隔很远,产生大量随机写,写入性能不会很高。
LSM 树存储
B 树解决了哈希索引的一些问题,但是写入性能不是特别高,针对高性能写入,出现了日志结构(LSM)树,它是怎么提高写性能的呢?又付出了什么代价呢?
B 树写入差主要原因是大量随机写,我们知道磁盘的顺序写和随机写性能差距几个数量级,能够用顺序写的话尽量减少随机写,像 Kafka,充分利用顺序写来提高性能。LSM 树把数据分成两大部分存储,一部分存储在磁盘上,一部分存储在内存中,内存中的数据结构要保证数据的有序性,类似跳表、平衡树这些实现都可以。为方便引用,我们把内存中的有序数据文件称为 C0,而磁盘上的数据文件称为 C1。每次数据写入,我们先写入到 C0,当 C0 大小超过某个阈值,会把数据按照顺序写入到磁盘上,磁盘上的数据按照 B 树形式组织,且一旦写入就不能再改变,随着写入增多,C1 上的文件会越来越多,且每个文件的数据都是有序的。我们下面来看几个关键流程:
数据写入:
如上面所说,数据首先写到 C0,超过某个阈值,再把数据写入到 C1,写入时候按照顺序写入,C1 中的数据一旦写入不能再更改,C1 中每个数据文件都是一个 SSTable(SSTable 其实就是一个不可变的有序键值对,可以网上看下相关介绍)。
因为数据一开始写入到 C0,是在内存中,数据可能会有丢失的风险,所以通常会有日志,记录每一笔写入操作,当 C0 中数据万一真的丢失了,我们可以找回。
看完了写入,问题就来了,数据读取流程是怎样的?
首先会去 C0 中查找,如果 C0 中的数据能满足查询的需要,查找出来直接返回
如果 C0 中数据不能满足查询需要,会再去 C1 上找,找的话会从最新添加的文件开始找,因为 C1 上的多个文件可能会有重复的数据,优先取新的版本。每个文件数据都是有序的,所以到每个文件查找的性能都不会太差。
照前面所说,随着数据写入, C1上的文件会越来越多,查找速度不是会越来越慢吗?
是的。所以 C1 有合并操作,每过一段时间定期合并,因为每个文件都是有序的,合并的话就是归并过程,速度也比较快,归并完了仍然是有序的,这样可以让 C1 中文件个数不是特别多。
一些提高查询性能的手段
布隆过滤器,内存中为 C1 每个文件维护一个布隆过滤器结构,每次查询数据的时候,可以快速判断数据是否在 C1 的某个文件上,减少不必要的读开销。
随着 C1 中文件合并,可能个别文件特别大,也可能会影响查询速度,对此,诸如 LevelDB 等的数据库采用了一种分级压缩的方法,感兴趣的可以网上找下相关资料。
LSM 在 hbase 应用
我们主要可以看下 hbase 模型里几个组件和 C0、C1 等的对应。
memstore 相当于 C0 中的数据,都保存在内存,hfile 相当于 C1 上的若干个文件,数据写入时候,先写入到 memstore 中,当 memstore 中数据满了,再写入到 hfile,读取的话先读 memstore,满足不了查询需求的话再读 hfile。hbase 有 WAL,防止memstore 中数据丢失。
hbase 的 blockcache?
memstore 和 blockcache 都是缓存,memstore 是写缓存,blockcache 是读缓存,会把一些磁盘 IO 的读取结果以块为单位缓存。
不了解 LSM 树上来就看 hbase 的物理存储模型,可能会蒙,不知道为什么要这么设计,完全是死记。但是当了解了 LSM 树的基本原理,回过头来再看 hbase 的物理模型就比较清晰了。
什么业务场景可以考虑使用 hbase?
写入量比较大,允许牺牲一定的读性能,之前线上环境测试过,hbase 响应大概是mysql 10倍+
没有太多复杂维度的查询,hbase 为了提高查询性能需要在 rowkey 上做文章,或者自己维护一个二级索引,二者各有各的问题,前者的话 rowkey 设计的要满足查询的条件,假设查询有多个维度,那可能要多张表,后者自己维护,一致性需要保证,而且查询要多一次网络开销。网上也有一些开源的 hbase 二级索引实现,不过看一些反馈坑也不少
hbase 读性能比较依赖缓存,缓存命中率低情况下,很大概率从 hfile 读数据,根据前面介绍的 LSM 树的特点,可能还会读取多个文件,效率比较低
hbase 碰到 full gc 的话比较尴尬,业务上可能就需要能容忍这偶尔的情况。
以上是关于LSM 树与 hbase 的读写的主要内容,如果未能解决你的问题,请参考以下文章
Log Structured Merge Trees (LSM)