Testowałem Go w nadziei, że użyję go w nowej witrynie i chciałem się upewnić, że jest tak szybki lub szybszy niż PHP. Przeprowadziłem więc podstawowy test robiący wstawianie zbiorcze w Go i PHP, ponieważ będę potrzebował wstawek zbiorczych.

W moich testach używałem transakcji, przygotowanych instrukcji, tej samej maszyny, dokładnie tej samej definicji tabeli, żadnego indeksu poza PK i tej samej logiki w funkcji.

Wyniki:

  • 100 tys. wstawek w PHP (mysqli) to 4,42 sekundy
  • 100 000 wstawek w Go (Go-MySQL-Driver) trwało 9,2 sekundy

Sterownik go mysql, którego używam, to najpopularniejszy sterownik „Go-MySQL-Driver”, który można znaleźć tutaj: https://github.com/go-sql-driver/mysql

Zastanawiam się, czy ktoś może mi powiedzieć, czy mój kod w go nie jest poprawnie skonfigurowany lub czy tak właśnie jest w go.

Funkcje dodają trochę zmienności do kilku zmiennych wierszy, tak aby każdy wiersz nie był taki sam.

Idź funkcja:

func fill_table(w http.ResponseWriter, r *http.Request, result_string *string, num_entries_to_add int) {
    defer recover_show_error(result_string)

    db := getDBConn()
    defer db.Close()

    var int_a int = 9  
    var int_b int = 4  

    var int_01 int = 1           
    var int_02 int = 1451628000 // Date Entered  (2016-1-1, 1am)
    var int_03 int = 11         
    var int_04 int = 0
    var int_05 int = 0

    var float_01 float32 = 90.0 // Value
    var float_02 float32 = 0
    var float_03 float32 = 0

    var text_01 string = "" 
    var text_02 string = ""
    var text_03 string = ""

    start_time := time.Now()

    tx, err := db.Begin()
    if err != nil {
        panic(err)
    }

    stmt, err := tx.Prepare("INSERT INTO " + TABLE_NAME +
        "(`int_a`,`int_b`,`int_01`,`int_02`,`int_03`,`int_04`,`int_05`,`float_01`,`float_02`,`float_03`,`text_01`,`text_02`,`text_03`) " +
        "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")

    if err != nil {
        panic(err)
    }
    defer stmt.Close()

    var flip int = 0
    for i := 0; i < num_entries_to_add; i++ {

        flip = ((int)(i / 500)) % 2
        if flip == 0 {
            float_01 += .1 // add to Value
        } else {
            float_01 -= .1 // sub from Value
        }

        int_02 += 1 // add a second to date.

        _, err = stmt.Exec(int_a, int_b, int_01, int_02, int_03, int_04, int_05, float_01, float_02, float_03, text_01, text_02, text_03)
        if err != nil {
            panic(err)
        }
    }

    err = tx.Commit()
    if err != nil {
        panic(err)
    }

    elapsed := time.Since(start_time)
    *result_string += fmt.Sprintf("Fill Table Time = %s</br>\n", elapsed)
}

Funkcja PHP:

