Batch Process

29 views
Skip to first unread message

Hudson Dunice

unread,
Jan 21, 2014, 7:13:52 PM1/21/14
to php-ar...@googlegroups.com
Boa noite pessoal,

Gostaria de algumas sugestões em como melhorar a performance de um processo automático. Esse processo automático consiste na leitura de alguns arquivos .txt totalizando 7Gb em que preciso extrair cada linha e gerar alguns números aleatórios.

Esses números e transações são gravados em uma tabela e a combinação entre número de série e número da sorte não poderão repetir para outro usuário. Ao todo, são 100.000.000 de números a serem distribuídos, ou seja, coisa pra caramba. Atualmente, meu processo automático está demorando 6 dias e gostaria que fosse (muito) mais performático.

Para os números, utilizo as seguintes tabelas:
numero_serie
id_numero_serie PK auto_increment 
nu_serie VARCHAR(3)
 
numero_sorte 
id_numero_sorte PK auto_increment
nu_sorte VARCHAR(5)

Esses números são gravados nessa tabela:
transacao_numero_sorte
id_numero_serie PK  FK -> numero_serie 
id_numero_sorte PK FK -> numero_sorte 
id_transacao FK -> transacao
 
Além disso, uma mesma transação pode ter N combinações de números e não poderão ser sequenciais, ou seja, deverão ser aleatórios.

Sugestões?

Obrigado.

Mayck Xavier

unread,
Jan 22, 2014, 5:39:43 AM1/22/14
to php-ar...@googlegroups.com

Hudson, acredito que seria interessante fazer um profiling  da sua aplicação. Assim você pode ver onde tem um maior gargalo e aplicar alguma melhoria, como dar unset em variáveis não utilizadas. Talvez usar uma conexão persistente com o banco de dados possa ajudar também.
Não consigo pensar em muita coisa no momento.
Talvez se você configurar o banco pra gravar os dados e na ram e de tempos em tempos salvar em disco, a performance aumente. Você pode dar mais detalhes?

Atenciosamente,
Mayck Xavier
Lab01 Tecnologia
www.lab01.com.br
21-9-8100-4206
Enviado por telefone

--
Você está recebendo esta mensagem porque se inscreveu no grupo "PHP Architect" dos Grupos do Google.
Para cancelar a inscrição neste grupo e parar de receber seus e-mails, envie um e-mail para php-architec...@googlegroups.com.
Para obter mais opções, acesse https://groups.google.com/groups/opt_out.

ivo nascimento

unread,
Jan 22, 2014, 7:06:36 AM1/22/14
to php-ar...@googlegroups.com
Esta tarefa pode ser dividida em parte e paralelizada?
A analise doa numeros é independente ou depende dos numeros vizinho?
Essea algum tipo de pre-processamento q torne possivel simplificar o processamento final?
Existe poasibilidade de otimizacao?

Essas sao algumas das perguntas q só quem conhece o algoritmo(vc) pode responder e que tentar ajudar a reaponder se vc compartilhar. 


--
Ivo Nascimento - Iann
-------------------------------------------------
   http://about.me/ivonascimento 
-------------------------------------------------

Newton Wagner

unread,
Jan 22, 2014, 7:13:21 AM1/22/14
to php-ar...@googlegroups.com
Em uma experiência com processamento de grandes arquivos que tive, a solução foi passar a tratar os dados diretamente no banco de dados, com um processo semelhante ao ETL de DW.

Mas pra saber se é viável, só conhecendo a lógica do processamento.




2014/1/22 Mayck Xavier <mayck...@gmail.com>

Evaldo Barbosa

unread,
Jan 22, 2014, 7:23:08 AM1/22/14
to php-ar...@googlegroups.com
Uma vez tive um problema com um processamento grande assim. O que resolveu foi gerar a lista dos INSERTS e, de tempos em temos, salvar em arquivo, zerar a lista e começar de novo o processo.
Ao final do processamento mandar tudo pro banco.

Não sei se ajuda no seu caso, mas no meu resolveu.

Maykonn Welington Candido

unread,
Jan 22, 2014, 7:26:53 AM1/22/14
to php-ar...@googlegroups.com
Como estão atualmente a leitura dos arquivos de texto e as inserções no banco de dados? Se possível tente explicar ou mostrar como se dá esse processo.

Atenciosamente,

Maykonn Welington Candido


--

Daniel Maffioletti

