如何解决Infinispace 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 注释)。一旦所有并发操作结束,当我从 Cache 中检索数据时,Cache 总是具有较少数量的值。如果我输入 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,String>> getDataCache() {
return container.getCache("test-data-cache");
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。