Site icon Kamil Wyremski

Import dużej bazy MySQL przez PHP

Zdjęcie monitora z kodem

W tym wpisie pokażę, jak wczytać ogromny plik .sql do bazy MySQL przy pomocy czystego PHP bez limitów czasu wykonania czy pamięci. To szczególnie przydatne, gdy masz pllik z bazą danych który zajmuje (nawet po spakowaniu do ZIP) więcej niż możesz załadować w phpMyAdmin.

Przygotowanie środowiska PHP

Na początku skryptu zdejmujemy wszystkie standardowe ograniczenia PHP, które przy dużych plikach SQL niemal zawsze powodują problemy:

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
set_time_limit(0);
ini_set('memory_limit', '-1');
ini_set('max_execution_time', 0);
ignore_user_abort(true);

Co tu się dzieje:

Konfiguracja połączenia z bazą danych

W tym miejscu wskazujemy plik SQL oraz dane dostępowe do bazy.

Ważne:

$sqlFile = __DIR__ . '/baza.sql'; 
$dsn = "mysql:host=localhost;dbname=DBNAME;charset=utf8";
$user = "DBUSER";
$pass = "PASSWORD";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_LOCAL_INFILE => true
]);

Wyłączenie kluczy obcych przez PDO

$pdo->exec("SET FOREIGN_KEY_CHECKS = 0;");

Dzięki temu:

Po zakończeniu importu klucze zostaną włączone ponownie.

Czytanie pliku SQL linia po linii

Kluczowy fragment skryptu to liniowe czytanie pliku, zamiast ładowania całości do pamięci:

$handle = fopen($sqlFile, "r");
$query = "";

Plik jest czytany przy pomocy fgets(), czyli jedna linia na raz, co pozwala obsłużyć nawet bardzo duże dumpy.

Pomijanie komentarzy i zbędnych instrukcji

Podczas importu ignorujemy:

if (trim($line) === '' || preg_match('/^(--|#)/', trim($line))) {
continue;
}

if (preg_match('/^\s*CREATE DATABASE/i', $line)) {
continue;
}

if (preg_match('/^\s*USE\s+/i', $line)) {
continue;
}

Składanie i wykonywanie zapytań SQL

Zapytania SQL często zajmują wiele linii. Dlatego linie są sklejane aż do średnika:

$query .= $line;

if (substr(trim($line), -1) === ';') {
$pdo->exec($query);
$query = "";
}

To rozwiązanie:

Do tego dodajemy obsługę błędów i wyświetlanie postępu.

catch (Exception $e) {
echo "<b>Błąd w linii $lineNumber:</b> " . $e->getMessage();
echo "<pre>$query</pre>";
exit;
}
if ($executed % 100 === 0) {
echo "Wykonano $executed zapytań...<br>";
ob_flush();
flush();
}

Dzięki ob_flush() i flush():

Zakończenie importu

fclose($handle);
$pdo->exec("SET FOREIGN_KEY_CHECKS = 1;");
echo "Import zakończony. Wykonano $executed zapytań.";

Podsumowanie

Ten skrypt przydał mi się już kilka razy w praktyce, gdy standardowy import przez phpMyAdmin nie dawał rady — szczególnie przy dużych dumpach baz danych, które przekraczały limity rozmiaru lub czasu wykonania. Dzięki liniowemu czytaniu pliku i zdjęciu ograniczeń PHP import przebiegał stabilnie nawet przy bardzo dużych plikach .sql.

Jeśli masz podobny problem i potrzebujesz prostego, ale skutecznego rozwiązania, ten skrypt powinien spełnić swoje zadanie.

Repozytorium na GitHubie: https://github.com/kamilwyremski/php-mysql-big-importer/

Exit mobile version