2011-09-06 9 views
19

W tej chwili piszę skrypt importu dla bardzo dużego pliku CSV. Problem najczęściej kończy się po pewnym czasie z powodu przekroczenia limitu czasu lub powoduje błąd pamięci.Przetwarzaj bardzo duży plik csv bez limitu czasu i błędu pamięci

My Idea była teraz w stanie przetworzyć plik CSV w krokach "100 linii" i po 100 liniach automatycznie przywołać skrypt. Próbowałem to osiągnąć za pomocą nagłówka (location ...) i przekazać bieżącą linię z get, ale nie wyszło tak jak chcę.

Czy istnieje lepszy sposób na to, czy ktoś ma pomysł, jak pozbyć się błędu pamięci i limitu czasu?

+2

Jak duży jest plik CSV? Musisz zaimportować to do bazy danych? –

+0

sprawdź moją odpowiedź na http://stackoverflow.com/a/22744300/2037323, która obejmuje również niektóre porównania. –

Odpowiedz

44

Użyłem fgetcsv do odczytu csv o rozmiarze 120 MB w sposób strumieniowy (czy to poprawny angielski?). Czyta się wiersz po wierszu, a następnie wstawiam każdą linię do bazy danych. W ten sposób w każdej iteracji przechowywana jest tylko jedna linia w pamięci. Skrypt nadal potrzebował 20 minut. biegać. Może następnym razem wypróbuję Pythona ... Nie próbuj ładować ogromnego pliku CSV do tablicy, która naprawdę pochłonie dużo pamięci.

// WDI_GDF_Data.csv (120.4MB) are the World Bank collection of development indicators: 
// http://data.worldbank.org/data-catalog/world-development-indicators 
if(($handle = fopen('WDI_GDF_Data.csv', 'r')) !== false) 
{ 
    // get the first row, which contains the column-titles (if necessary) 
    $header = fgetcsv($handle); 

    // loop through the file line-by-line 
    while(($data = fgetcsv($handle)) !== false) 
    { 
     // resort/rewrite data and insert into DB here 
     // try to use conditions sparingly here, as those will cause slow-performance 

     // I don't know if this is really necessary, but it couldn't harm; 
     // see also: http://php.net/manual/en/features.gc.php 
     unset($data); 
    } 
    fclose($handle); 
} 
12

Jeśli nie dbasz o to, ile czasu zajmuje i ile potrzebuje pamięci, możesz po prostu zwiększyć wartości tego skryptu. Wystarczy dodać następujące linie na początku skryptu:

ini_set('memory_limit', '512M'); 
ini_set('max_execution_time', '180'); 

Dzięki funkcji memory_get_usage() można dowiedzieć się, ile pamięci skrypt musi znaleźć dobrą wartość dla memory_limit.

Możesz również chcieć rzucić okiem na fgets(), który umożliwia odczytanie pliku wiersz po linii. Nie jestem pewien, czy to zajmuje mniej pamięci, ale naprawdę myślę, że to zadziała. Ale nawet w tym przypadku musisz zwiększyć max_execution_time do wyższej wartości.

+1

Jest to oczywiście tylko dobre podejście, jeśli wiesz, że plik ma zawsze ten sam rozmiar. –

+3

Jeśli wiesz, że nie jest większy niż rozmiar speficic, działa również. – 2ndkauboy

-2

Och. Po prostu ustaw ten skrypt jako CLI, a nie przez głupi interfejs WWW. Nie ma więc wpływu na ograniczenie czasu wykonania.
I nie utrzymuj analizowanych wyników na zawsze, ale natychmiast je zapisz - więc nie będziesz mieć wpływu na limit pamięci.

12

Uważam przesyłając plik i wstawienie przy użyciu MySQL LOAD DATA LOCAL kwerendy szybkie rozwiązanie np

$sql = "LOAD DATA LOCAL INFILE '/path/to/file.csv' 
     REPLACE INTO TABLE table_name FIELDS TERMINATED BY ',' 
     ENCLOSED BY '\"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES"; 
    $result = $mysqli->query($sql); 
+0

Wow, poszedłem od 5 minut +, aby zaimportować 6400 rekordów CSV do mniej niż 5 sekund. To jest fantastyczne! – Iznogood