MongoDB、Hbase、Redis和ElasticSearch对比

1、MongoDB

MongoDB最大的特点是表结构灵活可变,字段类型可以随时修改。MongoDB没有MySQL中表结构这样的概念,每一行数据只是简单的被转化成Json格式后存储,可以将任意结构的数据塞入同一个表中。

MongoDB不需要定义表结构这个特点给表结构的修改带来了极大的方便,但是也给多表查询、复杂事务等高级操作带来了阻碍。如果数据的逻辑结构非常复杂,经常需要进行复杂的多表查询或者事务操作,那显然还是MySQL这类关系型数据库更合适。

MongoDB适合表结构经常改变,数据的逻辑结构不复杂、无多表查询操作,数据量又比较大的应用场景

MongoDB底层存储结构是B Tree(平衡树,Balance Tree)。平衡树是一颗查找树,并且所有叶子节点位于同一层。MongoDB层次组织的数据节点非常适合采用B树,而且查找效率较高。
在这里插入图片描述

2、Hbase

作为Hadoop系列产品之一,HBase也继承了Hadoop项目的最大优点,那就是对海量数据的支持,以及极强的横向(存储容量)扩展能力。HBase每一行数据有一个关键字key,一行数据还可以有非常多的列项,数据会按照列进行分组和存储,同一列的数据存储在同一个地方,这也是HBase被称为列式存储数据库的原因。不仅是列,数据的行数到达一定数量后表也会再被拆分。因此,HBase能够把巨大的表分布到很多台机器上,从而容纳规模近乎无限的数据。

HBase的列式存储特性带来了海量数据规模的支持和极强的扩展能力,但是也给数据的读取带来很大的局限。由于只有同一列族的数据才会被存放在一起,而且所有的查询都必须要依赖Key,这就使得很多复杂查询难以进行。例如,如果你的查询条件涉及多个列项,或者你无法获取要查询数据的key,那么查询效率将会非常低下。

HBase的列式存储特点带来了对海量数据的容纳能力,因此非常适合数据量极大,查询条件简单,列与列之间联系不大的轻查询应用场景。HBase是很重的一款产品,需要依赖很多的Hadoop组件,因此如果你的数据规模不大,那就完全没必要杀鸡用牛刀,MongoDB这类产品完全可以更好的满足你的需求。

LSM树

hbase的快速写入依赖于其底层实现Log-Structured-Merge-Tree,它的存储结构像一个上小下大的“树”,越往下存储容量越大。最上面是内存的 C0 层,保存了所有最近写入的 (k,v),这个内存结构是有序的,并且可以随时原地更新,同时支持随时查询。剩下的 C1 到 Ck 层都在磁盘上,每一层都是一个在 key 上有序的结构

在这里插入图片描述当执行写入操作时,首先在写前日志进行记录(Write Ahead Log),然后将数据加到 C0 层。当 C0 层的数据达到一定大小,就把 C0 层 和 C1 层合并Compaction,类似归并排序。合并出来的新的 new-C1 会顺序写磁盘,替换掉原来的 old-C1。当 C1 层达到一定大小,会继续和下层合并,合并之后所有旧文件就可以删掉。

LSM树中的数据可能存在不同的版本,例如在C3层a=2,这时有新的a=1写入,这样就出现了两个版本的a。所以在查询数据时也是从上往下,先检索C0,没有才会向下继续查找。通过给数据增加版本号字段,在 Compaction 时候进行比较只留下最新的版本。

由于采用多级缓存,LSM树在频繁写入、较少查询的场景下性能更高。

LevelDB
在这里插入图片描述可以看到levelDB的组成主要有如下几个部分:

  1. MemTable存放在内存中,用于快速保存最新写入的数据。为防止断电会丢失数据,因此通常会通过WAL(Write-ahead logging,预写式日志)的方式来保证数据的可靠性
  2. 当 MemTable达到一定大小后,会转化成Immutable MemTable,将数据转存到磁盘的SSTable。同时新开一个 memtable 接收新的写入请求
  3. SSTable(Sorted String Table)有序键值对集合,是LSM树组在磁盘中的数据结构。与直接修改原数据不同,LSM树的数据更新是日志式的,直接append一条更新记录。这样就可以不断地将Immutable MemTable flush到持久化存储即可,而不用去修改之前的SSTable中的key,保证了顺序写。因此在不同的SSTable中可能存在相同Key的记录,最新的那条记录才是准确的

读写放大

放大倍数 = 磁盘上实际读写的数据量 / 用户需要的数据量。比如用户本来要写 1KB 数据,在LSM树中写入时可能触发Compact操作,最后往磁盘写了 10KB 的数据,写放大就是 10,读也类似。

3、Redis

Redis以键值对key-value的形式存储数据。其优点如下:

  • 高性能:得益于key-value简单的结构,再加上Redis会把所有数据加载到内存中的,Redis能得到远高于MongoDB这类常规数据库的读写性能
  • 支持list、set等多种数据结构
  • 支持数据持久化、主从复制备份等功能

