Como acelerar a função node_save () do drupal?

9

Estou tendo muitos problemas com a ineficiência de node_save (). Mas o nó está salvando meu problema? É isso que estou tentando descobrir.

Eu criei um loop com 100.000 iterações. Eu criei o mínimo necessário para o objeto nó ser válido e salvar corretamente. Aqui está o código de salvamento do nó:

$node = new stdClass();
        $node->type = "test_page";

        node_object_prepare($node);

        $node->uid = 1;
        $node->title = $node_title;
        $node->status = 1;
        $node->language = LANGUAGE_NONE;
        if($node = node_submit($node)){
            node_save($node);
}

Aqui estão os resultados:

100.000 nós foram salvos, cada um usando node_save (). Demorou 5196,22 segundos para concluir. Isso é apenas 19 economiza um segundo.

Para dizer o mínimo, isso não é aceitável, especialmente quando essa pessoa recebe cerca de 1200 consultas de inserção individuais por segundo e recebe 25.000 inserções por segundo .

Então, o que está acontecendo aqui? Onde está o gargalo? É a função node_save () e como ela é projetada?

Poderia ser o meu hardware? Meu hardware é um servidor de desenvolvimento, ninguém, exceto por mim - Intel dual core, 3Ghz, Ubuntu 12.04 com 16 GB de RAM.

Enquanto o loop é executado, meu uso de recursos é: MySQL 27% de CPU, 6M de RAM; PHP 22% CPU 2M RAM.

Minha configuração do mysql foi feita pelo assistente percona .

O Mysql diz que, se o uso da minha CPU estiver abaixo de 70%, o problema estará ligado ao disco . É verdade que eu tenho apenas uma usina WD Caviar 7200 RPM, mas espero ter mais de 19 pastilhas por segundo, espero!

Há pouco tempo, escrevi sobre como salvar 30.000 nós em um dia . No entanto, para ficar claro, esse nó não tem nada a ver com forças externas. É puramente uma referência para aprender sobre como aumentar a velocidade das chamadas para node_save ().

Realisticamente, preciso inserir 30.000 itens no banco de dados a cada minuto usando o node_save. Se o nó salvar não é uma opção, pergunto-me se posso escrever minha própria função de API drupal "node_batch_save ()" ou algo que aproveite a capacidade do mysql de fazer inserções em massa com a consulta INSERT . Pensamentos sobre como abordar isso?

blue928
fonte
2
Há uma grande diferença entre o desempenho da inserção bruta e o que o node_save fará. Por um lado, o node_save executa uma série de leituras e gravações. Mas não faz sentido discutir possíveis gargalos e otimizações sem mais dados.
Alfred Armstrong #
Você precisa considerar por que está usando o Drupal dessa maneira para seus propósitos. Se você simplesmente quer capturar uma grande quantidade de dados em uma mesa plana e exibi-lo usando Drupal, você pode querer ignorar Drupal completamente quando escrevê-lo e usar um módulo personalizado para integrar os dados usando Visualizações etc.
Alfred Armstrong
Duvido que o gargalo esteja no lado do banco de dados. O salvamento do nó faz muitas coisas em segundo plano: ele invocará vários ganchos (hook_node_presave, hook_entity_presave, hook_node_insert, hook_entity_insert etc.), cada um dos quais pode chamar qualquer número de módulos. Além disso node_save irá reconstruir as permissões para esse nó e ele irá limpar o cache para esse nó ...
Alice Heaton
@AlfredArmstrong Estou criando nós com base nos dados que estão em outro banco de dados. Eu moldo os dados para o tipo de conteúdo correto do drupal e o node_save. Meus clientes são principalmente universidades que desejam mudar para o drupal. Não é incomum que eles tenham entre 200.000 e 1.000.000 nós (conteúdo do site dos depósitos, registros de alunos e professores, etc.) que eles gostariam de migrar após uma década usando sua própria solução na Web. Eu li isso, o que é encorajador, mas ainda menos do que desejável. evolvingweb.ca/story/...
blue928
.. então, eu preferiria ficar o mais drupalmente possível. Usar o nó save com tantos dados garante a integridade. Se não consigo fazer isso funcionar, estou disposto a ser criativo.
blue928

