Самый быстрый способ записать огромные данные в текстовый файл Java

Мне нужно записать огромные данные в файл text[csv]. Я использовал BufferedWriter для записи данных, и мне потребовалось около 40 секунд, чтобы записать 174 МБ данных. Это самая быстрая скорость, которую может предложить Java?

bufferedWriter = new BufferedWriter ( new FileWriter ( "fileName.csv" ) );

Примечание. Эти 40 секунд включают в себя также время итерации и выборки записей из набора результатов. :) . 174 МБ для 400000 строк в наборе результатов.


person Rakesh Juyal    schedule 30.06.2009    source источник
comment
У вас случайно не был активен антивирус на машине, где вы запускаете этот код?   -  person Thorbjørn Ravn Andersen    schedule 18.09.2011


Ответы (7)


Вы можете попробовать удалить BufferedWriter и просто использовать FileWriter напрямую. В современной системе есть большая вероятность, что вы все равно просто записываете в кэш-память диска.

Мне требуется 4-5 секунд, чтобы записать 175 МБ (4 миллиона строк) — это на двухъядерном компьютере Dell с частотой 2,4 ГГц под управлением Windows XP с диском Hitachi емкостью 80 ГБ, 7200 об/мин.

Можете ли вы выделить, сколько времени занимает поиск записей и сколько времени — запись файлов?

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

public class FileWritingPerfTest {
    

private static final int ITERATIONS = 5;
private static final double MEG = (Math.pow(1024, 2));
private static final int RECORD_COUNT = 4000000;
private static final String RECORD = "Help I am trapped in a fortune cookie factory\n";
private static final int RECSIZE = RECORD.getBytes().length;

public static void main(String[] args) throws Exception {
    List<String> records = new ArrayList<String>(RECORD_COUNT);
    int size = 0;
    for (int i = 0; i < RECORD_COUNT; i++) {
        records.add(RECORD);
        size += RECSIZE;
    }
    System.out.println(records.size() + " 'records'");
    System.out.println(size / MEG + " MB");
    
    for (int i = 0; i < ITERATIONS; i++) {
        System.out.println("\nIteration " + i);
        
        writeRaw(records);
        writeBuffered(records, 8192);
        writeBuffered(records, (int) MEG);
        writeBuffered(records, 4 * (int) MEG);
    }
}

private static void writeRaw(List<String> records) throws IOException {
    File file = File.createTempFile("foo", ".txt");
    try {
        FileWriter writer = new FileWriter(file);
        System.out.print("Writing raw... ");
        write(records, writer);
    } finally {
        // comment this out if you want to inspect the files afterward
        file.delete();
    }
}

private static void writeBuffered(List<String> records, int bufSize) throws IOException {
    File file = File.createTempFile("foo", ".txt");
    try {
        FileWriter writer = new FileWriter(file);
        BufferedWriter bufferedWriter = new BufferedWriter(writer, bufSize);
    
        System.out.print("Writing buffered (buffer size: " + bufSize + ")... ");
        write(records, bufferedWriter);
    } finally {
        // comment this out if you want to inspect the files afterward
        file.delete();
    }
}

private static void write(List<String> records, Writer writer) throws IOException {
    long start = System.currentTimeMillis();
    for (String record: records) {
        writer.write(record);
    }
    // writer.flush(); // close() should take care of this
    writer.close(); 
    long end = System.currentTimeMillis();
    System.out.println((end - start) / 1000f + " seconds");
}
}
person David Moles    schedule 30.06.2009
comment
@rozario каждый вызов записи должен производить только около 175 МБ, а затем удалять себя. в противном случае вы получите 175 МБ x 4 разных вызова записи x 5 итераций = 3,5 ГБ данных. вы можете проверить возвращаемое значение из file.delete() и, если оно ложно, сгенерировать исключение. - person David Moles; 14.04.2011
comment
Обратите внимание, что writer.flush() в этом случае не нужен, потому что writer.close() сбрасывает неявность памяти. Кстати: лучшие практики рекомендуют использовать попытаться закрыть ресурс вместо явного вызова close(). - person patryk.beza; 25.04.2015
comment
FWIW, это было написано для Java 5, которая, по крайней мере, не была задокументирована для очистки при закрытии и не имела попытки с ресурсами. Вероятно, это могло бы использовать обновление. - person David Moles; 27.04.2015
comment
Работают отлично для меня! - person Abner Escócio; 31.08.2018
comment
У меня тоже такая же проблема, но в моем случае мне нужно создать zip-файл с размером 100csv, каждый csv имеет размер 7-8 МБ. Какой возможный класс можно использовать для быстрой загрузки/создания файла csv. - person NobesInd; 11.06.2020
comment
@InduKaur Вот хороший учебник по написанию CSV на Java, это не так сложно сделать вручную. baeldung.com/java-csv Если вам нужна библиотека, я не пробовал эту я сам, но он утверждает, что он очень быстрый: github.com/osiegmar/FastCSV - person David Moles; 11.06.2020
comment
@David Moles Я пытался использовать предложение кода github, но для записи CSV-файла размером 6 МБ требуется 1 секунда. Это правильное время или оно должно быть меньше? - person NobesInd; 12.06.2020
comment
@InduKaur Очень сложно сказать, не зная точно, что делает ваш код и как выглядят данные. Я предлагаю опубликовать отдельный вопрос с минимальным воспроизводимым примером. - person David Moles; 12.06.2020
comment
Хорошо, я сделаю это. - person NobesInd; 12.06.2020
comment
@DavidMoles, я обновил свой вопрос здесь. Если вы можете проверить и помочь мне stackoverflow.com/questions/62350539/ - person NobesInd; 16.06.2020
comment
Я только что просмотрел документацию по Java 1.1 для Writer.flush(), и там написано: «Закройте поток, сначала сбросив его».». Таким образом, вызов flush() перед close() никогда не был необходим. Кстати, одна из причин, по которой BufferedWriter может быть бесполезной, заключается в том, что FileWriter, специализация OutputStreamWriter, в любом случае должна иметь собственную буферизацию, когда она выполняет преобразование последовательностей символов в последовательности байтов в целевой кодировке. Наличие большего количества буферов во внешнем интерфейсе не помогает, когда кодировщик набора символов все равно должен сбрасывать свой меньший байтовый буфер с более высокой скоростью. - person Holger; 01.07.2020
comment
@Holger Вы правы, close() задокументировано для сброса, IDK, как я в 2009 году пропустил это. Re: буферы, возможно, вы правы, но OutputStreamWriter документы рекомендую завернуть его в BufferedWriter для максимальной эффективности. (И, кажется, сделали это с 1.1.) - person David Moles; 01.07.2020
comment
Действительно, но фактические последствия дополнительной буферизации и то, как решить, использовать ее или нет, никогда не рассматривались должным образом в документации или руководствах (насколько мне известно). Обратите внимание, что NIO API вообще не имеет аналога Buffered… для типов каналов. - person Holger; 02.07.2020

