Jak mogę przyspieszyć funkcję node_save () drupala?

9

Mam dużo problemów z nieefektywnością node_save (). Ale czy węzeł zapisuje mój problem? Właśnie tego próbuję się dowiedzieć.

Utworzyłem pętlę ze 100 000 iteracjami. Stworzyłem absolutne minimum, aby obiekt węzła był poprawny i poprawnie zapisany. Oto kod zapisu węzła:

$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);
}

Oto wyniki:

Zapisano 100 000 węzłów, każdy za pomocą node_save (). Wykonanie tego zadania zajęło 5196,22 sekund. To TYLKO 19 oszczędza sekundę.

Mówiąc co najmniej, jest to nie do przyjęcia, szczególnie gdy ta osoba otrzymuje około 1200 pojedynczych zapytań na sekundę , a ta osoba otrzymuje 25 000 wstawek na sekundę .

Co się tu dzieje? Gdzie jest wąskie gardło? Czy jest z funkcją node_save () i jak jest zaprojektowana?

Czy to może być mój sprzęt? Mój sprzęt to serwer programistyczny, nikt na nim poza mną - dwurdzeniowy Intel, 3Ghz, Ubuntu 12.04 z 16 gigabajtami pamięci RAM.

Podczas działania pętli moje użycie zasobów to: MySQL 27% CPU, 6M RAM; PHP 22% CPU 2M RAM.

Moja konfiguracja mysql została wykonana przez kreatora Percona .

Mysql mówi, że jeśli użycie procesora spadnie poniżej 70%, mój problem jest związany z dyskiem . To prawda, że ​​mam tylko jeden bieg młyna WD Caviar 7200 RPM, ale mam nadzieję, że powinienem otrzymywać więcej niż 19 płytek na sekundę, mam nadzieję!

Nie tak dawno temu pisałem o oszczędzaniu 30 000 węzłów dziennie . Jednak dla jasności ten węzeł nie ma nic wspólnego z żadnymi siłami zewnętrznymi. Jest to wyłącznie test porównawczy, aby dowiedzieć się, jak zwiększyć szybkość wywołań funkcji node_save ().

Realistycznie muszę co minutę wprowadzać do bazy danych 30 000 elementów za pomocą node_save. Jeśli zapisywanie węzłów nie jest opcją, zastanawiam się, czy mogę napisać własną funkcję API drupal „node_batch_save ()” lub coś, co wykorzystuje zdolność mysql do wykonywania wstawiania zbiorczego za pomocą zapytania INSERT . Myśli, jak do tego podejść?

blue928
źródło
2
Istnieje duża różnica między wydajnością wstawiania raw a tym, co zrobi node_save. Po pierwsze, node_save wykonuje serię odczytów i zapisów. Ale nie ma sensu omawianie możliwych wąskich gardeł i optymalizacji bez większej ilości danych.
Alfred Armstrong,
Musisz rozważyć, dlaczego używasz Drupala w ten sposób do swoich celów. Jeśli chcesz po prostu uchwycić wiele danych na płaskim stole i wyświetlić go przy użyciu Drupal, może chcesz bocznego Drupal całkowicie pisząc go i użyć niestandardowego moduł do integracji danych przy użyciu widoków itp
Alfred Armstrong
Wątpię, by szyjka butelki była po stronie bazy danych. Węzeł save robi wiele rzeczy w tle: wywoła szereg haków (hook_node_presave, hook_entity_presave, hook_node_insert, hook_entity_insert itp.), Z których każdy może wywołać dowolną liczbę modułów. Ponadto node_save odbuduje uprawnienia dla tego węzła i będzie wyczyścić pamięć podręczną dla tego węzła ...
Alice Heaton
@AlfredArmstrong Tworzę węzły na podstawie danych znajdujących się w innej bazie danych. Formuję dane do właściwego typu zawartości drupal i zapisuję je w węźle. Moimi klientami są głównie uniwersytety, które chcą przejść na drupal. Często zdarza się, że mają od 200 000 do 1 000 000 węzłów (zawartość witryny oddziału, akta studentów i wykładowców itp.), Do których chcieliby migrować po dekadzie używania własnego w rozwiązaniu internetowym. Przeczytałem to, co jest zachęcające, ale wciąż mniej niż pożądane podejście. evolvingweb.ca/story/...
blue928
.. więc wolałbym pozostać tak drogo, jak to możliwe. Korzystanie z funkcji oszczędzania węzła przy tak dużej ilości danych zapewnia integralność. Jeśli nie mogę tego uruchomić, jestem gotów wykazać się kreatywnością.
blue928

