[PHP]Backup dużej bazy danych

0
<?php
$link = mysql_connect('127.0.0.1', 'root', 'haslo') or die('Connection error : ' . mysql_error());

mysql_select_db('nazwa bazy') or die('Could not select database');

$sql = '';
$crlf = "\r\n";

$time = date('Y m d, H:i', time());
$sql .= '#' . $crlf;
$sql .= '# Zrzut bazy danych ' . $crlf;
$sql .= '# Wygenerowano: ' . $time . $crlf;
$sql .= '#' . $crlf . $crlf;

$res = mysql_list_tables('nazwa bazy');
while($row = mysql_fetch_array($res))
{
  $table_name = $row[0];
  $table_fields = array();

  $sql .= '#' . $crlf;
  $sql .= '# Struktura tabeli ' . $table_name . $crlf;
  $sql .= '#' . $crlf;

  $sql .= 'DROP TABLE IF EXISTS ' . $table_name . ';' . $crlf;
  $sql .= 'CREATE TABLE ' . $table_name . ' ( ' . $crlf;

  /* Generujemy strukture tabeli. Najpierw wyciagamy pola */
  $res2 = mysql_query('SHOW FIELDS FROM ' . $table_name);
  while($fields = mysql_fetch_array($res2))
  {
      $sql .= '   ';
      $sql .= $fields['Field'] . ' ' . $fields['Type'];
      if (!empty($fields['Default']))
      {
          $sql .= 'DEFAULT \'' . $fields['Default'] . '\'';
      }

      if ($fields['Null'] != 'Yes')
      {
          $sql .= ' NOT NULL';
      }

      if (!empty($fields['Extra']))
      {
          $sql .= ' ' . $fields['Extra'];
      }

      $sql .= ',' . $crlf;

      $table_fields[] = $fields['Field'];
  }

  $index = '';

  /* Teraz wyciagamy pola typu KEY */
  $res2 = mysql_query('SHOW KEYS FROM ' . $table_name);
  while ($keys = mysql_fetch_assoc($res2))
  {
      $kname = $keys['Key_name'];
      if(($kname != 'PRIMARY') && ($keys['Non_unique'] == 0))
      {
          $kname = 'UNIQUE|' . $kname;
      }

      $index[$kname] = array();
      $index[$kname][] = $keys['Column_name'];
  }

  while(list($n, $columns) = @each($index))
  {
      if ($n == 'PRIMARY')
      {
          $sql .= '   PRIMARY KEY (' . implode($columns, ', ') . ')';
      }
      elseif (substr($n, 0, 6) == 'UNIQUE')
      {
          $sql .= '   UNIQUE ' . substr($n, 7) . ' (' . implode($columns, ', ') . ')';
      }
      else
      {
          $sql .= '   KEY ' . $n . ' (' . implode($columns, ', ') . ')';
      }
      $sql .= $crlf;
  }

  $sql .= ');' . $crlf;
  $sql .= $crlf . $crlf;

  $sql .= '#' . $crlf;
  $sql .= '# Dane z tabeli ' . $table_name . $crlf;
  $sql .= '#' . $crlf;

  /* Mamy juz strukture, wiec teraz czas
     wyciagnac dane z tabeli */
  $d_res = mysql_query('SELECT * FROM ' . $table_name);
  while ($data = mysql_fetch_array($d_res))
  {
      $sql .= 'INSERT INTO ' . $table_name . ' (' . implode(', ', $table_fields) . ') VALUES(';

      $field_count = count($table_fields);
      $f_data = array();
      for ($i = 0; $i < $field_count; $i++)
      {
          $f_data[] .= '\'' . $data[$i] . '\'';
      }
      $sql .= implode(', ', $f_data);
      $sql .= ');' . $crlf;
  }
  $sql .= $crlf . $crlf;
}


$file = fopen('test.sql', 'w');
fwrite($file, $sql);
fclose($file);

?>

Powyższy kod działa bez problemu na mniejszych bazach danych. Jednak moja baza ma ok 150mb i działanie tego kodu konczy się zawieszką serwera lub "nie mozna wyswietlic strony..". Czy da się poprawić powyższy kod tak aby robil backup jakos stopniowo, zeby nie zawiesil serwera?
PS. bardzo podoba mi sie dbloader znany z phpBB by przemo, ktory wczytuje backup bazy stopniowo a na ekranie wyświetla postęp wczytywania w %. Czy da sie zrobić cos podobnego z powyższym skryptem?

0

Serwer pewnie po 15 sec przestaje wykonywać skrypt.(Wiesza sie serwer ?, lepiej, zeby to był tylko twój serwer :) ).
Może spróbować wlaśnie połączyć to z js i jak sam powiedziałeś wczytywać to stopniowo, np: po jednej tabeli, a wynik gdzie skończył skrypt zapisać do pliku.

0

ustaw set_time_limit(0), ignore_user_abort(true) i wywołuj zapytania np wyciągając z bazy danych po 100 rekordów (LIMIT $x,100) i jeśli coś zostało wyciągniete i to coś jest równe 100 elementom to zapisuj do pliku, rób sekunde przerwy (sleep(1);)i ponownie wykonaj zapytanie do danej tabeli. dzieki przerwie unikniesz zwiechy serwera, a do całości mozesz odwoływać sie za pmocą no AJAX'a pokazując info ze proces trwa lun nawet tak jak sam o tym wspomniałeś dając pasek postępu, dodatkowo po zakończeniu całego procesu mozesz spakować całość wyciągnietych danych używając rozszerzeń gzip co zmniejszy rozmiar sciąganego pliku.

