Java: dividir el archivo de texto SQL grande en el delimeter usando Scanner (OutOfMemoryError)

StackOverflow https://stackoverflow.com/questions/20353915

  •  25-08-2022
  •  | 
  •  

Pregunta

Estoy tratando de escribir una aplicación que tome un archivo de texto SQL muy grande ~ 60GB (257 millones de líneas) y dividirá cada una de las declaraciones de copia en archivos de texto separados.

Sin embargo, el código que estoy usando actualmente causa un OutOfMemoryError debido a las líneas que exceden el límite del búfer del escáner. La primera declaración será de ~ 40 millones de líneas de largo.

public static void readFileByDelimeter(String fileName, String requestType, String output) throws FileNotFoundException {

//creating file instance
File file = new File(fileName);

//create scanner instance
Scanner scanner = new Scanner(file, "latin1");

//set custom delimeter
scanner.useDelimeter("COPY");

int number = 0;
System.out.println("Running......");
while (scanner.hasNext()){
    String line = scanner.next();
    if (line.length() > 20) {
        //save statements to seperate SQL files
        PrintWriter out = new PrintWriter("statement" + number + ".sql");
        out.println("COPY" + line.trim());
        out.close();
        }
        number++;
    }

System.out.println("Completed");
}

Proporcione recomendación sobre si este es el método incorrecto para realizar esto o las alteraciones al método existente.

Gracias

¿Fue útil?

Solución

Primero, ¡por qué está creando o algún otro proceso es crear un archivo de 60 GB! Tal vez deba echar un vistazo a ese proceso para corregir ese proceso para generar un archivo de texto SQL más pequeño en lugar de crear un nuevo proceso. Sin embargo, si esto es algo único que debe hacer, entonces eso podría estar bien, pero para abordar su pregunta, usaría el BufferedReader para leer y procesar los registros si es un archivo grande como lo indicó.

BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
   // process the line. and write into your output file and close the file.
   }
br.close();

Otros consejos

Yo personalmente: uso BufferedReader en lugar del escáner. También tiene un método conveniente de readline () y nunca he tenido problemas de rendimiento con él. Lo único es que necesitaría verificar manualmente si una lectura de línea es una que desea procesar, pero eso generalmente es tan simple como aplicar los métodos de clase de cadena.

Esa no es una respuesta a su pregunta real, pero considero que es una alternativa decente fácil de usar.

Prueba algo como esto (pero más bonito):

Scanner sc = new Scanner(new BufferedReader(new FileReader(file)));

Esto decora todo con un BufferedReader, lo que significa que no todo el contenido del archivo se cargará en la memoria a la vez. Puede usar el escáner de la misma manera.

Intenta usar un BufferedReader. El uso directo del escáner con el archivo o las secuencias de archivos sin procesar cargarán los datos en la memoria y no lo eliminarán en GC. El enfoque de BETS es usar BufferedReader y leer una línea a la vez y hacer verificaciones de cadenas manuales y dividir. Si se hace correctamente de esta manera, puede darle al GC suficiente oportunidad para reclamar la memoria cuando sea necesario

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top