Hazelcast和MapDB - 实现一个简单的分布式数据库

时间:2014-09-02 11:03:27

标签: java hazelcast mapdb nosql

我已经实现了一个hazelcast服务,它通过MapStoreFactory和newMapLoader将其数据存储到本地mapdb实例中。这样,如果需要重新启动集群,则可以加载密钥:

public class HCMapStore<V> implements MapStore<String, V> {

Map<String, V> map;

/** specify the mapdb e.g. via 
  * DBMaker.newFileDB(new File("mapdb")).closeOnJvmShutdown().make() 
  */
public HCMapStore(DB db) {
    this.db = db;
    this.map = db.createHashMap("someMapName").<String, Object>makeOrGet();
}

// some other store methods are omitted
@Override
public void delete(String k) {
    logger.info("delete, " + k);
    map.remove(k);
    db.commit();
}

// MapLoader methods
@Override
public V load(String key) {
    logger.info("load, " + key);
    return map.get(key);
}

@Override
public Set<String> loadAllKeys() {
    logger.info("loadAllKeys");
    return map.keySet();
}

@Override
public Map<String, V> loadAll(Collection<String> keys) {
    logger.info("loadAll, " + keys);
    Map<String, V> partialMap = new HashMap<>();
    for (String k : keys) {
        partialMap.put(k, map.get(k));
    }
    return partialMap;
}}

我现在面临的问题是来自hazelcast的MapLoader接口的loadAllKeys方法需要返回整个集群的所有密钥,但每个节点只存储它拥有的对象。

示例:我有两个节点并存储8个对象,例如5个对象存储在node1的mapdb中,3个存储在node2的mapdb中。我认为哪个对象由hazelcast决定由哪个节点决定。现在重新启动node1将为loadAllKeys返回5个键,node2将返回3. Hazelcast决定忽略3个项目并且数据“丢失”。

这可能是一个很好的解决方案吗?

赏金更新Here我在hc邮件列表中提到了2个选项(我还要再添加1个),我想知道这样的事情是否已经存在可能使用hazelcast 3.2或3.3:

  1. 目前,MapStore接口仅从本地节点获取数据或更新。是否可以通知MapStore接口完整集群的每个存储操作?或者这可能已经成为一些听众的魔力?也许我可以强制使用hazelcast将所有对象放入一个分区,每个节点上都有一个副本。

  2. 如果我重新启动,例如2个节点然后使用我的本地数据库为node1正确调用MapStore接口,然后为node2正确调用。但是当两个节点都加入时,node2的数据将被删除,因为Hazelcast假设只有主节点可以正确。我可以教hazelcast接受来自两个节点的数据吗?

4 个答案:

答案 0 :(得分:2)

根据Hazelcast 3.3文档 MapLoader初始化流程如下:

  

首次从任何节点调用getMap()时,将开始初始化   取决于InitialLoadMode的值。如果设置为EAGER,   初始化开始。如果设置为LAZY,则实际初始化   没有启动,但每次加载分区时都会加载数据   完成。

     
      
  1. Hazelcast会调用MapLoader.loadAllKeys()来获取所有密钥   每个节点
  2.   
  3. 每个节点都会找出它拥有的密钥列表
  4.   
  5. 每个节点都会通过调用加载所有拥有的密钥   MapLoader.loadAll(键)
  6.   
  7. 每个节点通过调用将其拥有的条目放入映射中   IMap.putTransient(键,值)
  8.   

以上暗示如果节点以不同的顺序启动,那么密钥也将以不同的方式分布。因此,每个节点将不会在其本地存储中找到所有/一些分配的密钥。您应该能够通过在HCMapStore.loadAllKeys和HCMapStore.loadAll中设置断点来验证它,并将您获取的密钥与密钥进行比较。

在我看来,你想要实现的目标与具有弹性特征的分布式缓存的概念相矛盾,因此是不可能的。即当一个节点消失(由于某种原因失败或断开连接)时,集群将通过移动部分数据来重新平衡,每次节点加入集群时都会发生相同的过程。因此,在群集更改的情况下,丢失节点的本地反向存储将变得过时。

Hazelcast集群本质上是动态的,因此它不能依赖具有静态分布式拓扑的后台存储。实质上,您需要拥有一个共享的后台存储,以使其与动态hazelcast集群一起使用。也可以分发后台存储,例如cassandra,但其拓扑必须独立于缓存集群拓扑。

更新:在我看来,您尝试实现的内容更具逻辑性,采用本地缓存的分布式数据存储(在MapDB之上)。

我希望这会有所帮助。

答案 1 :(得分:1)

也许有两个选择:

1)深入了解分区在Hazelcast中的工作原理。我认为每个分区都可以有MapLoader,强制节点只加载自己的分区,这样可以解决冲突。

2)当节点重新联机时,在添加节点之前与Hazelcast群集进行交互。你可以从MapDB中合并两个HZ第二组。

3)强制Hazelcast在每个节点上存储所有数据。将分区号设置为1或其他

答案 2 :(得分:1)

可以加载存储在所有节点上的数据,但此时您必须手动执行此操作。

在每个节点上:

HCMapStore store = createMapDbStore();
HazelcastInstance hz = createHz( store ); // use store in MapStoreConfig as implementation
IMap imap = hz.getMap("map"); 
Map diskMap = store.loadAll( store.loadAllKeys() ); // load all entries on disk
imap.putAll( diskMap ); // put into distributed map

但是正如邮件列表MapStore中提到的那样,并不是真的想以这种方式使用。另请注意,备份不会以这种方式持久保存到磁盘。因此,如果重新启动群集并且一个节点上的磁盘死亡,那么这些条目将会丢失。

答案 3 :(得分:0)

似乎是not easily possible

  

Hazelcast的持久层要求它是某种形式   中央存储。像数据库或共享文件一样。

或查看herehere。将调查正在使用Hazelcast并坚持光盘的OrientDB。