批量MySQL插入比PHP慢2倍

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了批量MySQL插入比PHP慢2倍相关的知识,希望对你有一定的参考价值。

我一直在测试Go,希望将它用于新网站,并希望确保它比php更快或更快。所以我在Go和PHP中运行了一个基本测试来进行批量插入,因为我需要批量插入。

我的测试使用事务,预处理语句,相同的机器,完全相同的表定义,没有索引但PK,以及函数中的相同逻辑。

结果:

  • PHP中的100k插入(mysqli)为4.42秒
  • Go中的100k插入(Go-MySQL-Driver)为9.2秒

我正在使用的go mysql驱动程序是最流行的'Go-MySQL-Driver'在这里找到:https://github.com/go-sql-driver/mysql

我想知道是否有人可以告诉我,我的代码是否设置不正确或者这是怎么回事。

这些函数为一些行变量添加了一些可变性,因此每行都不相同。

去功能:

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>
", elapsed)
}

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
    }        
}
答案

在我的测试中,为了找到我想用于我的新网站的语言,我尝试了php,golang和java。我对任何一种语言都没有多少经验,所以我在这里所说的任何内容都可能会被某人更正。

我的主要测试是批量插入到mysql数据库中,因为我将需要它用于应用程序。

我想离开php,因为它是一种非编译的旧脚本语言,它在许多方面比golang和java慢。这对许多事情来说也是一种尴尬的语法。然而,对于大型“交易”而言,php mysqli实际上比golang快2倍,除非你笨拙地产生许多例程以分割工作。

在我的测试和研究过程中,我发现了一些事情。

PHP mysqli“transactions”api可能正在使用某种批处理操作来完成“事务”,因为mysqli没有单独的批处理函数,并且事务比单个插入更快。但在大多数其他语言中,事务不会自动批处理所有内容,甚至不会增加执行时间。它们只是一种在出现问题时回滚事务中所有内容的机制。使用批次增加其他语言的执行时间的原因。

但是现在使用mysql接口的一个大问题似乎并没有真正支持批处理操作。我得到的最接近的是jerry rig one并按照这篇文章(golang - mysql Insert multiple data at once?)的指示进行自己的批量操作。这样做我能够将执行时间从9.2s提高到3.9s,而不会产生其他的例行程序。但由于没有真正的支持,批处理操作只返回批处理的第一个操作的单个结果集。这对我来说毫无价值,因为我需要为插入的行返回autoinc ID。这个设置还存在其他问题,我不会进入。

所以最后我在tomcat服务器上尝试了java。 Tomcat / java安装比go更复杂,但java中的编程非常简单和自然。 JDBC是一个出色的驱动程序,完全支持使用预准备语句进行简单的批处理操作。它仅在1秒内完成了100k插入。这是我书中明显的赢家。另外java语法比golang IMO更自然。

以上是关于批量MySQL插入比PHP慢2倍的主要内容,如果未能解决你的问题,请参考以下文章

实体框架的大批量更新比我自己批量更新慢得多

MySQL View 比 Select 慢 20 倍

为什么 Dapper 的批量插入比我预期的要慢很多?

为啥这个 numba 代码比 numpy 代码慢 6 倍?

一个简单的 MySQL 插入/更新怎么会比外部 Web 请求慢?

Tensorflow 2.0 Keras 的训练速度比 2.0 Estimator 慢 4 倍