2

我正在使用 Spring 4.0.3,并利用 EhCache 2.8.1 支持的缓存抽象功能。

我担心方法级别的注释在编辑@Cacheable@CacheEvict可能无法正确锁定缓存,从而导致线程安全问题。我下面的测试似乎证实了这一点。我的问题是,我是在滥用框架还是误解了我的测试结果?或者我得出的结论是正确的,注释不会导致缓存被正确锁定,因此使用@CacheEvict不能保证未来的读取将是有效的?

测试:

创建一个数据库表来存储一个计数器值

create table counter
(
    counter integer
);
insert into counter values(0);

在 ehcache.xml 中创建一个条目来缓存计数器值

<cache name="counter"
    eternal="true"
    maxElementsInMemory="1"/>

在 Spring 控制器中创建两种请求映射方法——一种是读取计数器的值并在长时间延迟后返回,另一种是递增值然后快速返回

@RequestMapping("/viewcounter")
@ResponseBody
@Cacheable(value = "counter", key = "1")
public int readCounter() {
    int count = dao.selectInteger("select counter from counter");
    try {
        Thread.sleep(5000);
    }
    catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return count;
}

@RequestMapping("/incrementcounter")
@ResponseBody
@CacheEvict(value = "counter", key = "1")
public int incrementCounter() {
    dao.update("update counter set counter = counter + 1");
    int count = dao.selectInteger("select counter from counter");
    return count;
}

在两种不同的场景下遵循这三个步骤——首先从控制器方法中删除缓存注释,然后将它们添加回

  1. 访问/incrementcounter
  2. 在一个浏览器选项卡中,访问/viewcounter,然后在发起此请求后立即/incrementcounter在另一个选项卡 中访问
  3. 访问/viewcounter

试验结果

Expected result: 2
Actual result without caching: 2
Actual result with caching: 1

缓存结果是错误的,是吗?

4

1 回答 1

1

缓存抽象中没有锁定。你不是第一个要求它的人,我们已经在内部进行了重大的头脑风暴,讨论支持它的成本。事实证明,这远非简单,在这种抽象中使用锁可能非常棘手,尤其是在“通用”方式中。此外,缓存供应商花费大量资源来支持这类用例,这是有原因的。

我目前最好的猜测是,如果你想要这些功能,你需要你的缓存是事务性的。但是,在将其与抽象一起使用时存在一些问题,请特别检查目前正在研究的SPR-11540 。

于 2014-05-23T19:31:05.880 回答