Respostas:

10

Você nunca receberá 30.000 inserções por minuto usando node_save. De jeito nenhum.

Um INSERT é rápido porque é tudo o que faz. O salvamento do nó faz várias inserções (tabela principal, tabela de revisão, uma tabela para cada campo), limpa os caches de entidade e aciona ganchos. Os ganchos são a parte complicada. Se você possui muitos módulos de contribuição (ou mesmo um que se comporta mal) que podem realmente prejudicar o desempenho, especialmente se o autor não considerou o caso de uso "Estou economizando vários nós de uma vez". Por exemplo, eu tive que adicionar isso à minha classe Migrate:

  public function processImport(array $options = array()) {
    parent::processImport($options = array());
    // Do not force menu rebuilding. Otherwise pathauto will try to rebuild
    // in each node_save() invocation.
    variable_set('menu_rebuild_needed', FALSE);
  }

Por outro lado, se você escrever uma função de gravação personalizada que não chama ganchos, você corre o risco de obter dados inconsistentes, em um estado inesperado pelo sistema. Eu nunca recomendaria fazer isso. Inicie o xhprof e veja o que está acontecendo.

Bojan Zivanovic
fonte
Alguns dos módulos de migração existentes, como eles acabam em nós de economia em massa? Quero dizer, no final de tudo, tudo se resume a uma instrução INSERT, certo? Como sua classe de migração é inserida da 'origem' para o 'destino' quando não está usando o nó save, mas ainda precisa manter a integridade dos dados nas tabelas?
blue928
Todos os módulos de migração que encontrei usam um node_save.
Alfred Armstrong
11
@ blue928 Ele está dizendo que ele faz uso node_save(), mas adiciona algum código para mitigar os problemas conhecidos que podem ser causados, como pathauto reconstruir o cache de menu após cada nó salvar
Clive
ah, entendi. Bojan é seu código disponível em um módulo ou online, onde eu pude ver como você lida com gargalos como o path auto? Boa ideia com o xhprof. Vou verificar isso.
blue928
5

Primeiro, instale o XCache / APC (para PHP <5.5) e configure o memcached para o Drupal.

Então você pode otimizar sua configuração do MySQL para consultas pesadas usando o script mysqltuner disponível em: http://mysqltuner.pl

Por exemplo

# performance tweaks (adjusted based on mysqltuner.pl)
query_cache_size = 32M
query_cache_limit = 256M
join_buffer_size = 32M
key_buffer = 8M
max_allowed_packet = 32M
table_cache = 512
sort_buffer_size = 1M
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 1M
myisam_sort_buffer_size = 8M

# When making adjustments, make tmp_table_size/max_heap_table_size equal
tmp_table_size = 16M
max_heap_table_size = 16M

thread_cache_size = 4

Outras sugestões:

  • desabilite os módulos que você não precisa (por exemplo , Devel , módulo principal de registro de banco de dados etc.),
  • atualize seu PHP para o ramo mais recente ou superior,
  • recompile seu PHP para arquitetura de 64 bits ou superior, dependendo da sua CPU,
  • use o dispositivo de armazenamento mais rápido para seus arquivos db ou ambiente LAMP inteiro (por exemplo, SSD ou sistema de arquivos baseado em memória ),
  • use o depurador ou criador de perfil PHP para descobrir qualquer gargalo de desempenho (por exemplo, XDebug Profiler , DTrace ou NuSphere PhpED PHP Profiler ),
  • execute algum comando drush demorado na ferramenta de perfil gprof , para que você possa encontrar também algum gargalo de desempenho
kenorb
fonte
11
Ajustar o MySQL parece fazer uma grande diferença. Eu fui de cerca de 80 node_saves por minuto para cerca de 700 apenas seguindo as dicas fornecidas pelo mysqltuner.pl.
John McCollum 04/02