实时流计算系统设计与实现:数据存储-Ad-Hoc查询

摘要:Ad-Hoc查询也称为即席查询,是用户为了某个查询目的,灵活选择查询条件并提交数据库执行,最终生成相应查询结果的过程。例如,一种常见的Ad-Hoc查询例子就是平时我们临时接到领导想要查看某项业务指标的任务,然后使用客户端连接到数据库,即时编写SQL语句并执行,

Ad-Hoc查询也称为即席查询,是用户为了某个查询目的,灵活选择查询条件并提交数据库执行,最终生成相应查询结果的过程。例如,一种常见的Ad-Hoc查询例子就是平时我们临时接到领导想要查看某项业务指标的任务,然后使用客户端连接到数据库,即时编写SQL语句并执行,然后将得到的查询结果上报给领导。

在实时流计算系统中,有时候需要将计算结果最终呈现给最终用户查看。这里的最终用户可能是运营人员,也可能是做决策的领导,还可能是客户。对于最终用户而言,直接写SQL并不方便,而且不现实,因为他们并不知道数据的具体细节。因此,这个时候通常会有一个用户友好的图形化界面来引导用户设置查询条件,用户通过单击相应按钮即可以看到各种图形和报表。说到报表系统,其典型特点是分析的内容丰富多样,可能是某个维度的排序,也可能是某几个组合维度的聚合,还可能是关键字的匹配过滤。特别地,UI通常还需要提供给用户各种查询条件的选择功能,如用户可以选择查询的时间范围、排序方式、过滤条件等。总之,这是一个需求千变万化的场景。作为一个开发人员,可能现在你的脑海中已经浮现出产品经理那轻松的一句:“这不就是加一个查询条件的事么!”。

除了需求灵活多变、查询千变万化以外,报表系统的查询还有一个特点,即它们是需要近实时返回结果的。也就是说,当触发查询后,必须在数秒以内将结果呈现在UI上,否则用户会等得不耐烦,造成不好的用户体验。同时,报表系统的查询频次不会太高,只有在用户需要查看报表时才需要进行查询操作。所以,相比数据上报接口使用的频率而言,报表查询的频次会低很多。

针对报表这种查询灵活、需要近实时响应结果的场景而言,比较好的选择是使用搜索引擎一类的数据存储和查询方案,ElasticSearch就是一个不错的选择。这是因为,搜索引擎通常采用倒排索引的方式来管理查询数据。诸如MySQL这样的非倒排索引数据库,如果要针对指定的查询条件加快查询效率,则必须预先建好索引。

可是,在报表系统这种场景下,一方面,查询需要灵活多变、随意组合;另一方面,随着产品的演进,需求可能在不断调整和增加。这就需要我们在数据库中建立大量的单键索引和多键索引。而在产品新版本上线时,还可能需要更新索引和新增新索引。这一切都增加了开发和运维的复杂性。当数据量已经很多时,在上面新增一个索引可能需要耗用数小时的时间,这会严重影响线上数据库的可用性,甚至有可能直接导致线上数据库不可用。相比而言,采用倒排索引的数据库自动为数据建立字典和倒排索引表,不需要我们再为查哪些字段、建哪些索引的问题而耗费过多精力。

倒排索引(inverted index)是一种新颖的索引方法,常用于搜

索引擎,是文档检索中最常用的数据结构。下面我们以搜索同时包含

“我”“爱”“你”3个字文档为例来讲解倒排索引的原理。

文档1:我喜欢你

文档2:我爱你

文档3:我很爱你

为了实现倒排索引,首先需要对每个文档进行“分词”处理。所谓“分词”,就是将文档切分成一个个单独的词。简单起见,我们将把每个字作为一个词。经过分词处理后,结果如下:

文档1:我、喜、欢、你

文档2:我、爱、你

文档3:我、很、爱、你

在这些文档中,所有出现的词构成了一个字典:

{我, 喜, 欢, 你, 爱, 很}

以这个字典为基础,构建倒排索引,即统计字典中的每个字出现在哪些文档中:

"我": {文档1, 文档2, 文档3}

"喜": {文档1}

"欢": {文档1}"你": {文档1, 文档2, 文档3}

"爱": {文档2,文档3}

"很": {文档3}

所以,搜索包含“我”“爱”“你”这3个字的文档,结果是这3个字各自所在文档集合的交集:

{文档1, 文档2, 文档3} ∩ {文档2,文档3}∩ {文档1, 文档2, 文档3} = {文档2, 文档3}我们再直观地检查下,文档2和文档3正好包含“我”“爱”“你”这3个字,而文档1则不包含这3个字,这正是我们要达到的目的。上面的过程就是构建倒排索引的过程。

