Infinispace 缓存:不一致写入并发操作
Infinispace Cache: Inconsistent writes concurrent actions
我到处搜索这个问题的解决方案,但找不到任何提示。
我的应用程序部署在 JBOSS EAP 6.4 上并使用 JDK 1.8 构建。我在独立 xml 中配置了一个本地 infinispan 缓存:
<subsystem xmlns="urn:jboss:domain:infinispan:1.5">
<cache-container name="test-cache" default-cache="test-data-cache" jndi-name="java:jboss/infinispan/test-cache" statistics-enabled="true">
<transport lock-timeout="60000"/>
<local-cache name="test-data-cache" statistics-enabled="true">
<!-- <transaction locking="PESSIMISTIC"/> -->
<locking isolation="READ_COMMITTED"/>
<expiration lifespan="3600000"/>
</local-cache>
</cache-container>
</subsystem>
我将数据放入缓存中为:
package com.comp.test;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import org.apache.commons.collections4.MapUtils;
import org.infinispan.Cache;
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class CacheTest {
@Resource(lookup = "java:jboss/infinispan/test-cache")
private org.infinispan.manager.CacheContainer container;
public void putData(String k, String v) {
final Map<String, String> tmap = getDataCache().get(k);
if (!containsData(k)) {
final HashMap<String, String> rmap = new HashMap<>();
rmap.put(k+"_"+v, v);
getDataCache().put(k, rmap);
} else {
getDataCache().get(k).put(k+"_"+v, v);
}
}
public boolean containsData(String k) {
return getDataCache().containsKey(k);
}
private Cache<String, Map<String, String>> getDataCache() {
return container.getCache("test-data-cache");
}
}
我有无状态 Bean,它可以同时将数据集合放入缓存(@Asyncronous 注释)。当所有并发操作结束后,当我从缓存中检索数据时,缓存中的值总是较少。如果我放入 20 个值,则缓存中仅存在 16 / 17 个值。
在开始将数据放入该特定密钥的缓存之前,我试图找出是否可以锁定该密钥。但我了解到它是由 Infinispan 内部处理的。我在 SO 上发现了另一个类似的问题。但这个问题也没有答案。
Infinispan cache inconsistency for massive concurrent operations
请告诉我如何确保并发放入infinispan本地缓存的数据最后是一致的。如果您需要更多信息,请告诉我。
您有两个选项可用于悲观锁定:
- getDataCache().getAdvancedCache().lock(k)
- getDataCache().getAdvancedCache().withFlags(Flag.FORCE_WRITE_LOCK)
还有第三种选择:改为使用乐观锁定,如果另一个事务修改了键,则重试事务。但这不适用于 @TransactionalAttribute
,您必须自己调用 TransactionManager.commit()
并捕获 WriteSkewException
.
我通过@Dan Berindei 提供的提示解决了这个问题。
我将缓存配置更改为:
<subsystem xmlns="urn:jboss:domain:infinispan:1.5">
<cache-container name="test-cache" default-cache="test-data-cache" jndi-name="java:jboss/infinispan/test-cache" statistics-enabled="true">
<transport lock-timeout="60000"/>
<local-cache name="test-data-cache" start="EAGER" batching="false" statistics-enabled="true">
<locking isolation="SERIALIZABLE" acquire-timeout="5000"/>
<transaction mode="FULL_XA" locking="PESSIMISTIC"/>
<expiration lifespan="3600000"/>
</local-cache>
</cache-container>
</subsystem>
然后我更新了我的代码以在每次调用缓存时锁定密钥:
package com.comp.test;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import org.apache.commons.collections4.MapUtils;
import org.infinispan.Cache;
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class CacheTest {
@Resource(lookup = "java:jboss/infinispan/test-cache")
private org.infinispan.manager.CacheContainer container;
public void putData(String k, String v) {
final Map<String, String> tmap = getDataCache().get(k);
if (!containsData(k)) {
final HashMap<String, String> rmap = new HashMap<>();
rmap.put(k+"_"+v, v);
getDataCache().put(k, rmap);
} else {
AdvancedCache<String, Map<String, String>> cache = getDataCache().getAdvancedCache();
cache.lock(k);
getDataCache().get(k).put(k+"_"+v, v);
}
}
public boolean containsData(String k) {
return getDataCache().containsKey(k);
}
private Cache<String, Map<String, String>> getDataCache() {
return container.getCache("test-data-cache");
}
}
我到处搜索这个问题的解决方案,但找不到任何提示。
我的应用程序部署在 JBOSS EAP 6.4 上并使用 JDK 1.8 构建。我在独立 xml 中配置了一个本地 infinispan 缓存:
<subsystem xmlns="urn:jboss:domain:infinispan:1.5">
<cache-container name="test-cache" default-cache="test-data-cache" jndi-name="java:jboss/infinispan/test-cache" statistics-enabled="true">
<transport lock-timeout="60000"/>
<local-cache name="test-data-cache" statistics-enabled="true">
<!-- <transaction locking="PESSIMISTIC"/> -->
<locking isolation="READ_COMMITTED"/>
<expiration lifespan="3600000"/>
</local-cache>
</cache-container>
</subsystem>
我将数据放入缓存中为:
package com.comp.test;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import org.apache.commons.collections4.MapUtils;
import org.infinispan.Cache;
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class CacheTest {
@Resource(lookup = "java:jboss/infinispan/test-cache")
private org.infinispan.manager.CacheContainer container;
public void putData(String k, String v) {
final Map<String, String> tmap = getDataCache().get(k);
if (!containsData(k)) {
final HashMap<String, String> rmap = new HashMap<>();
rmap.put(k+"_"+v, v);
getDataCache().put(k, rmap);
} else {
getDataCache().get(k).put(k+"_"+v, v);
}
}
public boolean containsData(String k) {
return getDataCache().containsKey(k);
}
private Cache<String, Map<String, String>> getDataCache() {
return container.getCache("test-data-cache");
}
}
我有无状态 Bean,它可以同时将数据集合放入缓存(@Asyncronous 注释)。当所有并发操作结束后,当我从缓存中检索数据时,缓存中的值总是较少。如果我放入 20 个值,则缓存中仅存在 16 / 17 个值。
在开始将数据放入该特定密钥的缓存之前,我试图找出是否可以锁定该密钥。但我了解到它是由 Infinispan 内部处理的。我在 SO 上发现了另一个类似的问题。但这个问题也没有答案。 Infinispan cache inconsistency for massive concurrent operations
请告诉我如何确保并发放入infinispan本地缓存的数据最后是一致的。如果您需要更多信息,请告诉我。
您有两个选项可用于悲观锁定:
- getDataCache().getAdvancedCache().lock(k)
- getDataCache().getAdvancedCache().withFlags(Flag.FORCE_WRITE_LOCK)
还有第三种选择:改为使用乐观锁定,如果另一个事务修改了键,则重试事务。但这不适用于 @TransactionalAttribute
,您必须自己调用 TransactionManager.commit()
并捕获 WriteSkewException
.
我通过@Dan Berindei 提供的提示解决了这个问题。
我将缓存配置更改为:
<subsystem xmlns="urn:jboss:domain:infinispan:1.5">
<cache-container name="test-cache" default-cache="test-data-cache" jndi-name="java:jboss/infinispan/test-cache" statistics-enabled="true">
<transport lock-timeout="60000"/>
<local-cache name="test-data-cache" start="EAGER" batching="false" statistics-enabled="true">
<locking isolation="SERIALIZABLE" acquire-timeout="5000"/>
<transaction mode="FULL_XA" locking="PESSIMISTIC"/>
<expiration lifespan="3600000"/>
</local-cache>
</cache-container>
</subsystem>
然后我更新了我的代码以在每次调用缓存时锁定密钥:
package com.comp.test;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import org.apache.commons.collections4.MapUtils;
import org.infinispan.Cache;
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class CacheTest {
@Resource(lookup = "java:jboss/infinispan/test-cache")
private org.infinispan.manager.CacheContainer container;
public void putData(String k, String v) {
final Map<String, String> tmap = getDataCache().get(k);
if (!containsData(k)) {
final HashMap<String, String> rmap = new HashMap<>();
rmap.put(k+"_"+v, v);
getDataCache().put(k, rmap);
} else {
AdvancedCache<String, Map<String, String>> cache = getDataCache().getAdvancedCache();
cache.lock(k);
getDataCache().get(k).put(k+"_"+v, v);
}
}
public boolean containsData(String k) {
return getDataCache().containsKey(k);
}
private Cache<String, Map<String, String>> getDataCache() {
return container.getCache("test-data-cache");
}
}