Lendo arquivo txt muito grande!

Prezados colegas,

Estou precisando ler arquivos grandes com aprox. 300mil linhas de 3,4mil caracteres cada linha para gravar em um banco de dados. Criei umas classes com o BufferedReader e estou pegando as substrings de cada linha e gravando-as uma a uma em um banco MySQL, mas quando chega por volta da linha 65mil estoura uma exception de OutOfMemory e eu já rodei por diversos foruns e não acho nenhuma solução para leitura de arquivos grandes!!! Estou tendo que dividir estes arquivos em várias partes para processar, mas gostaria muito de encontrar uma solução menos TOSCA!!!

Tentei usar o comando “LOAD DATA IN FILE” do SQL, que copia todo o arquivo direto no banco, porém, este comando precisa que exista um separador entre as colunas e meu arquivo não tem!!! eu até poderia concatenar um caractere “;” entre as colunas, mas cairia no mesmo problema do estouro de memoria!!!

Estou quebrando a cabeça e não consigo achar uma solução.
Se alguém puder me ajudar, agradeço muito!!!

Abs,

Mostra aí um formato da linha de arquivo. talvez adicionar o separador não seja tão difícil assim

Não teria que setar o parâmetro de memória da JVM?

O que EU faria se tivesse um problema semelhante ao seu: escreveria um programa em alguma linguagem qualquer (nem precisa ser Java) que pega o arquivo original e o transforma no formato que o LOAD DATA IN FILE requer, e então submeteria esse arquivo a esse comando.
É a maneira mais simples e mais eficiente de resolver seu problema.
Você, como pessoa esperta que deve ser, deve saber que para ler um arquivo-texto linha por linha e o converter para outro arquivo-texto não é preciso carregá-lo todo na memória. Basta processar uma linha de cada vez. Certo?

[quote=entanglement][quote=helderbritto]
Tentei usar o comando “LOAD DATA IN FILE” do SQL, que copia todo o arquivo direto no banco, porém, este comando precisa que exista um separador entre as colunas e meu arquivo não tem!!! eu até poderia concatenar um caractere “;” entre as colunas, mas cairia no mesmo problema do estouro de memoria!!!
[/quote]

O que EU faria se tivesse um problema semelhante ao seu: escreveria um programa em alguma linguagem qualquer (nem precisa ser Java) que pega o arquivo original e o transforma no formato que o LOAD DATA IN FILE requer, e então submeteria esse arquivo a esse comando.
É a maneira mais simples e mais eficiente de resolver seu problema.
Você, como pessoa esperta que deve ser, deve saber que para ler um arquivo-texto linha por linha e o converter para outro arquivo-texto não é preciso carregá-lo todo na memória. Basta processar uma linha de cada vez. Certo?[/quote]

Concordo contigo… é a solução mais adequada e o que acho que vou fazer. Mas gostaria muito de achar uma solução em JAVA para ler arquivos, pois desta maneira poderia criar algumas Threads criar uma fila de processamento automático para rodar de madrugada e não interferir na performance do nosso banco de produção.

Mas de qualquer forma muito obrigado pela sua dica!!! Vou segui-la.

Abraço

Valeu pela dica!! Interessante este artigo!!!

Abs

Tem certeza que não está mantendo os registros guardados na memória (em alguma coleção)? O correto é ler, fazer o que precisa fazer e livrar-se dele o mais cedo possível.

Outra coisa: você está reaproveitando os PreparedStatements? Deve haver apenas um para a conexão, cuidado para não fazer stm = conn.prepareStatement(…) a cada registro, isso vai detonar a performance.

[quote=gomesrod]Tem certeza que não está mantendo os registros guardados na memória (em alguma coleção)? O correto é ler, fazer o que precisa fazer e livrar-se dele o mais cedo possível.

Outra coisa: você está reaproveitando os PreparedStatements? Deve haver apenas um para a conexão, cuidado para não fazer stm = conn.prepareStatement(…) a cada registro, isso vai detonar a performance.[/quote]

Então Gomes… eu estou salvando estas substrings em um Bean e utilizando a gravação via DAO, usando uma session fectory do Hibernate. Com relação às variáveis que eu estou salvando, a cada 100 linhas que leio e gravo, eu chamo o System.gc(); para acionar manualmente o Garbage Collector e limpar as variáveis. Não sei se esta é a melhor prática, mas foi uma solução que encontrei para ter certeza que não são as variáveis que estão estourando a memória.