Odpowiedzi:

10

Nigdy nie dostaniesz 30 000 wstawek na minutę za pomocą node_save. Nie ma mowy.

INSERT jest szybki, ponieważ to wszystko, co robi. Węzeł save wykonuje wiele wstawek (tabela główna, tabela wersji, tabela dla każdego pola), czyści wszelkie pamięci podręczne jednostek i odpala haki. Haki są trudną częścią. Jeśli masz wiele modułów contrib (lub nawet takich, które źle się zachowują), które mogą naprawdę zabić wydajność, szczególnie jeśli autor nie wziął pod uwagę przypadku użycia „Oszczędzam mnóstwo węzłów naraz”. Na przykład musiałem dodać to do mojej klasy 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);
  }

Z drugiej strony, jeśli napiszesz niestandardową funkcję składowania, która nie wywołuje żadnych przechwyceń, istnieje wyraźne niebezpieczeństwo otrzymania niespójnych danych w stanie nieoczekiwanym przez system. Nigdy nie zalecałbym tego. Odpal xhprof i zobacz, co się dzieje.

Bojan Zivanovic
źródło
Niektóre z modułów migracji tam, w jaki sposób kończą masowe węzły oszczędzania? Na koniec wszystko sprowadza się do instrukcji INSERT, prawda? W jaki sposób klasa migracji ostatecznie wstawia się ze „źródłowego” do „docelowego”, gdy nie używa się zapisywania węzłów, ale nadal musi zachowywać integralność danych między tabelami?
blue928
Wszystkie napotkane moduły migracji używają save_save.
Alfred Armstrong,
1
@ blue928 Mówi on robi wykorzystania node_save(), ale dodaje trochę kodu do złagodzenia znane problemy, które mogą być spowodowane np Pathauto odbudowy cache menu po każdym węźle oszczędzania
Clive
ah, Ok, rozumiem. Bojan to twój kod dostępny w module lub w Internecie, gdzie mogłem zobaczyć, jak radziłeś sobie z wąskimi gardłami, takimi jak path auto? Dobry pomysł z xhprof. Zajmę się tym.
blue928
5

Przede wszystkim zainstaluj XCache / APC (dla PHP <5.5) i skonfiguruj memcached dla Drupala.

Następnie możesz zoptymalizować konfigurację MySQL pod kątem ciężkich zapytań, używając skryptu mysqltuner dostępnego na stronie: http://mysqltuner.pl

Na przykład

# 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

Inne sugestie:

  • wyłącz moduły, których nie potrzebujesz (np. Devel , podstawowy moduł rejestrowania baz danych itp.),
  • zaktualizuj swój PHP do najnowszej lub wyższej gałęzi,
  • rekompiluj PHP dla architektury 64-bitowej lub wyższej w zależności od procesora,
  • użyj szybszego urządzenia pamięci masowej dla plików db lub całego środowiska LAMP (np. SSD lub system plików oparty na pamięci ),
  • użyj debugera PHP lub profilera, aby znaleźć jakiekolwiek wąskie gardło wydajności (np. XDebug Profiler , DTrace lub NuSphere PhpED PHP Profiler ),
  • uruchom trochę czasochłonnego polecenia drush w narzędziu do profilowania gprof , aby znaleźć również wąskie gardło w wydajności
kenorb
źródło
1
Strojenie MySQL wydaje się mieć dużą różnicę. Poszedłem z około 80 węzłów oszczędza minutę do około 700, postępując zgodnie ze wskazówkami mysqltuner.pl.
John McCollum