попробуйте файлы с отображением памяти (требуется 300 м / с, чтобы записать 174 МБ в моем m / c, core 2 duo, 2,5 ГБ ОЗУ):

byte[] buffer = "Help I am trapped in a fortune cookie factory\n".getBytes();
int number_of_lines = 400000;

FileChannel rwChannel = new RandomAccessFile("textfile.txt", "rw").getChannel();
ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, buffer.length * number_of_lines);
for (int i = 0; i < number_of_lines; i++)
{
    wrBuf.put(buffer);
}
rwChannel.close();
person Deepak Agarwal    schedule 17.02.2011
comment
что означает aMessage.length(), когда вы создаете экземпляр ByteBuffer? - person Hotel; 27.09.2012
comment
К вашему сведению, запуск этого на MacBook Pro (конец 2013 г.), 2,6 ГГц Core i7, с Apple 1 ТБ SSD занимает около 140 мс для 185 мегабайт (строки = 4 миллиона) - person Egwor; 16.04.2014
comment
@JerylCook Сопоставление памяти полезно, когда вы знаете точный размер. Здесь мы заранее резервируем место в буфере*число_файлов. - person Deepak Agarwal; 19.12.2016
comment
Спасибо! Могу ли я использовать его для файла размером более 2 ГБ? Карта MappedByteBuffer (MapMode var1, long var2, long var4): if (var4 › 2147483647L) { throw new IllegalArgumentException (размер превышает Integer.MAX_VALUE) - person Mikhail Ionkin; 24.03.2018
comment
Какой волшебный метод, 105мс на Dell core i5(1.6,2.3)Ghz - person FSm; 03.10.2020

Только ради статистики:

Машина старая Dell с новым SSD

Процессор: Intel Pentium D 2,8 ГГц

Твердотельный накопитель: Патриот Инферно 120 ГБ SSD

4000000 'records'
175.47607421875 MB

Iteration 0
Writing raw... 3.547 seconds
Writing buffered (buffer size: 8192)... 2.625 seconds
Writing buffered (buffer size: 1048576)... 2.203 seconds
Writing buffered (buffer size: 4194304)... 2.312 seconds

Iteration 1
Writing raw... 2.922 seconds
Writing buffered (buffer size: 8192)... 2.406 seconds
Writing buffered (buffer size: 1048576)... 2.015 seconds
Writing buffered (buffer size: 4194304)... 2.282 seconds

Iteration 2
Writing raw... 2.828 seconds
Writing buffered (buffer size: 8192)... 2.109 seconds
Writing buffered (buffer size: 1048576)... 2.078 seconds
Writing buffered (buffer size: 4194304)... 2.015 seconds

Iteration 3
Writing raw... 3.187 seconds
Writing buffered (buffer size: 8192)... 2.109 seconds
Writing buffered (buffer size: 1048576)... 2.094 seconds
Writing buffered (buffer size: 4194304)... 2.031 seconds

Iteration 4
Writing raw... 3.093 seconds
Writing buffered (buffer size: 8192)... 2.141 seconds
Writing buffered (buffer size: 1048576)... 2.063 seconds
Writing buffered (buffer size: 4194304)... 2.016 seconds

Как мы видим, необработанный метод медленнее буферизованного.

person Damian Leszczyński - Vash    schedule 23.05.2011
comment
Однако буферизованный метод становится медленнее, когда размер текста больше. - person FSm; 04.02.2017

Ваша скорость передачи, скорее всего, не будет ограничена Java. Вместо этого я бы подозревал (в произвольном порядке)

  1. скорость передачи из базы
  2. скорость передачи на диск

Если вы прочитаете полный набор данных, а затем запишете его на диск, это займет больше времени, так как JVM придется выделять память, и запись db rea/disk будет происходить последовательно. Вместо этого я бы записывал в буферизованный писатель для каждого чтения, которое вы делаете из БД, и поэтому операция будет ближе к параллельной (я не знаю, делаете ли вы это или нет)

person Brian Agnew    schedule 30.06.2009

Для этих громоздких операций чтения из БД вам может потребоваться настроить размер выборки вашего заявления. Это может сэкономить много обращений к БД.

http://download.oracle.com/javase/1.5.0/docs/api/java/sql/Statement.html#setFetchSize%28int%29

person gpeche    schedule 30.08.2010

Для тех, кто хочет сократить время извлечения записей и создания дампа в файл (т. е. без обработки записей), вместо помещения их в ArrayList добавьте эти записи в StringBuffer. Примените функцию toSring(), чтобы получить одну строку и сразу записать ее в файл.

Для меня время поиска сократилось с 22 до 17 секунд.

person Rajendra Dangwal    schedule 19.05.2020
comment
Это был просто пример создания поддельных записей — я бы предположил, что в реальном мире записи поступают откуда-то еще (база данных в случае OP). Но да, если вам нужно сначала прочитать весь контент в память, StringBuffer, вероятно, будет быстрее. Необработанный массив строк (String[]) также, вероятно, будет быстрее. - person David Moles; 05.11.2020
comment
Использование StringBuffer приведет к потере большого количества ресурсов. Большинство стандартных java-писателей используют StreamEncoder внутри, и у него есть собственный буфер размером 8192 байта. Даже если вы создадите одну строку всех данных, она будет поступать в виде кусков и кодироваться из символов в байты []. Лучшим решением было бы реализовать собственный Writer, который напрямую использует метод write(byte[]) FileOutputStream, который использует базовый собственный метод writeBytes. - person krishna Telgave; 11.04.2021
comment
как сказал @DavidMoles, исходный формат данных также очень важен в этом сценарии. Если данные уже доступны в байтах, запишите их напрямую в FileOutputSteam. - person krishna Telgave; 11.04.2021

person    schedule
comment
Пожалуйста, добавьте текст, объясняющий, почему этот ответ лучше других ответов. Наличие комментариев в коде недостаточно. - person Benjamin Lowry; 09.10.2016
comment
Причина, по которой это могло бы быть лучше: это сценарий в реальном времени и пример рабочего состояния. Другие его преимущества: он обрабатывает чтение, обработку и запись асинхронно... Он использует эффективный файл java api (т.е.) с произвольным доступом, который является потокобезопасным, и несколько потоков могут читать и писать на нем одновременно. Это не вызывает накладных расходов памяти во время выполнения, а также не приводит к сбою системы ... это многоцелевое решение для устранения сбоев обработки записей, которые можно отслеживать в соответствующем потоке. Пожалуйста, дайте мне знать, если я могу помочь больше. - person RAM; 10.10.2016
comment
Спасибо, это та информация, которая нужна вашему сообщению. Возможно, стоит добавить его в тело сообщения :) - person Benjamin Lowry; 11.10.2016
comment
Если с 10 потоками для записи 2 ГБ данных требуется 349,317 секунд, то это может претендовать на самый медленный способ записи огромных данных (если вы не имеете в виду миллисекунды) - person Deepak Agarwal; 19.12.2016