从上面的过程可以看出,倒排索引实际上为文档集合中的每一个分词都构建了它包含在哪些文档中的索引。当我们需要按多个条件(也就是多个分词)查询文档时,只需要将它们各自出现的文档集合求交集即可。在具体工程实现中,可以通过位图(bitmap)来记录索引,这样极大地节省了存储空间,并且通过布尔运算就能实现集合间的交集、并集等操作,极大地提高了查询的效率。倒排索引的这种特点使得它非常适用于搜索引擎领域。

在我们的报表系统中,倒排索引构建所有分词的索引且查询迅速的特点也完全满足查询的灵活性和准实时性要求。

ElasticSearch是一个实现了倒排索引的全文搜索引擎,可以在TB级别的数据量规模下做到准实时搜索。ElasticSearch支持丰富多彩的查询类型,并且运行稳定、可靠,集群部署、扩展和维护都非常方便。

由于ElasticSearch具备以下4个特点,所以它非常适用于报表系统。

1)ElasticSearch支持丰富的过滤、分组、聚合、排序等查询,可以充分、灵活地从一个或多个维度分析数据,这正是报表系统查询的核心所在。

2)ElasticSearch执行OLAP(On-Line Analytical Processing,联机分析处理)查询性能十分优异。在TB级别的数据规模下,ElasticSearch做各种OLAP查询,能够做到准实时,也就是数秒级别,是不是很赞!

3)ElasticSearch集群搭建和扩展非常容易,并且稳定、可靠。可以说,ElasticSearch是笔者使用的所有分布式系统中最方便、最可靠、最省心的分布式系统。

4)数据存入ElasticSearch,不需要专门预先针对OLAP查询设计各种聚合任务。这点在产品不断演进时非常重要,因为一开始的时候,可能产品经理自己也不知道以后会展示哪些报表,而使用ElasticSearch这类数据存储和查询都非常灵活的存储方案可以减少太多以后的麻烦。

图9-5展示了ElasticSearch集群的组成。该集群由3个物理节点组成,并且我们在其中创建了一个包含5个分片(shard)且每个分片又包含两份副本(replica)的索引(index)。在ElasticSearch中,“索引”相当于关系型数据库中的“表”。当一个索引包含的文档非常多时,可以通过分片的方式将其分布到多个节点上去。在ElasticSearch中,可以对各个分片创建多份冗余副本,这一方面保障了数据安全,实现了集群的高可用,另一方面副本也可以参与执行查询请求,从而提升集群整体的性能表现。

虽然ElasticSearch有很多优点,但是在使用过程中,还是需要注意一些问题。ElasticSearch有两种方式组织大量数据:一种是用一个大索引,索引内部分成很多分片;另一种是用多个索引,每个索引内部用较少的分片,图9-6就是按日期分成多个索引存储的示例。由于ElasticSearch的查询有一个非常好的特性,即同一个查询是可以跨多个索引的,所以这两种方案对于查询范围相同的查询请求而言是没有太大区别的。

对于不停往系统里追加新文档的场景来说,维护一个较小的索引是更加高效的。因此,对于数据不停追加、数据量与日俱增的场景来说,最好还是将大索引分成多个小索引。以笔者的经验来看,对于需要频繁追加数据的应用场景而言,在单台4核8GB的云主机上,单个索引控制在20~30GB比较合适。

ElasticSearch分索引的方式可以分为3种:按时间分片、按数据量分片及同时按时间和数据量分片。

1.按时间分片

按时间分片是指根据时间周期,在每个新的时间周期使用一个新的索引,如按天分片、按小时分片等。按时间分片的好处在于实现简单、数据时间范围清晰明确、容易实现TTL。但是如果时间周期不好选择,或者数据流量在每个周期的变化比较大,就会造成每个索引内数据量的分布不均匀,索引数过多或者过少,从而给运维带来麻烦。

2.按数据量分片

按数据量分片是指根据索引内记录的条数来决定是否使用新的索引。例如,如果平均每条记录是1KB,每个索引存放两千万条记录,那么,当索引中记录的数量超过两千万条时,就创建一个新的索引来存放新的数据。按数据量分片的好处在于每个索引数据量比较均匀。如果非要说缺点的话,这种方式的缺点就是不能通过索引名直接确定里面数据的时间范围。

3.同时按时间和数据量分片

同时按时间和数据量分片既可以保留每个索引内数据比较均匀的优点,还可以通过索引名直接确定里面数据的时间范围。这种方式是一个不错的选择,只是在代码实现时相对更复杂些。

来源:大数据架构师

相关推荐