unread,
Jan 22, 2014, 7:42:36 AM1/22/14
to php-ar...@googlegroups.com
Não temos muitas informações. Talvez o sistema já contemple a sugestão apresentada. Más, se cada linha do arquivo verificado, após os ajustes é feito um insert, pode ser lento. Talvez agrupando os dados para inserir em lote pode melhorar os resultados.

--------------------------------------------------------
Daniel Maffioletti
Brasília-DF

Hudson Dunice

unread,
Jan 22, 2014, 8:07:03 AM1/22/14
to php-ar...@googlegroups.com
@Mayck, o gargalo até sei onde é. É exatamente nesse método:
    protected function gerarNumeroSorte()
    {
        try {
            $idNumeroSerie = $this->listaNumeroSerie[mt_rand(0, self::MAX_SERIAL_NUMBER)]['id_numero_serie'];
            $idNumeroSorte = $this->listaNumeroSorte[mt_rand(0, self::MAX_SELECTED_NUMBER)]['id_numero_sorte'];

            $sql = '
                INSERT INTO transacao_numero_sorte (id_sorteio, id_numero_serie, id_numero_sorte, id_transacao)
                VALUES (' . $this->transacao->id_sorteio . ', ' . $idNumeroSerie . ', ' . $idNumeroSorte . ', ' . $this->transacao->id_transacao . ')
            ';

            $this->container['db']->exec($sql);
        } catch(\PDOException $e) {
            $this->gerarNumeroSorte();
        }
    }

Ou seja, caso haja choque nos números de série e números da sorte, o método manda de volta pra ele mesmo. E, caso um usuário tenha 200 números a receber, o processo vai executar essa query 200x no mínimo.

Antes de chegar nesse método, carrego muita coisa em memória. E feito isso, melhorou consideravelmente. Mas nessa etapa não estou encontrando uma solução.

@Ivo, o processo poderia ser separado em etapas sim. Mas não vejo se seria mais performático tendo em vista que, como os dados da transação já está em memória, basta gravar e distribuir os números da sorte, não?

@Evaldo, o ideal seria esse, mas como faço insert de duas PKs, como garantir os números que foram inseridos não darão problema?

@Newton, entendo praticamente nada de ETL, tem como demonstrar com um exemplo? Até poderia gravar as transações e gerar os números após isso, mas... como fazer? Tem idéia?

@Maykonn, a leitura do arquivo/ponteiro, gravação da transação é extremamente rápida. Já testei e está excelente. O problema é justamente no método que acabei de citar.

@Daniel, como disse, realmente o agrupamento dos dados para o insert seria o ideal. Eu consigo garantir que não serão repetidos pelo fato da tabela possuir as respectivas FKs. Mas tem idéia de como fazê-lo e garantir que as quantidades exatas serão distribuídas? 

Além disso, pessoal, fiz uma query para tentar fazer um insert em batch, segue:
-- Considerem que essa transação possui 100 números a serem distribuídos

SELECT '1', x.id_numero_serie, y.id_numero_sorte
FROM
    (
        SELECT  *
        FROM    (
            SELECT  @cnt := COUNT(*) + 1,
                    @lim := 10
            FROM    numero_serie
            ) vars
        STRAIGHT_JOIN
            (
            SELECT  r.*,
                    @lim := @lim - 1
            FROM    numero_serie r
            WHERE   (@cnt := @cnt - 1)
                    AND RAND() < @lim / @cnt
            ) i
    ) x

JOIN
    (
        SELECT  *
        FROM    (
            SELECT  @cnt := COUNT(*) + 1,
                    @lim := 10
            FROM    numero_sorte
            ) vars
        STRAIGHT_JOIN
            (
            SELECT  r.*,
                    @lim := @lim - 1
            FROM    numero_sorte r
            WHERE   (@cnt := @cnt - 1)
                    AND RAND() < @lim / @cnt
            ) i
    ) y
WHERE 
    CONCAT(x.nu_serie, y.nu_sorteavel) NOT IN (
        SELECT CONCAT(ns2.nu_serie, nss2.nu_sorteavel) FROM transacao_numero_sorte tns
        INNER JOIN numero_serie ns2 ON tns.id_numero_serie = ns2.id_numero_serie
        INNER JOIN numero_sorte nss2 ON tns.id_numero_sorte = nss2.id_numero_sorte
    )
LIMIT 100
;

Como vêem, essa irá distribuir 100 números, o problema é que caso exista 20 números cadastrados, a query retornará apenas 80... Além disso, ela retorna resultados como:
'1',   '15',    '27580'
'1',   '165',   '27580'
'1',   '204',   '27580'
'1',   '233',   '27580'

