前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Map-Reduce风格:数据感知vFabric GemFire中的分布式查询

Map-Reduce风格:数据感知vFabric GemFire中的分布式查询

作者头像
用户2176511
发布2018-05-31 16:49:27
1.2K0
发布2018-05-31 16:49:27

大量快速的数据正在为当今市场上一些最有趣的计算机会提供动力。但想要达成目标,我们需要改变数据层的方法。企业正试图从昂贵的大型架构转向虚拟化数据中心,并更有效地利用商用硬件。有了数据层,这意味着可以通过在运行时增加更多基于商品的计算和存储来水平扩展体系结构。

为了横向扩展数据层,一些公司使用诸如vFabric GemFire这样的系统,这是一个分布式数据系统,旨在专门在商品硬件节点上容纳大型数据集。在GemFire中,数据分布在集群成员之间,其成员称为“节点”,这些节点的数据分布称为“分区”。然后,vFabric GemFire允许开发人员查询驻留在多个节点上的数据,同时保留大规模的核心价值。这是如何做到的?简而言之,答案是“数据感知查询” - 查询API,允许在可选择的节点上执行查询,而不是以map-reduce一样在所有节点上进行。

为了回答这个问题,本文包含以下内容:

  • 了解数据分区
  • 了解基本数据查询
  • 使用自定义分区实现数据感知查询
  • 使用自定义分区实现函数执行

了解数据分区

首先,我们应该了解数据是如何映射出来的,以便了解如何以动态的方式快速存储和访问大量数据。

GemFire使用密钥对数据进行分区,因此,密钥和对应值的子集存储在单个节点上。这种方法有助于以高吞吐量的性能同时访问大型数据集,而不会导致节点集群中的存储/访问延迟。密钥是使得存储/访问成为O(1)操作(所耗费的时间固定,不依赖于输入数据的大小)的具有唯一标识的实体,并且允许存储重复值。此外,密钥既可以是独立的实体(如序列号),也可以是对值中多个属性的引用,从而使分区基于组合密钥。

对数据进行分区可以提高查询性能,因为它使用大型数据集的部分扫描功能,并避免使用全部数据存储扫描或分散在整个数据存储区中的多个随机读取。

在GemFire中,数据使用PartitionRegion分区。一个分区或节点由多个在启动时配置的存储桶组成。存储桶根据密钥确定性地分布在多个节点上。要在存储桶中添加一条额外的背景信息,在数据从一个分区转移到另一个分区的重新平衡的过程中,他们是最小的数据单位。

了解基本数据查询

GemFire提供了一种现代化的分布式数据查询方式。查询以分散 - 汇总的方式执行, 从协调器开始,将其他相关节点的结果收集到协调器,最后将结果提供给应用程序。所有执行查询的节点都被视为数据节点,并且第一个节点,即开始执行查询操作(或从客户端接收查询请求)的节点成为协调器。这使得查询在相关数据节点上并行运行,并在协调器节点上收集结果以进行最终处理。例如,ORDER BY查询的协调器仅执行各数据节点排序结果集的最终合并排序。

在进一步阐述之前,我们先从一个基本的例子开始。GemFire使用键值对中的关键字分配数据。查询这些数据涉及类SQL语言的使用,如面向对象查询语言或者OQL。如果不在GemFire中使用任何特殊的分区(后面会讨论),关键字最终会与数组的值无关。OQL查询执行x时,不指定数据在所有节点(即散射状)的分布。如果不指定分发,则必须查询所有节点。在整个网络中执行这样的操作,不仅效率低下同时所付出的代价巨大。

举一个例子,假设我们有一个带有“航班”字段的“乘客”对象。

Passenger {

String name, Date travelDate, int age, Flight flt,

} Flight {

int flightId, String origin, String dest,

}

比方说,在“乘客”数据存储区(在GemFire中,数据存储区称为“区域”)有1亿个乘客对象,存储在3个节点上,我们希望对该数据存储区内的所有乘客执行以下查询。

"SELECT p.travelDate, p.age,

FROM /Travelers p, p.flt f

WHERE

f.origin IN ('Boston', 'Chicago')

AND f.dest = 'Seattle'

AND p.age < 35"

(注:上述示例查询可用于航空公司的电影推荐,即应该向35岁以下的从波士顿或芝加哥飞向西雅图的乘客提供什么类型的电影。其思想是,年轻游客或有小孩的家庭可能会喜欢面向家庭的电影,而成年人则可能需要新闻或近期热门的电视连续剧。)

