Java 缓存或多重映射用于基于日期的缓存过期?

Java 缓存或多重映射用于基于日期的缓存过期?,java,caching,guava,multimap,Java,Caching,Guava,Multimap,背景:我正在为订购系统开发分析系统。每天大约有100000份订单,分析需要在过去N(比如说100)个月内运行。相关数据存储在内存中。N天后,所有订单都将从内存缓存中收回,过去一整天都将被收回。可以创建或更新订单 传统方法将使用ConcurrentHashMap。每天,表示过去N天以上日期的键的值都将被删除。但是,当然,使用番石榴的全部目的是为了避免这种情况。编辑:将Map更改为ConcurrentHashMap,请参见问题末尾了解基本原理 有了番石榴系列,MultiMap会更简单。逐出类似,是显

背景:我正在为订购系统开发分析系统。每天大约有100000份订单,分析需要在过去N(比如说100)个月内运行。相关数据存储在内存中。N天后,所有订单都将从内存缓存中收回,过去一整天都将被收回。可以创建或更新订单

  • 传统方法将使用
    ConcurrentHashMap
    。每天,表示过去N天以上日期的键的值都将被删除。但是,当然,使用番石榴的全部目的是为了避免这种情况。编辑:将
    Map
    更改为
    ConcurrentHashMap
    ,请参见问题末尾了解基本原理

  • 有了番石榴系列,
    MultiMap
    会更简单。逐出类似,是显式实现的

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

  • 因此,我的问题是:是否可以使用使用和公开多重映射语义并允许从外部控制驱逐的缓存,特别是使用我需要的规则(“删除N天以上的所有订单”)

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

    编辑:忘了提及地图需要是并发的,因为订单进来时,它们会根据同一客户或地点的先前订单进行实时评估


    EDIT2:只是被绊倒了。看起来多重映射不是并发的。

    我认为最简单的方法是在订单记录中包含订单日期。(我想它已经是一个字段了)因为你每天只需要清理缓存一次,所以它不需要非常高效,只是相当及时

    e、 g

    对于100000个订单,我预计这需要花费10毫秒,而不是在午夜的一个安静的时间内发生。
    顺便说一句:如果您的医嘱ID是按时间排序的,那么您可以使其更有效

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

    我在这里既不使用
    缓存,也不使用
    多映射。虽然我喜欢并使用这两种方法,但在这里收获不多

    • 您希望手动逐出条目,因此
      Cache
      的功能在这里没有真正使用
    • 您正在考虑使用
      ConcurrentHashMap
      ,从某种意义上讲,它比
      多映射
      更强大
    如果我考虑到不同的逐出标准,如果我想随时丢失它的任何条目,我会使用
    缓存

    您可能会发现您需要一个
    ConcurrentMap
    ConcurrentMap
    或其他任何东西。这可能是由
    多映射
    以某种方式管理的,但我认为它会变得更复杂而不是更简单

    我会问自己,“在这里使用
    Cache
    Multimap
    有什么好处?”。在我看来,普通的
    ConcurrentMap
    提供了您所需要的一切



    1我决不是说番石榴会这样。相反,如果没有逐出原因(容量、过期等),它的工作方式就像一个
    ConcurrentMap
    。只是你所描述的感觉更像是一个
    映射
    而不是
    缓存

    参见(
    缓存
    MapMaker
    生成的
    并发映射
    的继承者),还有。关于编辑#2:你可以用指定的多映射来支持一个同步(线程安全的)多映射。@Xaerxess谢谢,我必须测试它的性能;我担心它不会像ConcurrentHashMap那么好,在这种情况下,我必须回到使用JDK类(即问题中的方法1)。订单日期是关键,因此在方法1中,整个订单集合(存储在队列中)被逐出。但问题更多的是关于#2对#3。我认为你是对的;早些时候,我看到了这样一条评论:“注意:如果您不需要缓存的功能,那么ConcurrentHashMap的内存效率更高——但是用任何旧的ConcurrentMap复制大多数缓存功能是极其困难或不可能的。”虽然缓存可以返回ConcurrentMap,但我认为不值得使用它。
    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