Java 如何访问与Hazelcast中的数据关联共存的相关条目?
我正在尝试使用Hazelcast的map reduce功能来执行聚合操作,该操作需要访问位于同一位置的条目。共定位是通过使用来控制的 想象一下中使用的经典客户/订单模型。在我的示例中,我希望返回一个客户摘要,其中包含客户及其所有订单的总和,例如,给定以下数据集:Java 如何访问与Hazelcast中的数据关联共存的相关条目?,java,hazelcast,Java,Hazelcast,我正在尝试使用Hazelcast的map reduce功能来执行聚合操作,该操作需要访问位于同一位置的条目。共定位是通过使用来控制的 想象一下中使用的经典客户/订单模型。在我的示例中,我希望返回一个客户摘要,其中包含客户及其所有订单的总和,例如,给定以下数据集: customer_id | name ------------------ 1 | Dave 2 | Kate order_id | customer_id | value ---------
customer_id | name
------------------
1 | Dave
2 | Kate
order_id | customer_id | value
------------------------------
1 | 1 | 5
2 | 1 | 10
3 | 2 | 12
我想返回:
customer_id | name | value
--------------------------
1 | Dave | 15
2 | Kate | 12
这很简单,但是使用数据关联的原因是能够在保存数据的各个分区内执行求和逻辑,只需获取该分区内的所有命令,从而避免任何跨JVM的通信
所以我的问题是,在一个或类似的缓存中,如何在另一个缓存中获得共同定位的条目
编辑:
在@noctarius的回答和评论之后,这里有一些代码(我已经尽力使其尽可能简短)强调了我只需要当前分区的命令的点
order key类如下所示:
public class OrderKey implements PartitionAware<CustomerIdentity>
{
...
@Override
public CustomerIdentity getPartitionKey()
{
return this.customerIdentity;
}
...
}
public class OrderSumMapper implements Mapper<CustomerKey, Customer, CustomerKey, CustomerOrderTotal>, HazelcastInstanceAware
{
...
@Override
public void map(CustomerKey customerKey, Customer customer, Context<CustomerKey, CustomerOrderTotal> context)
{
Predicate ordersForCustomer = new OrdersForCustomerPredicate(customerKey);
int totalValue = 0;
//******************************************************************
//
// Given orders are co-located with the customer, how do you ensure
// this call to get the orders only runs in the current partition?
//
//******************************************************************
for (Order order : hazelcastInstance.getMap("orders").values(ordersForCustomer))
{
totalValue += order.getValue();
}
context.emit(customerKey, new CustomerOrderTotal(customer, total));
}
...
}
private Collection<Order> getCustomerOrders(CustomerKey customerKey)
{
List<Order> orders = new ArrayList<>();
MapService mapService = node.getClusterService().getNodeEngine().getService(MapService.SERVICE_NAME);
RecordStore recordStore = mapService.getRecordStore(node.getPartitionService().getPartitionId(customerKey), "orders");
for (Data key : recordStore.keySet())
{
OrderKey orderKey = mapService.getSerializationService().toObject(key);
if (customerKey.equals(orderKey.getCustomerKey()))
{
orders.add(mapService.getSerializationService().toObject(recordStore.get(key)));
}
}
return orders;
}
突出显示的调用hazelcastInstance.getMap(“orders”).values(ordersForCustomer)通常会命中集群中的所有节点,但由于数据位于同一位置,因此这是不必要的开销
回到我原来的问题,您如何获得订单,以便只返回当前分区中的订单?您只需将当前节点的HazelcastInstance注入到映射器中,然后检索第二个数据结构来读取数据 请参见此处的基本示例:
我已经解决了这个问题,希望这对其他人有用(因此我无耻地回答并接受我自己的问题) 经过一些实验后,可以从运行在分区中的
映射器
获取分区内另一个映射中保存的对象
第一件事是让Mapper
实现NodeAware
,这会导致Hazelcast向运行Mapper
的节点注入引用
一旦有了节点
,就可以编写这样的方法来访问给定分区内其他映射中的数据,如下所示:
public class OrderKey implements PartitionAware<CustomerIdentity>
{
...
@Override
public CustomerIdentity getPartitionKey()
{
return this.customerIdentity;
}
...
}
public class OrderSumMapper implements Mapper<CustomerKey, Customer, CustomerKey, CustomerOrderTotal>, HazelcastInstanceAware
{
...
@Override
public void map(CustomerKey customerKey, Customer customer, Context<CustomerKey, CustomerOrderTotal> context)
{
Predicate ordersForCustomer = new OrdersForCustomerPredicate(customerKey);
int totalValue = 0;
//******************************************************************
//
// Given orders are co-located with the customer, how do you ensure
// this call to get the orders only runs in the current partition?
//
//******************************************************************
for (Order order : hazelcastInstance.getMap("orders").values(ordersForCustomer))
{
totalValue += order.getValue();
}
context.emit(customerKey, new CustomerOrderTotal(customer, total));
}
...
}
private Collection<Order> getCustomerOrders(CustomerKey customerKey)
{
List<Order> orders = new ArrayList<>();
MapService mapService = node.getClusterService().getNodeEngine().getService(MapService.SERVICE_NAME);
RecordStore recordStore = mapService.getRecordStore(node.getPartitionService().getPartitionId(customerKey), "orders");
for (Data key : recordStore.keySet())
{
OrderKey orderKey = mapService.getSerializationService().toObject(key);
if (customerKey.equals(orderKey.getCustomerKey()))
{
orders.add(mapService.getSerializationService().toObject(recordStore.get(key)));
}
}
return orders;
}
私有集合getCustomerOrders(CustomerKey CustomerKey)
{
列表顺序=新的ArrayList();
MapService MapService=node.getClusterService().getNodeEngine().getService(MapService.SERVICE\u名称);
RecordStore RecordStore=mapService.getRecordStore(node.getPartitionService().getPartitionId(customerKey),“订单”);
for(数据键:recordStore.keySet())
{
OrderKey OrderKey=mapService.getSerializationService().toObject(键);
if(customerKey.equals(orderKey.getCustomerKey()))
{
add(mapService.getSerializationService().toObject(recordStore.get(key));
}
}
退货订单;
}
有一点反序列化开销,但使用谓词
就是这种情况,这样做可以使映射器
在包含被映射数据的JVM中执行的所有处理都保持不变,因此避免了任何昂贵的进程/网络跳变-基本上它“应该”更快,并将明确减少节点间的网络流量沟通。对于那些熟悉连贯性的人,你可以通过聚合器实现这一点,并通过支持地图获得共址条目。如果我错了,请纠正我-你的代码是否有效,是因为你对人员和工薪年使用了相同的键?如果存储了多个年份,您将如何访问SalaryYear
s,并使用email
和year
的复合键进行键入?为什么它与该键有关?可以使用任何键从另一个映射检索另一个值。这些是独立的数据结构。如果你问,因为一致性分区快照还没有实现,但很快就会实现。在本例中,您将在给定时间对同一分区中的所有数据结构拥有一致的视图。关键点很重要,因为您的映射是针对包含主值的缓存运行的—在我的情况下是客户,在您的情况下是人。这意味着,在我的例子中,我想要检索给定分区中客户的所有订单,我没有完整的密钥,只有客户id。我认为如果每个人都有多年的工作时间,你也会遇到同样的问题。我想知道这是否可以通过分区感知
谓词
实现-你知道这是否有效吗?我仍然不明白你的问题,为什么不简单地对订单使用多重映射,然后在映射器中检索它呢?我想我不理解你的问题,所以可能需要添加一些演示代码。MultiMap
只能满足一个用例,但是我想要的是一个由组合标识(客户和订单id)键入的订单映射。然后,我想将reduce映射到客户,并让映射器
仅访问给定分区中与客户位于同一位置的订单。我将添加一些代码,并强调我只需要给定分区中的订单的一点。在寻找最小化反序列化开销的方法时,我注意到您可以从SerializationService
获得PortableReader
,这将允许从序列化的OrderKey
中提取CustomerKey
,而无需对所有内容进行反序列化,因为据我所知,NodeWare是3.3+,所以这可能在3.2上不起作用,并且您使用的私有API总是会发生更改。我们还没有向用户SPI公开这些东西,但你是对的,我们最终需要这样做,以方便使用。你能在github上填写一个增强请求吗?