2016-06-14 59 views
1

情况的Infinispan缓存的执行情况:在Wildfly 9(考虑集群)

我有多个上千条记录结算表。它们被分成包装,例如, 500条记录。然后每个数据包通过消息驱动Bean发送到AS。 AS根据每条记录的内容(例如currency,validStart,validEnd)计算一个密钥,并需要将该密钥存储在数据库中(与内容的组合一起)。

请求:

为了避免重复,我想集中的“工具”,它计算的关键,并将其存储,从而通过缓存与记录这些键减少与数据库的通信。

现在我试着在每个包处理线程的Utility-class-implementation中使用本地Infinispan缓存。这导致了这样的事实,即多个软件包计算出相同的密钥,因此将重复项插入到数据库中。或者有时我会陷入僵局。

我试图通过一个静态变量来实现一个“锁定”来在数据库插入期间阻止对缓存的访问,但没有成功。 下一次尝试是使用复制分别分布式的Infinispan缓存。这并没有改变AS行为的结果。

我最后的想法是实现一个bean管理的单例会话bean,以在插入数据库期间获取事务锁。

AS当前在独立模式下运行,但在不久的将来将被移到群集,因此高可用性解决方案是首选。

恢复:

什么是锁创建的(键,值)对,以避免重复期间的Infinispan缓存访问正确的方法是什么?

更新:

@cruftex:我的要求是:我有一组(键,值)对,这应被缓存。如果插入新记录,则应用算法并计算密钥。然后,如果密钥已经存在并且值将被追加到新记录中,则应检查缓存。但是,如果价值不存在,则应创建并存储在数据库中。

缓存需要使用Infinispan来实现,因为AS应该在群集中运行。创建密钥的算法存在。在数据库中也插入值(通过JDBC或实体)。但我有这个问题,即使用消息驱动Bean(并因此在AS中多线程)相同(键,值)对计算在不同的线程中,因此每个线程都尝试将值插入到数据库中(我想要避免!)。

@戴夫:

public class Cache { 
    private static final Logger log = Logger.getLogger(Cache.class); 
    private final Cache<Key, FullValueViewer> fullCache; 
    private HomeCache homes;  // wraps EntityManager 
    private final Session session; 

    public Cache(Session session, EmbeddedCacheManager cacheContainer, HomeCache homes) { 
     this.session = session; 
     this.homes = homes; 
     fullCache = cacheContainer.getCache(Const.CACHE_CONDCOMBI); 
    } 

    public Long getId(FullValueViewer viewerWithoutId) { 
     Long result = null; 

     final Key key = new Key(viewerWithoutId); 
     FullValueViewer view = fullCache.get(key); 

     if(view == null) { 
      view = checkDatabase(viewerWithoutId); 
      if(view != null) { 
       fullCache.put(key, view); 
      } 
     } 

     if(view == null) { 
      view = createValue(viewerWithoutId); 

      // 1. Try 
      fullCache.put(key, view); 

      // 2. Try 
      //  if(!fullCache.containsKey(key)) { 
      //   fullCache.put(key, view); 
      //  } else { 
      //   try { 
      //    homes.condCombi().remove(view.idnr); 
      //   } catch (Exception e) { 
      //    log.error("remove", e); 
      //   } 
      //  } 

      // 3. Try 
      //  synchronized(fullCache) { 
      //   view = createValue(viewerWithoutId); 
      //   fullCache.put(key, view); 
      //  } 
     } 
     result = view.idnr; 
     return result; 
    } 

    private FullValueViewer checkDatabase(FullValueViewer newView) { 
     FullValueViewer result = null; 
     try { 
      CondCombiBean bean = homes.condCombi().findByTypeAndKeys(_parameters_); 
      result = bean.getAsView(); 
     } catch (FinderException e) { 
     } 
     return result; 
    } 

    private FullValueViewer createValue(FullValueViewer newView) { 
     FullValueViewer result = null; 
     try { 
      CondCombiBean bean = homes.condCombi().create(session.subpk); 
      bean.setFromView(newView); 
      result = bean.getAsView(); 
     } catch (Exception e) { 
      log.error("createValue", e); 
     } 
     return result; 
    } 

    private class Key { 

     private final FullValueViewer view; 

     public Key(FullValueViewer v) { 
      this.view = v; 
     } 

     @Override 
     public int hashCode() { 
      _omitted_ 
     } 

     @Override 
     public boolean equals(Object obj) { 
      _omitted_ 
     } 
    } 
} 

缓存配置我试着用Wildfly:

<cache-container name="server" default-cache="default" module="org.wildfly.clustering.server"> 
    <local-cache name="default"> 
     <transaction mode="BATCH"/> 
    </local-cache> 
</cache-container> 

<cache-container name="server" default-cache="default" module="org.wildfly.clustering.server"> 
    <transport lock-timeout="60000"/> 
    <distributed-cache name="default" mode="ASYNC"/> 
</cache-container> 
+0

请发表您已经写过/尝试过的内容; [so]在这里是为了帮助你解决你的编码问题,而不是为你写下全部内容:) – Dave

+0

它很不清楚你所要求的。你想知道如何生成密钥?将某些内容存储在缓存中?或者用缓存生成密钥(为什么?)?从数据中得到的密钥应该如何?如果您想生成唯一的密钥,请参阅UUID算法示例,您不需要任何通信。 – cruftex

回答

0

我反应过来只对简历的问题:

不能锁定整个缓存;那不会扩展。最好的方法是使用cache.putIfAbsent(key, value)操作,并且如果条目已经存在(或使用列表作为值并用条件cache.replace(key, oldValue, newValue)替换它)则生成不同的密钥。

如果要真正禁止写入某个密钥,可以使用具有悲观锁定策略的事务性高速缓存,并发出cache.getAdvancedCache().lock(key)。请注意,没有解锁:当通过事务管理器提交/回滚事务时,所有锁都会释放。

+0

我在使用putIfAbsent的问题是,我需要做一个数据库插入来检索主键(并将其附加到值),只有这样我才能将值放入缓存。 替换也是禁止的,因为这意味着,我创建了一个副本。 – RainerZufall

+0

所以如果你从DB获得(唯一)密钥,就不能有重复。 –

+0

该表对一个主自动增量键和两个不可空对象列进行constains,每个组合应该是唯一的(就像没有唯一约束的组合键)。即使我添加了一个唯一的约束,我不会插入重复项,但是这将通过解析错误代码等来使SQL Exception异常处理成为必需(对我来说看起来是错误和丑陋的) – RainerZufall

0

您不能生成自己的密钥,并使用它来同时检测重复项。

每个数据行都保证只到达一次,或者它需要从生成它的外部系统中体现唯一标识符。

如果数据中有唯一标识符,如果全部出错,并且没有标识符存在,只是将所有属性连接在一起,那么您需要使用它来检查重复项。

现在您可以直接使用该唯一标识符,或生成自己的内部标识符。如果您使用后者,则需要从外部ID转换为内部ID。

如果重复到达,您需要在生成内部标识时根据外部标识锁定,然后记录分配的内部标识。

要在集群中生成一个唯一的长值序列,可以使用缓存的CAS操作。比如这样的事情:

@NotThreadSafe 
class KeyGeneratorForOneThread { 

    final String KEY = "keySequenceForXyRecords"; 
    final int INTERVAL = 100; 
    Cache<String,Long> cache = ...; 
    long nextKey = 0; 
    long upperBound = -1; 

    void requestNewInterval() { 
    do { 
     nextKey = cache.get(KEY); 
     upperBound = nextKey + INTERVAL; 
    } while (!cache.replace(KEY, nextKey, upperBound)); 
    } 

    long generateKey() { 
    if (nextKey >= upperBound) { 
    requestNewInterval(); 
    } 
    return nextKey++; 
    } 
} 

每个线程都有自己的密钥生成器,并且不需要协调就可以生成100个密钥。

您可能需要单独的缓存为:由外部ID

  • 锁定
  • 查找从外部到内部编号
  • 序列号,注意,其实并不是一个高速缓存,因为它必须知道上次数重新启动后
  • 内部ID数据
0

我们发现,在我们的情况下,有效的解决方案和可能对其他人有所帮助:

我们有两个主要组件,一个缓存类和一个单身bean。
缓存包含当前存在于数据库中的所有记录的副本以及大量的逻辑。
单身bean有权访问infinispan缓存并用于创建新记录。

初始化缓存从单身bean获取infinispan缓存的副本。然后,如果我们在缓存中搜索一条记录,我们首先应用一种散列方法,该方法为记录计算一个unqiue密钥。如果记录需要添加到数据库中,使用这个密钥我们可以识别。 如果是这样,那么缓存使用带有@Lock(WRITE)注释的create-method调用单例bean。 create方法首先检查,如果值包含在infinispan-cache中,如果不包含,则创建一条新记录。

使用这种方法,我们可以保证,即使缓存在多个线程中使用,并且每个线程发送一个请求以在数据库中创建相同的记录,创建过程也会被锁定,并且所有后续请求都不会继续进行,因为该值已经在先前的请求中创建。