Размер хранилища файлов infinispan непропорционален размеру данных

Я написал небольшой PoC для кеша infinispan (код ниже), чтобы попытаться оценить производительность 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 настроен (должен быть?) как кеш со сквозной записью с 20 последними элементами в ОЗУ и живой копией всего на диске.

выполнение приведенного выше кода дает следующее:

1000 элементов x 100 ячеек. ожидаемое=807300 фактическое=411664404 в 100 элементах. разница = 509,92741731698254

это означает, что для 788 КБ данных я получаю файл ~ 392 МБ!

Что я делаю не так?

рассматриваемая версия infinispan 6.0.2.Final


person radai    schedule 05.04.2014    source источник


Ответы (1)


Когда вы сохраняете только более длинные записи, пространство, использованное ранее, не используется повторно. В SingleFileStore нет политики дефрагментации, свободное пространство сохраняется в виде карты списка входящих пространств, но соседние свободные пространства не объединяются. Поэтому новая запись всегда добавляется в конец файла, а начало фрагментировано и не используется.

Кстати, для выяснения предполагаемого размера также следует:

  • используйте JBoss Marshalling вместо сериализации Java
  • также сериализовать ключ
  • сериализовать метаданные Infinispan (например, продолжительность жизни записи, время последнего использования, возможно, версию и т. д.)
person Radim Vansa    schedule 07.04.2014
comment
я уверен, что даже со всеми этими накладными расходами соотношение x500 безумно :-) - person radai; 07.04.2014
comment
Тогда займемся математикой. Для каждого из 100 бинов вы получаете место, занятое 1, 2 ... 1000, поэтому каждый бин и его история (свободное место) составляют 1 + 2 + ... + 1000 = 1000 * 999 / 2 = 499500 значений, где вы ожидаете 1000 значений. Вот где соотношение 500x приходит. - person Radim Vansa; 07.04.2014
comment
Я не говорю, что это не безумие, но ваш шаблон использования — это то, для чего SingleFileStore не предназначен. Ожидается, что распределение размеров входа будет примерно постоянным. - person Radim Vansa; 07.04.2014
comment
Я понимаю, откуда 500. я имел в виду, что даже если я правильно рассчитал ожидаемый размер, соотношение все равно было бы примерно таким же (и увеличивалось со временем). я также думаю, что мой вариант использования должен быть более распространенным, чем вы думаете - любой вариант использования, в котором вы сохраняете образцы данных любого типа, будет включать постоянно увеличивающиеся значения, сохраняемые - person radai; 07.04.2014
comment
Вопрос в том, является ли это хорошей практикой - часто вам не нужно просматривать все данные при чтении (особенно при чтении для обновления). Рекомендуемый способ хранения таких списков — разбивать их на записи ограниченного размера со ссылкой на следующий ключ. Кроме того, если бы вы не увеличили все записи синхронно, новые (более короткие) списки заняли бы место, оставшееся после старых списков. - person Radim Vansa; 07.04.2014
comment
для меня это усложнило бы то, что в противном случае было бы простым прямым механизмом, допускающим основные недостатки в инфраструктуре. это также сделало бы другие операции в стиле rrdb более сложными для написания. - person radai; 07.04.2014
comment
Конечно, я понимаю ваши опасения. Я рекомендую пожертвовать простотой ради лучшей производительности; вот почему люди обычно используют кеши - получить лучшую производительность. В любом случае, я признаю, что неспособность работать по-твоему — это недостаток. Либо подождите, пока PR справятся с этим (эти PR обсуждались и теперь ждут некоторых обновлений от автора), обновите свой код, чтобы он работал по-другому, либо интегрируйте PR самостоятельно. Это с открытым исходным кодом, это зависит от вас. - person Radim Vansa; 07.04.2014