Se puder me ajudar com isto eu agradeço!!!

Abraço,

Diáriamente leio arquivos grandes aqui na empresa. Porém para executar tal demanda utilizo o banco Oracle 10g. Basta apenas escrever uma rotina que abra o arquivo, leia, trate de acordo com a sua necessidade e grave nas tabelas desejadas…

Possuo uma rotina, que diariamente lê um arquivo em média de 932790 linhas, com 410M.

Essa rotina não demora mais de 20 minutos.

Dica, a versão Oracle Database 10g Express Edition é gratuito para download, apenas fica limitado em tamanho máximo da base de dados de 4GB.

Se você usa as linhas do arquivo para carregar o Bean e só mantém um bean de cada vez então beleza… meu receio é que estivesse guardando tudo em uma lista, ou algo assim.

Já que você falou em Hibernate pensei em outra coisa, ele deve estar mantendo suas entidades como Managed e com isso consumindo memória.
Experimente a cada certo número de linhas (por exemplo, 1.000 , 5.000 , 10.000 , algo assim) chamar um session.flush() e session.clear();
E não se preocupe em chamar o garbage collector manualmente. Antes de estourar pode ter certeza que a JVM vai tentar com todas as suas forças recuperar um pouco de memória.

Olá rodrigo, concatenar os separadores não é difícil, pois eu tenho as posições exatas das substrings gravadas nas linhas, o problema é que para eu concatenar este separador, teria que ler a linha e inserir o separador para gravar novamente a mesma. E fazendo isto, eu cairia no mesmo problema de usar o BufferedReader para ler!

A não ser que você me ajude com alguma outra opção de inserir um separador sem precisar usar o BufferedReader!

Muito obrigado,

[quote=gualtieri]Diáriamente leio arquivos grandes aqui na empresa. Porém para executar tal demanda utilizo o banco Oracle 10g. Basta apenas escrever uma rotina que abra o arquivo, leia, trate de acordo com a sua necessidade e grave nas tabelas desejadas…

Possuo uma rotina, que diariamente lê um arquivo em média de 932790 linhas, com 410M.

Essa rotina não demora mais de 20 minutos.

Dica, a versão Oracle Database 10g Express Edition é gratuito para download, apenas fica limitado em tamanho máximo da base de dados de 4GB.[/quote]

Valeu pela dica gualtieri… não sou nenhum DBA mas tenho alguma noção de SQL e vou ver se acho alguma solução direto no banco mesmo!!!
Vou pesquisar sobre como fazer isto no Oracle!

Valeu… abs

Segue um exemplo, caso o separador seja um ;[code] public static void main(String[] args) throws Exception{
File source = new File(“C:/arq.txt”);
File destination = new File(“C:/arq2.txt”);

	if (!destination.exists()){
		destination.createNewFile();
	}
	Writer w = new FileWriter(destination);
	BufferedWriter writer = new BufferedWriter(w);

	Scanner sc = new Scanner(source);

	while (sc.hasNext()){
		writer.write(sc.nextLine().replaceAll("\\s+", ";"));
		writer.newLine();
	}

	writer.flush();
	writer.close();
}[/code]Deve funcionar

[quote=gomesrod]Se você usa as linhas do arquivo para carregar o Bean e só mantém um bean de cada vez então beleza… meu receio é que estivesse guardando tudo em uma lista, ou algo assim.

Já que você falou em Hibernate pensei em outra coisa, ele deve estar mantendo suas entidades como Managed e com isso consumindo memória.
Experimente a cada certo número de linhas (por exemplo, 1.000 , 5.000 , 10.000 , algo assim) chamar um session.flush() e session.clear();
E não se preocupe em chamar o garbage collector manualmente. Antes de estourar pode ter certeza que a JVM vai tentar com todas as suas forças recuperar um pouco de memória.[/quote]

Mais uma vez obrigado Gomes… vou ver se funciona dar um flush() e um clear() a cada 100 registros e te falo depois!!! Não tinha tentado isto antes!!

Abs

[quote=helderbritto][quote=gualtieri]Diáriamente leio arquivos grandes aqui na empresa. Porém para executar tal demanda utilizo o banco Oracle 10g. Basta apenas escrever uma rotina que abra o arquivo, leia, trate de acordo com a sua necessidade e grave nas tabelas desejadas…

Possuo uma rotina, que diariamente lê um arquivo em média de 932790 linhas, com 410M.

