【问题标题】:infinispan file store size disproportional to data sizeinfinispan 文件存储大小与数据大小不成比例
【发布时间】:2014-04-05 07:43:03
【问题描述】:

我编写了一个小型 infinispan 缓存 PoC(代码如下)来尝试评估 infinispan 的性能。在运行它时,我发现对于我的配置,infinispan 显然不会从磁盘中清除缓存条目的旧副本,导致磁盘空间消耗比预期的要多几个数量级。

我可以做些什么来将磁盘使用率降低到大约实际数据的大小?

这是我的测试代码:

import org.infinispan.AdvancedCache;
import org.infinispan.manager.DefaultCacheManager;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Random;

public class App {
    final static int ELEMENTS_PER_BIN = 1000;
    final static int NUM_OF_BINS = 100;

    public static void main(String[] args) throws Exception {
        File storeFile = new File("store/store.dat");
        if (storeFile.exists() && !storeFile.delete()) {
            throw new IllegalStateException("unable to delete store file from previous run");
        }

        DefaultCacheManager cm = new DefaultCacheManager("infinispan.xml");
        AdvancedCache<String, Bin> cache = cm.<String,Bin>getCache("store").getAdvancedCache();

        Random rng = new Random(System.currentTimeMillis());

        for (int i=0; i<ELEMENTS_PER_BIN; i++) {
            for (int j=0; j<NUM_OF_BINS; j++) {
                String key = "bin-"+j;
                Bin bin = cache.get(key); //get from cache
                if (bin==null) {
                    bin = new Bin();
                }
                bin.add(rng.nextLong()); //modify
                cache.put(key, bin); //write back
            }
        }

        long expectedSize = 0;

        for (int j=0; j<NUM_OF_BINS; j++) {
            String key = "bin-"+j;
            Bin bin = cache.get(key);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(bin);
            oos.flush();
            oos.close();
            expectedSize += baos.size();
            baos.close();
        }

        long actualSize = new File("store/store.dat").length();

        System.err.println(ELEMENTS_PER_BIN+" elements x "+NUM_OF_BINS+" bins. expected="+expectedSize+" actual="+actualSize+" in "+cache.size()+" elements. diff="+(actualSize/(double)expectedSize));
    }

    public static class Bin implements Serializable{
        private long[] data = null;
        public void add(long datum) {
            data = data==null ? new long[1] : Arrays.copyOf(data, data.length+1); //expand capacity
            data[data.length-1] = datum;
        }
    }
}

这是 infinispan 的配置:

<infinispan
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="urn:infinispan:config:6.0 http://www.infinispan.org/schemas/infinispan-config-6.0.xsd"
        xmlns="urn:infinispan:config:6.0">
    <namedCache name="store">
        <eviction strategy="LRU" maxEntries="20"/>
        <persistence passivation="false">
            <singleFile location="store">
                <async enabled="false"/>
            </singleFile>
        </persistence>
    </namedCache>
</infinispan>

infinispan 被(应该是?)配置为直写式缓存,在 RAM 中包含 20 个最新元素以及磁盘上所有内容的实时副本。

运行上面的代码会得到:

1000 个元素 x 100 个 bin。预期=807300 实际=411664404 in 100 元素。 diff=509.92741731698254

这意味着对于 788 KB 的数据,我最终会得到一个 ~392 MB 的文件!

我做错了什么?

infinispan 的版本是 6.0.2.Final

【问题讨论】:

    标签: java infinispan


    【解决方案1】:

    当您只存储越来越长的记录时,以前使用的空间不会被重复使用。 SingleFileStore 中没有碎片整理策略,空闲空间保留为条目空间列表的映射,但相邻的空闲空间不会合并。 因此,新条目总是添加在文件的末尾,而开头是零散的和未使用的。

    顺便说一句,要找出预期的尺寸,您还应该:

    • 使用 JBoss Marshalling 而不是 Java 序列化
    • 也序列化密钥
    • 序列化 Infinispan 元数据(例如条目生命周期、上次使用时间、可能的版本等...)

    【讨论】:

    • 我确信即使有这么多开销,x500 比率也是疯狂的 :-)
    • 那么让我们算一下吧。对于 100 个 bin 中的每一个,您会得到 1、2 ... 1000 占用的空间,因此每个 bin 及其历史记录(可用空间)为 1 + 2 + ... + 1000 = 1000 * 999 / 2 = 499500 个值,其中您期望 1000 个值。这就是 500 倍比率的来源。
    • 我并不是说这并不疯狂,但您的使用模式并不是 SingleFileStore 设计的。它预计条目大小的分布将保持不变。
    • 我了解 500 的来源。我的意思是,即使我“正确”地计算了预期大小,该比率仍然大致相同(并且随着时间的推移而增加)。我还认为我的用例应该比您想象的更常见 - 任何保留任何类型数据样本的用例都将涉及存储不断增加的值
    • 问题是这是否是一个好的做法——通常你不需要在阅读时查看所有数据(尤其是为了更新而阅读)。存储此类列表的推荐方法是将它们分成具有“下一个键”引用的有限大小的条目。此外,如果您没有以同步方式增长所有条目,则较新(较短)的列表将占用旧列表之后留下的空间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-14
    • 2015-01-07
    • 2012-11-10
    相关资源
    最近更新 更多