'1',   '15',    '48591'
'1',   '165',   '48591'
'1',   '204',   '48591'
'1',   '233',   '48591'

'1',   '15',    '62230'
'1',   '165',   '62230'
'1',   '204',   '62230'
'1',   '233',   '62230'

Ou seja, as combinações deveriam ser aleatórias, é como se a query estivesse retornando uma matriz de 10x10.

Obrigado, pessoal.


--

Hudson Dunice

61 9156.0982
dunice.com.br


2014/1/22 Daniel Maffioletti <dmaffi...@gmail.com>

Maykonn Welington Candido

unread,
Jan 22, 2014, 8:16:25 AM1/22/14
to php-ar...@googlegroups.com
Só para complementar, tente fazer apenas 1 insert:

Atenciosamente,

Maykonn Welington Candido


Evaldo Barbosa

unread,
Jan 22, 2014, 8:35:14 AM1/22/14
to php-ar...@googlegroups.com
Bom, vejo que você pode usar um terceiro elemento ai (sqlite, mongodb) para colecionar os números já utilizados. Utilizar uma coleção com milhões de números pré-gerados poderia te ajudar um pouco.
Gerar os números sob demanda caso excedesse os pré-gerados não iria te consumir muito recurso.

Nos comunique da solução.

Atenciosamente,

Evaldo Barbosa
Analista de Sistemas / Desenvolvedor Web

ESTOU SUBSTITUINDO ESSA CAIXA DE E-MAIL PELA:
> evaldo...@gmail.com
Visualizar meu perfil
http://www.vivaolinux.com.br/~collapse
http://www.twitter.com/evaldobarbosa


2014/1/22 Hudson Dunice <dun...@gmail.com>

Mayck Xavier

unread,
Jan 22, 2014, 10:04:51 AM1/22/14
to php-ar...@googlegroups.com
Hudson, recursividade consome bastante memória.
Talvez quando chame de novo o método, as variáveis $idNumeroSerie, $idNumeroSorte e $sql se mantenham em memória, dentro do seu escopo, claro.

Tenta fazer um profiling com o xdebug. Isso pode ajudar um pouco.
--
Atenciosamente,
Mayck Xavier
Lab01 Tecnologia
www.lab01.com.br
21-9-8100-4206


22 de janeiro de 2014 11:35
Bom, vejo que você pode usar um terceiro elemento ai (sqlite, mongodb) para colecionar os números já utilizados. Utilizar uma coleção com milhões de números pré-gerados poderia te ajudar um pouco.
Gerar os números sob demanda caso excedesse os pré-gerados não iria te consumir muito recurso.

Nos comunique da solução.

Atenciosamente,

Evaldo Barbosa
Analista de Sistemas / Desenvolvedor Web

ESTOU SUBSTITUINDO ESSA CAIXA DE E-MAIL PELA:
> evaldo...@gmail.com
Visualizar meu perfil
http://www.vivaolinux.com.br/~collapse
http://www.twitter.com/evaldobarbosa



--
Você está recebendo esta mensagem porque se inscreveu no grupo "PHP Architect" dos Grupos do Google.
Para cancelar a inscrição neste grupo e parar de receber seus e-mails, envie um e-mail para php-architec...@googlegroups.com.
Para obter mais opções, acesse https://groups.google.com/groups/opt_out.
22 de janeiro de 2014 11:07
22 de janeiro de 2014 10:42
Não temos muitas informações. Talvez o sistema já contemple a sugestão apresentada. Más, se cada linha do arquivo verificado, após os ajustes é feito um insert, pode ser lento. Talvez agrupando os dados para inserir em lote pode melhorar os resultados.

--------------------------------------------------------
Daniel Maffioletti
Brasília-DF



--
Você está recebendo esta mensagem porque se inscreveu no grupo "PHP Architect" dos Grupos do Google.
Para cancelar a inscrição neste grupo e parar de receber seus e-mails, envie um e-mail para php-architec...@googlegroups.com.
Para obter mais opções, acesse https://groups.google.com/groups/opt_out.
22 de janeiro de 2014 10:26
Como estão atualmente a leitura dos arquivos de texto e as inserções no banco de dados? Se possível tente explicar ou mostrar como se dá esse processo.

Atenciosamente,

Maykonn Welington Candido



--
Você está recebendo esta mensagem porque se inscreveu no grupo "PHP Architect" dos Grupos do Google.
Para cancelar a inscrição neste grupo e parar de receber seus e-mails, envie um e-mail para php-architec...@googlegroups.com.
Para obter mais opções, acesse https://groups.google.com/groups/opt_out.
22 de janeiro de 2014 10:23

