2015-09-14 7 views
12

Robiłem bulk inserts w RealTime Index przy użyciu PHP i wyłączając AUTOCOMIT, np.Sfinks Jak utrzymywać połączenie aktywne, nawet jeśli nie ma aktywności przez dłuższy czas?

// sphinx connection 
$sphinxql = mysqli_connect($sphinxql_host.':'.$sphinxql_port,'',''); 

//do some other time consuming work 

//sphinx start transaction 
mysqli_begin_transaction($sphinxql); 

//do 50k updates or inserts 

// Commit transaction 
mysqli_commit($sphinxql); 

i przechowywane skrypt działa na noc, rano widziałem

PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 
212334 bytes) in 

więc gdy sprawdziłem plik nohup.out bliżej, zauważyłem, te linie, wykorzystanie

PHP Warning: mysqli_query(): MySQL server has gone away in /home/script.php on line 502 
Warning: mysqli_query(): MySQL server has gone away in /home/script.php on line 502 

pamięć zanim te linie były normalne, ale użycie pamięci po tych liniach zaczęło wzrastać, i uderzyło w phpmem_limit i dało PHP Fatal error i zmarł.

in script.php , line 502 is 

mysqli_query($sphinxql,$update_query_sphinx); 

Więc zgaduję, że serwer sfinks został zamknięty/zmarł po kilku godzinach/minutach bezczynności.

Próbowałem ustawienie w sphinx.conf

client_timeout = 3600 

wznowił searchd przez

systemctl restart searchd 

i nadal jestem stoi sam problem.

Jak więc nie mogę sprawić, że serwer sfinksów umrze na mnie, gdy żadna aktywność nie będzie obecna przez dłuższy czas?


więcej informacji dodanej -

otrzymuję dane z mysql w 50k kawałki na raz i robi pętli while sprowadzić każdy wiersz i aktualizować go w sfinks indeksu RT. tak jak to

//6mil rows update in mysql, so it takes around 18-20 minutes to complete this then comes this following part. 

$subset_count = 50000 ; 

$total_count_query = "SELECT COUNT(*) as total_count FROM content WHERE enabled = '1'" ; 
$total_count = mysqli_query ($conn,$total_count_query); 
$total_count = mysqli_fetch_assoc($total_count); 
$total_count = $total_count['total_count']; 

$current_count = 0; 

while ($current_count <= $total_count){ 

$get_mysql_data_query = "SELECT record_num, views , comments, votes FROM content WHERE enabled = 1 ORDER BY record_num ASC LIMIT $current_count , $subset_count "; 

//sphinx start transaction 
mysqli_begin_transaction($sphinxql); 

if ($result = mysqli_query($conn, $get_mysql_data_query)) { 

    /* fetch associative array */ 
    while ($row = mysqli_fetch_assoc($result)) { 

    //sphinx escape whole array 
    $escaped_sphinx = mysqli_real_escape_array($sphinxql,$row); 

    //update data in sphinx index 
    $update_query_sphinx = "UPDATE $sphinx_index 
    SET 
     views  = ".$escaped_sphinx['views']." , 
     comments = ".$escaped_sphinx['comments']." , 
     votes = ".$escaped_sphinx['votes']." 
    WHERE 
     id   = ".$escaped_sphinx['record_num']." "; 

    mysqli_query ($sphinxql,$update_query_sphinx); 

    } 

    /* free result set */ 
    mysqli_free_result($result); 
} 
// Commit transaction 
mysqli_commit($sphinxql); 

$current_count = $current_count + $subset_count ; 
} 
+0

Można aktualizować/wstawiać w mniejsze porcje. Powiedz 1k. Lub zaktualizuj/wstaw w jednym zapytaniu (jeśli jest to możliwe w twoim przypadku). – Andrew

+0

@Andrew Mam około 6 milionów rekordów do codziennej aktualizacji, więc 1k rekordów na cykl jest czasochłonny, więc szukam czegoś solidnego rozwiązania, nawet czegoś w rodzaju funkcji przywracania połączenia, która utrzyma połączenie aktywne w każdej pętli. – AMB