function FillTable($num_entries_to_add){ 

    $mysqli= new mysqli("localhost", $GLOBALS['db_username'], $GLOBALS['db_userpass'], $GLOBALS['database_name']);
    if ($mysqli->connect_errno == 0) {

        $int_a = 9; 
        $int_b = 4; 

        $int_01 = 1; 
        $int_02 = 1451628000; // Date Entered  (2016-1-1, 1am)
        $int_03 = 11; 
        $int_04 = 0;         
        $int_05 = 0;         

        $float_01 = 90.0; // Value
        $float_02 = 0;
        $float_03 = 0;

        $text_01 = ""; 
        $text_02 = "";
        $text_03 = "";


        $mysqli->autocommit(FALSE);     // This Starts Transaction mode. It will end when you use mysqli->commit();         

        $sql = "INSERT INTO " . $GLOBALS['table_name'] . 
            "(`int_a`,`int_b`,`int_01`,`int_02`,`int_03`,`int_04`,`int_05`,`float_01`,`float_02`,`float_03`,`text_01`,`text_02`,`text_03`) " . 
            "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)";

        $start_time = microtime(true);

        if($stmt = $mysqli->prepare($sql)) {
            $stmt->bind_param('iiiiiiidddsss', $int_a, $int_b, $int_01, $int_02, $int_03, $int_04, $int_05, $float_01, $float_02, $float_03, $text_01, $text_02, $text_03);

            $flip = 0;
            for ($i = 1; $i <= $num_entries_to_add; $i++) {
                $flip = ((int)($i / 500)) % 2;
                if ($flip == 0) {
                    $float_01 += .1;    // add Value
                }
                else {
                    $float_01 -= .1;    // sub Value
                }

                $int_02 += 1;       // add a second to date.

                $stmt->execute(); //Executes a prepared Update 
            }

            $mysqli->commit();  // Transaction mode ends now    
            $stmt->close();  //Close statement
        }

        $execute_time = microtime(true) - $start_time;
        echo $GLOBALS['html_newline'] . $GLOBALS['html_newline'] . 
            'FillDataEntryTable Speed: '.$execute_time.' sec' . $GLOBALS['html_newline'] . $GLOBALS['html_newline'];

        $thread_id = $mysqli->thread_id;    // Get MySQL thread ID
        $mysqli->kill($thread_id);          // Kill MySQL Server connection
        $mysqli->close();                   // Close MySQL Server connection
    }        
}
2
user223304 13 listopad 2018, 19:54

1 odpowiedź

Najlepsza odpowiedź

W moich testach, aby znaleźć język, którego chcę użyć na mojej nowej stronie, eksperymentowałem z php, golang i java. Nie mam dużego doświadczenia z żadnym z języków, więc wszystko, co tutaj powiem, może zostać przez kogoś poprawione w przyszłości.

Moim głównym testem były wstawki wsadowe do bazy danych mysql, ponieważ będę go potrzebować do aplikacji.

Chciałem odejść od php, ponieważ jest to nieskompilowany stary język skryptowy, który jest w wielu sprawach wolniejszy niż golang i java. Jest to również niezręczna składnia wielu rzeczy. Jednak php mysqli jest w rzeczywistości 2x szybsze niż golang w przypadku dużych "transakcji", chyba że niezręcznie stworzysz wiele procedur go, aby podzielić pracę.

Podczas moich testów i badań dowiedziałem się kilku rzeczy.

PHP mysqli "transakcje" API prawdopodobnie używają pewnego rodzaju operacji wsadowych, aby wykonać "transakcję", ponieważ mysqli nie ma oddzielnych funkcji wsadowych, a transakcje są szybsze niż pojedyncze wstawianie. Jednak w większości innych języków transakcje nie obejmują wszystkiego automatycznie i nawet nie wydłużają czasu realizacji. Są tylko mechanizmem cofania wszystkiego w transakcji, jeśli coś pójdzie nie tak. To, co wydłuża czas wykonania w innych językach, to używanie partii.

Ale obecnie jednym z największych problemów z interfejsem go mysql jest brak obsługi operacji wsadowych. Najbliżej mi było jerry rig i wykonanie własnej operacji wsadowej, jak wskazano w tym poście (golang - mysql Wstawić wiele danych jednocześnie?). Dzięki temu udało mi się uzyskać czas wykonania z 9,2 s do 3,9 s bez odradzania innych procedur go. Ale ponieważ nie ma dla tego rzeczywistego wsparcia, operacja wsadowa zwraca tylko jeden zestaw wyników dla pierwszej operacji wsadowej. Jest to dla mnie bezwartościowe, ponieważ muszę zwrócić identyfikatory autoinc dla moich wstawionych wierszy. Były też inne problemy z tą konfiguracją, o które nie będę się zagłębiał.

W końcu spróbowałem javy na serwerze tomcat. Instalacja Tomcat/java jest nieco bardziej skomplikowana niż go, ale programowanie w javie było o wiele łatwiejsze i naturalne. JDBC to doskonały sterownik z pełną obsługą prostych operacji wsadowych z przygotowanymi instrukcjami. Zrobił 100 tys. wstawek w zaledwie 1 sek. To wyraźny zwycięzca w mojej książce. Dodatkowo składnia java jest znacznie bardziej naturalna niż golang IMO.

0
user223304 18 listopad 2018, 05:14