用于基于日期的缓存过期的缓存或MultiMap?

时间:2012-11-26 12:42:37

标签: java caching guava multimap

背景信息:我正在为订购系统开发分析系统。每天大约有100,000个订单,分析需要运行最后N个(比如100天)的月份。相关数据适合内存。 N天后,所有订单都从内存缓存中逐出,过去一整天被驱逐。订单可以创建或更新。

  1. 传统方法会使用ConcurrentHashMap<Date, Queue<Order>>。每天,将删除表示过去N天以上日期的键的值。但是,当然,使用番石榴的重点是避免这种情况。编辑:将Map更改为ConcurrentHashMap,请参阅问题的结尾。

  2. 使用Guava集合,MultiMap <Date, Order>会更简单。驱逐是类似的,明确地实施。

  3. 虽然Cache实现看起来很吸引人(毕竟我正在实现一个缓存),但我不确定驱逐选项。驱逐只发生一次,最好从缓存外部发起,我不希望缓存必须检查订单的年龄。我甚至不确定缓存是否会使用MultiMap,我认为在这种情况下它是一个合适的数据结构。

  4. 因此,我的问题是:是否可以使用一个使用和公开MultiMap语义的Cache,并允许从外部控制的驱逐,特别是我需要的规则(“删除所有超过N天的命令”) )?

    作为一个重要的澄清,我对LoadingCache不感兴趣,但我确实需要批量加载(如果需要重新启动应用程序,必须从数据库中填充缓存,最后一个N订单日。)

    编辑:忘记提及地图需要并发,因为订单进入时它们会根据同一客户或位置等的先前订单进行实时评估。

    EDIT2:偶然发现Guava issue 135。看起来MultiMap不是并发的。

3 个答案:

答案 0 :(得分:2)

我在这里既不使用Cache也不使用Multimap。虽然我喜欢和使用它们,但在这里获得的并不多。

  • 您想要手动逐出您的参赛作品,因此Cache的功能并未在此处使用。
  • 您正在考虑ConcurrentHashMap<Date, Queue<Order>>,这在某种意义上比Multimap<Date, Order>更强大。

我会使用Cache,如果我考虑了不同的驱逐标准,如果我想随时 1 丢失任何条目,那么

您可能会发现需要ConcurrentMap<Date, Dequeue<Order>>ConcurrentMap<Date, YouOwnQueueFastSearchList<Order>>或其他任何内容。这可能是由Multimap以某种方式管理的,但恕我直言,它变得更复杂而不是更简单。

我会问自己“在这里使用CacheMultimap可以获得什么?”对我来说,看似普通的ConcurrentMap提供了所需的一切。


1 绝不是我建议Guava会发生这种情况。相反,没有驱逐理由(容量,到期,......),它就像ConcurrentMap一样。只是你所描述的内容更像Map而不是Cache

答案 1 :(得分:1)

恕我直言最简单的事情是在订单记录中包含订单的日期。 (我希望它已经是一个字段了)因为你每天只需要清理一次缓存,所以它不一定非常有效,只需要合理及时。

e.g。

public class Main {
    static class Order {
        final long time;

        Order(long time) {
            this.time = time;
        }

        public long getTime() {
            return time;
        }
    }

    final Map<String, Order> orders = new LinkedHashMap<String, Order>();

    public void expireOrdersOlderThan(long dateTime) {
        for (Iterator<Order> iter = orders.values().iterator(); iter.hasNext(); )
            if (iter.next().getTime() < dateTime)
                iter.remove();
    }

    private void generateOrders() {
        for (int i = 0; i < 120000; i++) {
            orders.put("order-" + i, new Order(i));
        }
    }

    public static void main(String... args) {
        for (int t = 0; t < 3; t++) {
            Main m = new Main();
            m.generateOrders();
            long start = System.nanoTime();
            for (int i = 0; i < 20; i++)
                m.expireOrdersOlderThan(i * 1000);
            long time = System.nanoTime() - start;
            System.out.printf("Took an average of %.3f ms to expire 1%% of entries%n", time / 20 / 1e6);
        }
    }
}

打印

Took an average of 9.164 ms to expire 1% of entries
Took an average of 8.345 ms to expire 1% of entries
Took an average of 7.812 ms to expire 1% of entries

对于100,000个订单,我预计这需要大约10毫秒,这不是在半夜的安静时期产生的。

BTW:如果您的OrderIds按时间排序,您可以提高效率。 ;)

答案 2 :(得分:0)

您是否考虑过使用某种排序列表?它可以让你拉出条目,直到你找到一个足够新鲜的条目。当然这假设这是你的主要功能。如果你最需要的是使用hashmap的O(1)访问,我的答案不适用。