+0

Czy możesz pisać zapytania? Mam przeczucie, że może być bardziej związany z tym niż cokolwiek innego. – Andrew

Odpowiedz

1

Trzeba ponownie lub uruchom sesję DB tuż przed mysqli_begin_transaction($sphinxql)

czegoś takiego.

<?php 

//reconnect to spinx if it is disconnected due to timeout or whatever , or force reconnect 
function sphinxReconnect($force = false) { 
    global $sphinxql_host; 
    global $sphinxql_port; 
    global $sphinxql; 
    if($force){ 
     mysqli_close($sphinxql); 
     $sphinxql = @mysqli_connect($sphinxql_host.':'.$sphinxql_port,'','') or die('ERROR'); 
    }else{ 
     if(!mysqli_ping($sphinxql)){ 
      mysqli_close($sphinxql); 
      $sphinxql = @mysqli_connect($sphinxql_host.':'.$sphinxql_port,'','') or die('ERROR'); 
     } 
    } 
} 



//10mil+ rows update in mysql, so it takes around 18-20 minutes to complete this then comes this following part. 

//reconnect to sphinx 
sphinxReconnect(true); 

//sphinx start transaction 
mysqli_begin_transaction($sphinxql); 

//do your otherstuff 

// Commit transaction 
mysqli_commit($sphinxql); 
1

Jest tu kilka problemów związanych z uruchamianiem dużych procesów.

  1. MySQL server has gone away - To zwykle oznacza, że ​​MySQL wygasła, ale może również oznaczać, że proces MySQL rozbił się z powodu wyczerpania pamięci. W skrócie oznacza to, że MySQL przestał odpowiadać i nie powiedział klientowi, dlaczego (tj. Nie ma bezpośredniego błędu zapytania). Widząc, jak powiedziałeś, że uruchamiasz aktualizacje 50k w pojedynczej transakcji, prawdopodobnie w MySQL zabrakło pamięci.
  2. Allowed memory size of 134217728 bytes exhausted - oznacza, że ​​zabrakło pamięci w postaci PHP. To również prowadzi do wiary w to, że MySQL zabrakło pamięci.

Co z tym zrobić?

Początkowe rozwiązanie polegające na zatrzymaniu luki polega na zwiększeniu limitów pamięci dla PHP i MySQL. To naprawdę nie rozwiązuje głównej przyczyny, i zależnie od tego, ile masz kontroli (i wiedzy, którą posiadasz) na stosie wdrażania, może nie być to możliwe.

Kilka osób wspomniało, że proces porcjowania może pomóc.Trudno powiedzieć najlepszy sposób, aby to zrobić, nie znając faktycznego problemu, nad którym pracujesz. Jeśli możesz obliczyć, powiedzmy, 10000 lub 20000 rekordów z 50000 instancji z serii, które mogą rozwiązać twoje problemy. Jeśli to zajmie zbyt dużo czasu w jednym procesie, możesz także przyjrzeć się używaniu kolejki komunikatów (RabbitMQ jest dobrym rozwiązaniem, z którego korzystałem w wielu projektach), aby można było uruchamiać wiele procesów w tym samym czasie: czas przetwarzanie mniejszych partii.

Jeśli robisz coś, co wymaga wiedzy o 6 milionach + rekordach, aby wykonać obliczenia, możesz potencjalnie podzielić proces na kilka mniejszych kroków, buforować wykonaną pracę "do tej pory" (jako takie), a następnie wybierz następny krok w następnym procesie. To, jak to zrobić, jest trudne (ponownie coś takiego jak RabbitMQ może uprościć to poprzez wystrzelenie zdarzenia po zakończeniu każdego procesu, aby następny mógł zostać uruchomiony).

Więc, krótko mówiąc, nie są najlepsze dwie opcje:

  1. Rzucać więcej zasobów pamięci/w problemu wszędzie, że można
  2. Przerwa problemu na mniejsze, samodzielne kawałki.
Powiązane problemy