可能有些童鞋已经发现:1.阿里云上的Hbase服务,基于Hbase深度定制和扩展,能比较好的支持时序场景和全文检索场景,其增强版Lindorm,已经作为单独的云服务售卖,单独演进。2.腾讯云上,基于ES构建了CTSDB时序数据库单独售卖,同时也推出了增强版的ES云服务。可以看出,在这两个服务所在团队中,一边选择基于ES来支持时序和全文检索场景,一边则选择基于Hbase来支持时序和全文检索场景。基于ES来支持时序和全文检索场景是比较好理解的,因为它本身的功能就支持这些,但是Hbase如何比较好支持全文检索场景呢?实际上,阿里云的Hbase服务借助了Solr这个搜索引擎来构建这块的能力,因为Solr和ES一样,都是基于Lucene构建的搜索服务。那为什么阿里不和腾讯一样,直接基于ES来构建这块的能力呢?Hbase和ES又究竟有哪些共同点,有哪些差异点呢?
总的来说,在数据量不是非常大的时候,使用ES更加简单,单纯使用ES就能解决各种查询场景的需求,但是当数据量非常大的时候,现有的社区版本ES则存在不少问题,这个时候往往需要进行一些内核级别的改进(否则就需要更多的业务层干预,并且成本消耗会比较高),类似阿里云和腾讯云上提供的ES服务,实际上都对ES内核做过改进。使用Hbase,则需要依赖更多的组件,这样有好处也有坏处。同时如果要支持全文检索场景,还需要引入Solr或者ES等服务,就如阿里云上的Hbase服务则通过引入Solr来解决全文检索的需求。二者的对比大致如下:
接下来将从某些小点展开看下二者的一些相同点及差异点:
ES的一些重要概念:
Hbase的一些重要概念:
单纯从单机部署的角度看,其实ES和Hbase是差不多的,都是一个安装包稍微配置下就可以启动使用了。因为Hbase单机版部署时,会同时启动Hmaster和HRegionServer,会内置zookeeper,同时通过使用Linux本地文件系统来解决文件的存储。ES默认就是Master节点、协调节点、以及数据节点同时处于一个节点上的。不过单机版的对比没有太多意义,毕竟不可能在生产环境使用。
接下来重点看下ES和Hbase在生产环境中使用的部署架构。
上面第一幅图是ES的部署架构,第二幅图是Hbase的部署架构。
由于ES对于集群元数据的分布式管理,是自己实现的一套逻辑,没有依赖zookeeper,所以少了zookeeper部署依赖。另外对于数据的分布式存储,也没有依赖HDFS,ES是基于Lucene自己实现的分布式存储,所以少了HDFS的部署依赖,所以ES的整个部署显得简单很多。由于ES在默认情况下,是集各种角色于一身的,如果不单独指定角色,则它同时作为maser,协调节点,数据节点,ingest节点等。在数据量不是很大的时候,采用这种部署方式是最简单的。不过随着数据量的增大,读写请求越来越多,考虑到集群的稳定性,这个时候,最好将master角色独立出来,因为master节点相当于集群的大脑,其稳定性尤为重要。并且master节点主要做集群管控相关工作,不承担数据的读写,它的负载并不高,所以一般采用的硬件配置也不需要太高。另外,在有些场景中,如果有必要,也可以将ES的协调节点独立出来。对于ES节点的角色配置,相对来说也是比较简单的,只需要在配置文件里指定下角色类型即可。
对于Hbase的部署:
对比写入之前,先了解下LSM树:为了规避磁盘随机写入问题,LSM树将一棵大树拆分成N棵小树,这些小树首先会写入内存中,随着小树越来越多,这些小树就会批量更新到磁盘中去,同时小树也会定期merge成大树,提高查询效率。ES和Hbase都采用了LSM树方式进行数据写入,所以他们写入性能都是比较高的,下面具体看下两者的写入流程。
Hbase写入流程:
ES写入流程:
和数据写入相比,ES和Hbase的数据查询都要复杂不少。另外,和Hbase相比,ES支持的查询场景更加丰富。对于ES包含get和search两种类型。与之相对应的,Hbase包含get和scan。因为get比较简单,这里主要对比search和scan。
Hbase和ES查询的复杂性,主要是因为它们一次查询都可能涉及多个Hbase的Region(或ES的shard),多块缓存,或者多个数据文件。另外ES和Hbase中的更新和删除操作都没有真正地更新或删除原始数据,更新都是通过多版本号来实现,删除都是通过加’deleted’标签的方式实现。这就使得查询时,需要感知这些,才能保证查询的准确性。另外,在ES中,由于查询场景的多样性,需要考虑的查询场景是非常多的。
Hbase查询主要专注于rowkey的范围查询,它的各种设计都围绕着这种场景展开,在这种场景下,它的查询是很快的。主要原因有:1.它会将一次大的请求切分成很多小的请求。2.它能根据keyRange过滤、timeRange过滤、布隆过滤器等快速过滤不符合条件的HFile。3.基于HFile的索引树以及BlockCache机制可以快速找到HFile中对应的key。
ES的查询,最主要的优势在于它基于倒排索引构建的全文检索能力,这块是Hbase所不具备的能力。当然,ES除了倒排索引,也有基于DocValues的正排索引,DocValues采用列式存储,可以比较快速地实现聚合和排序查询场景。通过ES提供的DSL和Aggregations,以及SQL查询引擎,用户可以方便的实现各式各样的查询需求。但是ES的索引成本是比较高的,主要是前面提到的,ES为了提高搜索性能,会将一些索引数据加载到堆内存。这块消耗最大的是ES里面的倒排索引对应的FST索引文件,它对内存的消耗比较高,也就是文章最开始提到的内存管理差异点,这里不再赘述。
在大数据写入场景中,由于读写请求的不均衡,各个节点之间就可能出现热点问题,就可能出现节点负载很不均衡的情况,从而进一步影响了整个集群的稳定性。所以控制好整个集群的负载均衡是很重要的一件事。在负载均衡这块,我觉得Hbase是要比ES成熟不少的。
首先讲下ES,它只保证集群中各个节点的分片数量是相对均衡的,但不保证节点真正的负载是相对均衡的。因为可能各个分片之间的数据读写请求差异很大,单纯从分片数量维度做到均衡是远远不够的。所以,在真实使用的过程中,往往需要业务方自己去实现索引的合并,迁移,分裂等功能,从而实现真正的负载均衡,ES只是提供了一些基础的API供用户调用(如:rollover, shrink等)。
Hbase则提供了两种均衡策略,1.SimpleLoadBalancer策略,类似于ES的均衡策略,只是保证各个RegionServer的Region个数基本相等,但没有考虑真正的负载。2.StochasticLoadBalancer策略,它会加权计算各种负载情况(包括:Region个数,Region负载,读写请求数,stroeFile大小,Memstore大小,数据本地化率,移动代价等),这也是Hbase默认的负载均衡策略。
也就是说,Hbase会结合真实的负载情况,自动实现Region的迁移、合并、分裂等操作,可以减少使用方的干预。而ES则不具备这种能力,这也是当前使用ES比较麻烦的一个点。
对于采用LSM树进行数据写入的数据库来说,由于会存在很多小文件,而在对大量小文件进行数据读取的时候,效率比较低。所以,一般都需要对小文件进行合并操作。在ES中,这种操作叫Segments merge。在Hbase中,这种操作叫HFile compaction。另外,在Hbase中,还对compaction进一步细分为minor compaction和major compaction,其中major compaction就是将一个Region下的HFile合并为一个大HFile,这是一个非常昂贵的操作,会在短时间产生大量的IO和网络消耗,一般生产环境对这个操作会非常谨慎。
【触发时机】
在ES中,触发merge动作的时机主要有:1.数据最开始写入memory buffer中,然后间隔一段时间后(间隔时间为refresh-interval,可修改),会执行refresh操作,此时会写入OS的文件缓存中,这个过程会触发一次merge。2.在文件缓存中的segments,当达到indexing buffer的阈值或者达到flush_threshhold,这些segments会执行Flush操作,也就是Lucene的commit操作,会写入磁盘,这个过程也会触发一次merge。3.手动调ES的接口执行merge操作。
在Hbase中,大致和ES是一样的,不过在细节上还是有不少差异。主要有:1.Memstore Flush: 数据最开始写入Memstore中,当满足一定的条件后(如:达到Memstore/RegionServer/Region/HLog级别的限制阈值,或者超过一定的时间周期),会执行Flush。2.后台线程周期性检查,如store中总文件数是否大于阈值,是否满足major compaction条件等。3.手动触发。
【合并策略】
由于文件的合并在短时间会消耗大量的IO和网络带宽,所以是一个比较昂贵的操作,选择什么样的合并策略,是一件很重要的事情。ES的合并策略比较单一且用户没有其他选择,只能对这个策略里的一些参数进行调整(如:每层允许的segments数量)。相对于ES,Hbase提供了更多的合并策略,用户可以根据自己的业务特点选择合适的合并策略(如:Exploring Compaction Policy, Stripe Compaction等)。
当集群越来越大之后,比如几百个节点的集群,出现一两个节点宕机,应该是时长有之的情况,那么在节点出现宕机时,能否快速恢复,并且不会造成数据丢失,不会造成读写异常,就显得很重要了。
在ES中,主要存在Master,协调节点,数据节点三种角色。对于master,如果是独立部署,一般会有3个节点,可允许1个节点挂掉。如果是非独立部署,则允许一半以上的master节点挂掉,不会影响master工作。由于master本身负载不高,采用独立部署模式,一般不会出问题。对于协调节点,因为它本身不存储数据,主要做读写请求转发以及搜索时的数据聚合,协调节点宕机后,master节点会感知到这个变化,后面的读写请求就会转发到其他节点上。宕机的节点恢复后,又会自动加入到集群中,所以协调节点的宕机影响也是很小的。重点是数据节点的宕机,此时影响相对来说就要大不少。当有数据节点宕机后,master会感知到这个变化并通知给其他节点,对于挂掉节点上的主分片,其副本分片马上会升级为主分片(所以主副分片的数据一致性保障是很重要的),之后会另外找其他节点生成一份副本。对于挂掉节点上的副本分片,就只需要另外找一个节点再生成一份副本就行(这里什么时候生成丢失的副本,和选择的策略有关,默认在很短的时间内就会触发,在这种情况下,如果节点过一会就恢复了,就会涉及大量数据移动,所以生成环境中,一般会等待比较长的时间,然后在等待的时间内发出告警,让维护者及时启动宕机节点,如果宕机节点还是无法恢复,才会生成对应副本,这样可以减少很多无用的资源消耗)。
在Hbase中,涉及的组件很多,但由于zookeeper和hdfs都是其依赖的组件,并且他们的可靠性保障以及宕机恢复机制也是比较成熟的,这里暂不讨论。不过Hdfs中的NameNode节点要特别注意下,因为它相当于整个hdfs集群的大脑,一旦它出问题,整个Hbase的读写操作就无法进行下去了。所以生产环境中,NameNode一定不要有单点问题,并且最好不要和DataNode节点混合部署在一台机器上,在hadoop2.0之后,为了解决NameNode的单点问题,已经支持NameNode HA高可靠部署方式了,前面在部署架构中也有说明。
对于Hbase中的Master,它主要负责集群的负载均衡和读写调度,并没有参与用户的读写请求,所以整体负载并不高,并且可以比较简单的实现Master HA高可靠部署,所以Master的宕机恢复是很容易的。最后,就只剩下RegionServer了,实际上RegionServer也不承担数据存储,不过数据写入前,会先写MemStore,并且这部分的数据是写在内存里的。所以,RegionServer宕机后,就涉及到这部分数据的恢复。这里就要用到HLog来进行恢复了,所以在生产环境中,不要轻易禁掉HLog,虽然不写HLog可以提高写入性能,但是一旦出现RegionServer宕机,就会造成数据丢失。
当RegionServer出现宕机时:首先,zookeeper会感知到这个变化,同时把这个变化告诉master。然后,会切分未持久化数据的HLog日志。之后,Master会重新分配宕机RegionServer上的Region。最后,会回放HLog日志补救数据,完成数据恢复。因为整个恢复过程涉及的HLog数据量并不会很大,所以整个恢复过程相对来说还是可控的。
本人专注于大数据和日志领域,欢迎有兴趣的人一起交流。另外,由于本人水平有限,文章中难免会出现一些错误,欢迎大家指正!还有,转载请注明出处,谢谢!