1

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

  1. 传统方法将使用ConcurrentHashMap<Date, Queue<Order>>. 每天,表示过去 N 天以上日期的键的值将被删除。但是,当然,使用 Guava 的全部目的是避免这种情况。编辑:更改MapConcurrentHashMap,请参阅问题的结尾以了解基本原理。

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

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

因此,我的问题是:是否可以使用 Cache 使用并公开 MultiMap 的语义并允许从外部控制驱逐,特别是使用我需要的规则(“删除所有早于 N 天的订单”)?

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

编辑:忘了提到地图需要是并发的,因为订单进来时,它们会针对同一客户或位置等的先前订单进行实时评估。

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

4

3 回答 3

2

我不会在这里使用 aCache或 a Multimap。虽然我喜欢并使用它们,但这里并没有什么好处。

  • 您想手动逐出您的条目,因此Cache这里并没有真正使用 的功能。
  • 您正在考虑ConcurrentHashMap<Date, Queue<Order>>,这在某种意义上比 a 更强大Multimap<Date, Order>

Cache如果我考虑不同的驱逐标准并且我觉得随时丢失它的任何条目,我会使用1就可以了。

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

Cache我会问自己“使用或在这里我能得到什么Multimap?”。对我来说,它看起来像是ConcurrentMap关于你需要的一切的普通旧优惠。


1我绝不是在暗示 Guava 会发生这种情况。相反,没有驱逐原因(容量,到期,...)它就像一个ConcurrentMap. 只是你描述的感觉更像是 aMap而不是 a Cache

于 2012-11-26T22:06:44.163 回答
1

恕我直言,最简单的做法是在订单记录中包含订单日期。(我希望它已经是一个字段)由于您每天只需要清理一次缓存,因此它不必非常有效,只需合理及时即可。

例如

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 毫秒,这在半夜的安静时段不会发生太多。

顺便说一句:如果您的 OrderIds 按时间排序,您可以提高效率。;)

于 2012-11-26T12:51:06.090 回答
0

您是否考虑过使用某种排序列表?它将允许您提取条目,直到您找到一个足够新鲜的条目。当然,这假设这是您的主要功能。如果您最需要的是使用哈希图进行 O(1) 访问,那么我的回答不适用。

于 2012-11-26T13:06:50.567 回答