Ismael Vacco

unread,
Jan 22, 2014, 4:25:59 PM1/22/14
to php-ar...@googlegroups.com
Como vc tem que ler arquivos, acho que daria para utilizar Map Reduce.  Creio que o mais performático seja o Hadoop.

Na fase de map vc captura as informações do txt e na fase de reduce você cruza as informações e gera um indice disso tudo em texto.

Gera uma segunda map reduce para atrelar o indices criados a transação.


Abraços


2014/1/22 Mayck Xavier <mayck...@gmail.com>



--
Ismael Vacco
Systems Analyst


postbox-contact.jpg
compose-unknown-contact.jpg
postbox-contact.jpg
postbox-contact.jpg

caferrari

unread,
Jan 23, 2014, 7:51:54 AM1/23/14
to php-ar...@googlegroups.com
Bom dia,

Achei seu problema super divertido e resolvi implementar alguma coisa para tentar resolve-lo. Minha solução foi criar um arquivo na RAM e escrever os 100.000.000 números de forma sequêncial, depois, utilizando o fseek eu fui alterando a posição desses numeros de forma randômica e, por fim, copiei o arquivo randomizado para o disco.

O processo de geração levou 14 minutos, consumiu exatamente 861Mb de memória e gerou um arquivo final com 900Mb.

A vantagem desse processo é que não vai existir o overhead de verificação e muito menos o problema de colisão de valores e a geração desse arquivo pode ser feita de forma assincrona, antes de rodar o update final da sua base de dados.

Acredito que assim, seu processo deve cair para algumas horas, ou minutos, dependendo da velocidade de escrita e da forma que você vai fazer as transações do seu banco de dados.


Fiz um segundo script para leitura para percorrer e escrever na tela os numeros.

https://gist.github.com/caferrari/8578002

Abraço e espero que seja útil.

postbox-contact.jpg
postbox-contact.jpg
compose-unknown-contact.jpg
postbox-contact.jpg

caferrari

unread,
Jan 26, 2014, 4:58:25 PM1/26/14
to php-ar...@googlegroups.com
Agora a solução está mais "apresentável" e reduzi o tempo de embaralhamento de 100 milhões de numeros para apenas 4 minutos.

https://github.com/caferrari/Shuffle

[]'s
postbox-contact.jpg
postbox-contact.jpg
postbox-contact.jpg
compose-unknown-contact.jpg

Hudson Dunice

unread,
Jan 26, 2014, 5:32:10 PM1/26/14
to php-ar...@googlegroups.com
Fala, @caferrari!

Não respondi o seu e os outros comentários pois estava (tentando ficar) de recesso!

Então após sua idéia para o problema que estava tendo vi uma luz no fim do túnel!

Só que fui muito preguiçoso e escrevi de forma sequencial os números que precisava, já que a quantidade era fixa. Após isso, rodo o shuf do Unix e transfiro parte do consumo de memória pro Linux, já que o script PHP já consome, por si só, muita memória.

Uma vez tendo o seed, apenas rodo o shuf e voilà!

Depois, tendo um tempinho, dou uma olhada no projeto que criaste e contribuirei!

Abs,


--

Hudson Dunice

61 9156.0982
dunice.com.br


2014/1/26 caferrari <cafe...@gmail.com>
postbox-contact.jpg
postbox-contact.jpg
postbox-contact.jpg
compose-unknown-contact.jpg

caferrari

unread,
Jan 26, 2014, 5:52:41 PM1/26/14
to php-ar...@googlegroups.com
Pow, não conhecia o shuf =/

Ele resolveu os 100 milhões em 42 segundos =/

andre@rizza:~/Desktop/Shuffle$ time shuf -i 0-99999999 > numbers.txt

real    0m42.212s
user    0m36.057s
sys    0m2.229s

compose-unknown-contact.jpg
postbox-contact.jpg
postbox-contact.jpg
postbox-contact.jpg

Hudson Dunice

unread,
Jan 26, 2014, 5:59:29 PM1/26/14
to php-ar...@googlegroups.com
Rsrs. O grupo é pra isso mesmo. Vivendo e aprendendo.

Mas valeu, cara! Obrigado, mesmo!
postbox-contact.jpg
postbox-contact.jpg
compose-unknown-contact.jpg
postbox-contact.jpg
Reply all
Reply to author
Forward
0 new messages