该查询基本上会创建一个包含1亿条记录的全表扫描,效率非常低。虽然GemFire支持创建索引,但是我们在这里不做讨论,而是讨论仅仅数据感知分区而引起的性能提升。

使用自定义分区来实现数据感知查询

从逻辑上讲,如果在指定范围内查询,则查询效率会更高。GemFire可以选择自定义分区或固定分区(在关系数据库术语中有时也称为“基于列的分区”)用于确定性分配数据。在GemFire 6.6.2中,我们可以选择性地查询基于列的分布式(即分区)数据。

使用与上面相同的示例,所有“乘客”数据都分区到多个GemFire节点。在“乘客”对象中,“航班”有一个“出发地”字段。如果我们将“出发地”作为密钥的一部分,使用则可以根据出发城市将数据分配到某个存储区(即分区内的分区)。这意味着对于特定节点上的“乘客”数据将是基于“航班”字段中的“出发地”。因此,在一个分区内,只会查询特定的存储桶而不是许多节点,单个节点或分区。因此,不会再迭代查询1亿个“乘客”对象。通过建立数据感知查询,上述查询将在有限的数据集上执行。假设有1亿乘客,50%的乘客年龄小于35岁,总共有9个出发城市,没有索引,GemFire的搜索引擎至多迭代1100万(100*(50%)*(2/9)=11.11百万)个乘客对象。

为了自定义分区数据,应用程序开发人员必须实现PartitionResolver以导入他们的GemFire分区策略。PartitionResolver可能如下所示,

/**

* 此解析器根据乘客的位置将所有“乘客”对象存储在一个存储桶中。

*共有9个可能的位置

*

*/ public class MyPartitionResolver implements PartitionResolver {

//Know no of buckets in the partition region which is configurable for a partition region.

//9 个不同的可能的出发地

Map keyToRoutingObject = new HashMap();

keyToRoutingObject.put("Seattle", 8);

keyToRoutingObject.put("Chicago", 4);

@Override

public Serializable getRoutingObject(EntryOperation opDetails) {

- - - - - -

//opDetails.getKey() returns key, used in region.put(key, value);

return keyToRoutingObject.get(opDetails.getKey().getOrigin()); //Could be "seq_num+origin" }

}

如下图所示,所有在“航班”中具有相同出发地的乘客将被放到同一个节点的同一个存储桶中。

使用自定义分区实现函数执行

然后,GemFire的函数执行服务可用于此分区数据,以实现对分布式数据进行像map-reduce 一样的操作,以及在数据所在位置进行查询的操作。这就是数据感知查询。函数执行服务任务可以在特定节点或一组节点上执行。函数被放置在筛选过的节点上(在上图中,分区B表示“芝加哥”,分区C表示“西雅图”),并在每个节点本地执行代码。查询执行也仅在本地使用新的API进行。在节点上不执行远程或分布式查询。在没有函数上下文和有函数上下文的情况下查询的区别在于,前者查询所有本地的存储桶,但是在后者仅在桶C和S上执行。

通过函数内的新的查询API查询:

//EmpFunction Class EmpFunction extends FunctionAdapter {

- - - - -

void execute(FunctionContext context) {

- - - - -

Query query = new Query(context.getArguments());

SelectResults results = query.execute(context); //New API

- - - - -

}

- - - - -

}

在应用程序代码中执行以上函数以执行查询:

// 应用程序客户端代码

Set filter = new HashSet();

filter.add("Seattle");

filter.add("Chicago");

Function empFunc = new EmpFunction("NAZFunction");

//执行函数

ResultCollector rColl = FunctionService

.onRegion(getRegion("employee"))

.withArgs(query)

.withFilter(filter)

.execute(empFunc);

//得到结果

Object result = rColl.getResults();

SelectResults queryResults = getResults(result);

这种方法提供了一种复杂有效的分布式数据查询方式,该方式有很好的预测效果。

关于作者:Shobhit Agarwal是VMware技术人员的成员,在过去两年中致力于为虚拟环境提供高可用性,低延迟,内存数据管理系统。Agarwal毕业于东北大学,获得计算机科学硕士学位,专攻系统工程。他的专长包括Java开发,分布式系统和数据结构。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 了解数据分区
  • 了解基本数据查询
  • 使用自定义分区来实现数据感知查询
  • 使用自定义分区实现函数执行
相关产品与服务
大数据
全栈大数据产品,面向海量数据场景,帮助您 “智理无数,心中有数”!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档