0

jakie dane przechowujesz w tej bazie??
jesli sa to dowolne teksty i dam w polu np

ala's ma kota's
To cos mi sie zdaje ze sie skrypt wylozy (tzn przy wrzucaniu)
Nie testowalem, ale tak na pierwszy rzut oka :]

Nie wiem czy to Twoj serewr czy nie, ale jesli masz dostep do shella, to lepiej to przez niego zrobic niz przez strone, bo nie zawsze set_time_limit(0), ignore_user_abort(true) spelniaja swoje zadanie
(na "prawdziwych" serwach jest to wylaczone i nie mozna zmienic czasu dzialania skryptu, bo mozna w ten sposob zamulic caly serwer)

Mowisz ze baza ma 150MB, wiec dump tej bazy bedzie mial jeszcze wiecej.
Nie mozesz zapisywac tego do jednej zmiennej i pozniej calosc do pliku!!
Jesli dump bedzie mial w tym przypadku 200MB, to php musi zarezerwowac minimum 200MB RAMu :|
Oczywiscie nie jest powiedziame ze TYLKO tyle :>
Po kazdej porcji danych (jak radzil nul, po 100, lub po wiekszej ilosci) zapisuj ja do pliku.

Jesli chcesz pakowac dane, mozesz sprobowac otworzyc nie plik, tylko potok do gz. Jesli oczywiscie admin serwerka nie zabronil tego ;)

0

ja bym nawet nie pakowal tego do jednego pliku, tylko:

  • najpierw zrzut sruktury do jednego pliku, może być nawet bez kompresji
  • zrzucanie kolejnych porcji do oddzielnych archiwów (zrób to tak, żeby po przerwanu można było łatwo wznowić proces - np. numerując paczki (1 dla rekordow 1-1000, 2 dla 1001-2000 itp)
  • potem sciagasz wszystko na HDD i tak samo paczkami wrzucasz na nowy serwer
0

@tomkiewicz: tak na chłopski rozum jeśli baza ma 150 mb to nawet paczki po 1000 wpisów zajmujące mniej miejsca będa prawdopodobnie wymagały gro pracy przy sciąganiu ich i uploadowaniu bo bedzie ich raczej bardzo dużo, dlatego lepiej jest je jednak spakować do jednego pliku - wygodniejsze

@desperat: zarówno ignore_user_abort jak i set_time_limit są czasem wyłączane, jednak jet to raczej rzadkość - przynajmniej na polskich serwerach - jak dotąd t spotkałem sie tylko z jednym serwerem polskim który te funkcje wyłączał i moim zdaniem jest to złe rozwiązanie i taki hosting bym odradzał bo płacimy przeciez za funkcjonalność a nie za ograniczenia. A na temat darmowych hostingów na których mogą być takowe ograniczenia sie wypowiadał nawet nie bede bo w ich przypadku to chyba oczywiste

0

o ile masz bardzo dobry serwer, to moze prostsze :P.

Poza tym co za problem napisac np. program w delphi, ktory bedzie sam wywolywal skrypt php z odpowiednimi parametrami (ktory by dumpowal i dostarczal do pobrania kolejne paczki), a potem wszystkie paczki kolejno ladnie zuploaduje przez inny skrypt PHP, ktory je zaladuje do bazy?

0

a no problem dla tych którzy delphi nie znają - poza tym jest to dodatkowe komplikowanie sprawy. bo byćmoze zajdzie potrzeba wykonania skryptu w miejscu w którym danego programu nie ma lbu nie ma możliwości zainstalowania go (linux, palmtom, kom itd)

0
desperat napisał(a)

ale jesli masz dostep do shella, to lepiej to przez niego zrobic niz przez strone

mam dostęp :-) Jak to zrobić przez shella? i czy przez shell zachowają się polskie znaki?

0

no jak masz shella to praktycznie tematu nie ma :P

#!/bin/bash
##############################################################
user="user";                        # usytkownik do bazy
pass="haslo";                     # haslo do bazy
host="localhost";               # host
baza="baza";                      # nazwa bazy do zgrania
to="moj_backup.sql.gz";    # nazwa plikow
##############################################################
mysqldump -u $user --password=$pass -h $host --opt $baza | gzip -fc > $to

Odpalasz taki pliczek i po klopocie ;]

0

Dopisze sie tutaj zeby nie robic wiecej smietnika.

Jezeli nie mam dostepu do shella ale moge laczyc sie do bazy przez klienta np mysql-connector-odbc to moge tam wykonac dump bazy? Gdzie sie on zapisze? Da rade to zgrac Od razu na twardy dysk z pominieciem serwera?

0

Lopezik, mozesz sie laczyc przez "mysql-connector-odbc", ale jak chcesz sie laczyc?? Masz jakis program, czy sam ten program piszesz??

0

Ehh a ja mam teraz problem z polskim kodowaniem... Bo jak zrobie kopie przez shella to zamiast polskich znaków są znaki zapytania albo jakies krzaczki. Admin wspominał mi cos o parametrze "--set-charset=" no ale jakie kodowanie dać? próbowałem iso-8859-2, utf8, latin2 no ale to nic nie daje :(

1 użytkowników online, w tym zalogowanych: 0, gości: 1