IMDB datasets Import

Was man falsch und besser machen kann

Kommentieren Mar 01 2020 .txt, .json, .md

IMDB bietet ihre Daten zur nicht kommerziellen Nutzung zu Verfügung.

Für die Nutzung der Daten plane ich den Import in eine MySQL Datenbank. Da ich auf die schnelle nichts einfaches und praktikables gefunden habe, schreibe ich den Import selbst.

Dabei bin ich über ein Szenario gestolpert an dem man zeigen kann so ist gut und so ist es besser.

Folgend ein Abschnitt meines ersten Entwurfs, aus der tsv Datei die Daten in eine Tabelle einzulesen: (Es ist nicht der komplette Code dargestellt, nur der wichtige Teil)

if (($handle = fopen($file, "r")) !== FALSE) {
    // skip first line as it should be column names
    fgetcsv($handle, 1000, "\t");
    $linesInFile = $this->_linesInFile($file);

    try {
        $queryStmt = $this->_DB->prepare("INSERT IGNORE INTO `".$this->_db_table_name."`
        VALUES(?,?,?,?,?,?,?,?)");
    } catch (Exception $e) {
        echo "Error while preparing sql statement.".$e->getMessage()."\n";
        return false;
    }
    $i=0;
    while (($data = fgetcsv($handle, 1000, "\t")) !== FALSE) {
        try {
            $queryStmt->bind_param('sisssssi',
                $data[0],
                $data[1],
                $data[2],
                $data[3],
                $data[4],
                $data[5],
                $data[6],
                $data[7]
            );
            $queryStmt->execute();
            $i++;
            echo "Inserting: $i/$linesInFile\r";
        } catch (Exception $e) {
            echo "Failed to execute the query: ".$e->getMessage()."\n";
            return false;
        }
    }
    $queryStmt->close();
    fclose($handle);
    echo "Import complete. Inserted $i rows\n";
}

Das Ergebnis dieser Art von Abarbeitung nach 10k Zeilen:

Inserting: 10210/20834144

real  0m31.936s
user  0m0.720s
sys   0m0.148s

Das dauert ja ewig. Denn in MB gemessen sind es nicht viele Daten. Aber bei ca. 20Mio. Einträge die dann in der Tabell vorhanden sind, merkt man dies schon.

Hier nur der Umbau an dem ich bemerkte das es schneller geht.

if (($handle = fopen($file, "r")) !== FALSE) {
    // skip first line as it should be column names
    fgetcsv($handle, 1000, "\t");
    $linesInFile = $this->_linesInFile($file);
    $queryStrStart = "INSERT IGNORE INTO `".$this->_db_table_name."` VALUES ";
    $queryStr = '';
    $total=0;
    while (($data = fgetcsv($handle, 1000, "\t")) !== FALSE) {
        $queryStr .= "(
            '".$this->_DB->real_escape_string($data[0])."',
            '".$this->_DB->real_escape_string($data[1])."',
            '".$this->_DB->real_escape_string($data[2])."',
            '".$this->_DB->real_escape_string($data[3])."',
            '".$this->_DB->real_escape_string($data[4])."',
            '".$this->_DB->real_escape_string($data[5])."',
            '".$this->_DB->real_escape_string($data[6])."',
            '".$this->_DB->real_escape_string($data[7])."'
        ),";

        $total++;

        if(isset($queryStr[1000000])) {
            $this->_DB->query($queryStrStart.trim($queryStr,","));
            $queryStr = '';
        }

        echo "Inserting: $total/$linesInFile\r";
    }
    fclose($handle);
    echo "Import complete. Inserted $total rows\n";

Dabei wird die Möglichkeit verwendet das ein INSERT so aussehen kann INSERT ... VALUES (),(),()... Also mehr Daten auf einmal. Damit der einzel Query nicht zu groß wird frage ich auf eine maximale Länge von 1000000 Zeichen ab. Dies kann je nach System aber abweichen.

Hier das Ergebnis nach ca 600k Zeilen:

Inserting: 595246/20834144

real  0m6.952s
user  0m3.038s
sys   0m0.822s