Essa rotina não demora mais de 20 minutos.

Dica, a versão Oracle Database 10g Express Edition é gratuito para download, apenas fica limitado em tamanho máximo da base de dados de 4GB.[/quote]

Valeu pela dica gualtieri… não sou nenhum DBA mas tenho alguma noção de SQL e vou ver se acho alguma solução direto no banco mesmo!!!
Vou pesquisar sobre como fazer isto no Oracle!

Valeu… abs[/quote]

Segue um link que explica bem como realizar essa tarefa no Oracle…

Cara, se vc já teve contato com Pascal não vai ter dificuldades…

Abaixo segue outro link para migrar os dados para o Mysql caso precise…

http://www.convert-in.com/ora2sql.htm

Abraço!

[quote=helderbritto]Prezados colegas,

Estou precisando ler arquivos grandes com aprox. 300mil linhas de 3,4mil caracteres cada linha para gravar em um banco de dados. Criei umas classes com o BufferedReader e estou pegando as substrings de cada linha e gravando-as uma a uma em um banco MySQL, mas quando chega por volta da linha 65mil estoura uma exception de OutOfMemory e eu já rodei por diversos foruns e não acho nenhuma solução para leitura de arquivos grandes!!! Estou tendo que dividir estes arquivos em várias partes para processar, mas gostaria muito de encontrar uma solução menos TOSCA!!!

Tentei usar o comando “LOAD DATA IN FILE” do SQL, que copia todo o arquivo direto no banco, porém, este comando precisa que exista um separador entre as colunas e meu arquivo não tem!!! eu até poderia concatenar um caractere “;” entre as colunas, mas cairia no mesmo problema do estouro de memoria!!!

Estou quebrando a cabeça e não consigo achar uma solução.
Se alguém puder me ajudar, agradeço muito!!!

Abs,[/quote]

Sem vc nos dar o codigo fica difícil opinar, mas quase de certeza que está guardando cada linha na memória. Não é isso que vc quer.
O BufferedReader já lhe dá uma linha por vez. Então vc grava ela e pronto. zero memoria “presa”.
Se vc quiser facilitar , vc pode guarda N linhas e guardar em bach mode para poupar o banco, mas o N é pequeno e ajustável conforme a sua memoria.

Se quiser pode usar o padrão Producer-Consumer (que já deveria estar usando) com multiplas thread , uma lendo e as outras escrevendo. Isto não poupa a memoria, mas aumenta a velocidade. O truque é usar um queue. E este queue que vai consumir memoria.
Ai vc restringe o tamanho do queue para o mesmo N de antes. A vantagem aqui é que o reader para de ler quando o queue fica cheio o que garante que nunca irá estourar a memória.

Valeu pela dica!! Interessante este artigo!!!

Abs[/quote]

Então, eu não usaria Java para fazer isso, faria direto no banco mesmo ou usaria um script/processo para ficar rodando no servidor (como no exemplo)…

[quote=gomesrod]Se você usa as linhas do arquivo para carregar o Bean e só mantém um bean de cada vez então beleza… meu receio é que estivesse guardando tudo em uma lista, ou algo assim.

Já que você falou em Hibernate pensei em outra coisa, ele deve estar mantendo suas entidades como Managed e com isso consumindo memória.
Experimente a cada certo número de linhas (por exemplo, 1.000 , 5.000 , 10.000 , algo assim) chamar um session.flush() e session.clear();
E não se preocupe em chamar o garbage collector manualmente. Antes de estourar pode ter certeza que a JVM vai tentar com todas as suas forças recuperar um pouco de memória.[/quote]

Era isso mesmo Gomes… Não estava dando flush() e clear() na session do hibernate… Vacilo total… Estava achando que era problemas do BufferedReader mas o que causou o OutOfMemory na VM foi a minha session!! Acabei de processar 1 arquivo txt de 500mb em 115min
Não tem a mesma performance do LOAD DATA IN FILE, mas está mais do que satisfatório!!

Valeu meu brother… Salvou meu emprego… Rsrsrs
Abraço

Que bom que resolveu!

Mas mantenha aí na sua “caixa de ferramentas” as outras soluções usando o próprio banco, quem sabe num futuro próximo os volumes aumentem e a performance se torne um problema…

E agora que você viu que o problema não era especificamente na leitura do arquivo, poderá ficar mais tranquilo em criar uma solução que converta para outros formatos.