Redis的key-valule存储带来了性能这个优势,但是也带来了很多局限。

  • 由于查询都依赖key,因此Redis无法提供常规数据库所具备的多列查询、区段查询等复杂查询功能。
  • 由于Redis需要把数据存在内存中,限制了Redis可存储的数据量,难以用在数据规模很大的应用场景中
跳表

Redis采用跳跃列表进行索引,通过多层索引进行快速查询、插入和删除有序连续元素的数据链表,其平均查找和插入时间复杂度都是O(logn)。

在这里插入图片描述
一张跳跃列表的示意图如图所示。每个带有箭头的框表示一个索引节点, 而每层索引是一个稀疏子序列的链表;底部的编号框(黄色)表示有序的数据序列。快速查询开始时,在最稀疏的层次沿着链表进行搜索,直到确定元素在某两个索引节点中间,跳转到下一个层次,重复刚才的搜索,直到在最底层找到需要查找的元素为止。

时间复杂度:如果一个链表有 n 个结点,如果每两个结点抽取出一个结点建立索引的话,那么第一级索引的结点数大约就是 n/2,第二级索引的结点数大约为 n/4,以此类推第 m 级索引的节点数大约为 n/(2^m)。假如一共有 m 级索引,第 m 级的结点数为两个,通过上边我们找到的规律,那么得出 n/(2^m)=2,从而求得 m=log(n)-1。如果加上原始链表,那么整个跳表的高度就是 log(n)。我们在查询跳表的时候,如果每一层都需要遍历 k 个结点,那么最终的时间复杂度就为 O(k*log(n)),忽略常数得 O(log(n))。

空间复杂度:一个链表有 n 个结点,如果每两个结点抽取出一个结点建立索引的话,那么第一级索引的结点数大约就是 n/2,第二级索引的结点数大约为 n/4,以此类推第 m 级索引的节点数大约为 n/(2^m),n/2+n/4+n/8…+8+4+2=n-2,所以跳表的空间复杂度为 o(n)

插入删除:为跳表插入或者删除数据,首先需要找到插入或者删除的位置,然后执行操作。跳表的查询的时间复杂度为 O(logn),插入和删除的时间复杂度为 O(1),所以最终时间复杂度也为 O(log(n))。删除操作除了要删除原始链表中的结点,还要删除索引中的

跳表退化:如果我们不停的向跳表中插入元素,就可能会造成两个索引点之间的结点过多。所以我们需要维护索引与原始链表的大小平衡,也就是结点增多了,索引也相应增加。跳表是通过一个随机函数来维护这个平衡的,当我们向跳表中插入数据的的时候,通过随机函数,来决定在哪一级索引中同时增加索引节点。

4、Elastic Search

常用的全文搜索框架是基于Lucene引擎的ElasticSearch(ES)和Solr,相比于支持多种数据格式的Solr,ES只支持JSON数据,但ES的实时搜索效率较高。它的相关概念和特点如下:

  • 分布式:节点Node是指单个保存数据和索引的服务器,每个节点在启动的时候会分配一个唯一标识符,也可以自定义名称。多个保存数据的节点就构成了集群,节点可以根据名称加入集群。
  • ES中索引Index是相似文档的集合。文档Document是索引的基本单位,ES以json格式保存每个文档。当数据过大超过单个节点的存储空间时,ES会将数据划分为多个分片并保存到不同的节点,并且对分片进行管理。为了提高可用性,还会对分片创建副本,当某几个主机出现问题时,由于副本的存在,系统仍然可用。并且由于分片可副本的存在,可以将搜索负荷分摊到不同的分配或副本,提高了系统的读写吞吐量。
  • 近实时:在索引创建之后ES不会立马写入磁盘,而是储存在缓存中,然后根据刷新策略(默认为1秒)定时将索引写入磁盘,因此搜索会存在1s左右的延迟。这是es对写入和查询一个平衡,这样设置既提升了es的索引写入效率同时也使得es能够近实时检索数据。
  • 多API:ES除了Java原生接口外,还支持RESTful类型的API
  • 面向文档:使用关系型数据库中的表时需要构建对应的实体类,而ES可以直接创建文档并进行存储
  • ES也有很多的短处,最明显的就是字段类型无法修改、写入性能较低和高硬件资源消耗
倒排索引 inverted index

如下所示为ElasticSearch实现搜索的方式–inverted index。对于正向索引forward index,我们是在文章中逐个查找关键字;而倒排索引则是利用关键词将文章串起来。例如我们在扫描第1篇文章时拆分为单个词组,其中有关键词Ada,则为Ada添加索引1,然后在扫描第2篇文章时也发现有Ada,同样将其添加到索引中。这样当我们检索Ada关键字时就可以很快地找到对应的文章。
在这里插入图片描述

小结

在这里插入图片描述

  • 对数据的读写要求极高,并且你的数据规模不大,作短期缓存,选redis;
  • 数据规模较大,对数据的读性能要求很高,数据表的结构需要经常变,有时还需要做一些聚合查询,选MongoDB;
  • 需要进行文档搜索,选ElasticSearch;
  • 需要存储海量数据,那么选HBase
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值