3

所以我一直在重写一个旧的 PHP 系统来寻找一些性能提升,但我没有得到任何提升。问题似乎出在我正在对 Mysql 执行的插入操作中。

因此,PHP 对 CSV 文件进行一些处理、进行一些散列处理并在 MySQL 中插入大约 10k 行需要 40 秒(未优化的代码)。

另一方面,现在 Go 去掉了任何处理,同样插入 10k(空)行需要 110 秒。

两个测试都在同一台机器上运行,我使用的是 go-mysql-driver。

现在来看一些 Go 代码:

这是极其简单的代码,与 PHP 相比,这仍然需要将近 2 分钟,而 PHP 的时间不到一半。

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

stmt, _ := db.Prepare("INSERT INTO ticket ( event_id, entry_id, column_headers, column_data, hash, salt ) VALUES ( ?, ?, ?, ?, ?, ? )")

for i := 0; i < 10000; i++{
    //CreateTicket(columns, line, storedEvent)
    StoreTicket(models.Ticket{int64(0), storedEvent.Id, int64(i),
                    "", "", "", "", int64(0), int64(0)}, *stmt)
}

//Extra functions
func StoreTicket(ticket models.Ticket, stmt sql.Stmt){
    stmt.Exec(ticket.EventId, ticket.EntryId, ticket.ColumnHeaders, ticket.ColumnData, ticket.Hash, ticket.Salt)
}

func GetDbCon() (sql.DB) {
    db, _ := sql.Open("mysql", "bla:bla@/bla")

    return *db
}

探查器结果

那么是我的代码,go-mysql-driver 还是这是正常的,PHP 在插入记录方面真的很快吗?

==编辑==

根据要求,我使用 tcpdump 记录了 PHP 和 Go 运行:文件:

比较这两个日志,我很难得出任何结论,两者似乎都在来回发送相同大小的数据包。但是使用 Go(~110) mysql 处理请求的时间几乎是 PHP(~44) 的两倍,Go 似乎在再次发送新请求之前等待稍长一些(尽管差异很小)。

4

2 回答 2

4

这是一个老问题,但仍然——迟到总比没有好;你要请客了:

将所有数据放入bytes.Buffer制表符分隔、换行符终止和未加引号的行中(如果文本引起问题,则必须首先对其进行转义)。NULL 必须编码为\N.

使用http://godoc.org/github.com/go-sql-driver/mysql#RegisterReaderHandler并在“instream”下注册一个返回该缓冲区的函数。接下来,调用 LOAD DATA LOCAL INFILE "Reader::instream" INTO TABLE ...- 这是一种将数据泵入 MySQL 的非常快速的方法(我从标准输入管道传输的文件中测量了大约 19 MB/秒,而从标准输入上传数据时 MySQL 命令行客户端为 18 MB/秒)。

据我所知,该驱动程序是在不需要文件的情况下加载 DATA LOCAL INFILE 的唯一方法。

于 2014-01-27T17:01:14.193 回答
3

我注意到您没有使用事务,如果您使用带有 InnoDB 的 vanilla mysql 5.x,这将是一个巨大的性能拖累,因为它会在每次插入时自动提交。

func GetDbCon() (sql.DB) {
    db, _ := sql.Open("mysql", "bla:bla@/bla")
    return *db
}

func PrepareTx(db *db.DB,qry string) (tx *db.Tx, s *db.Stmt, e error) {
 if tx,e=db.Begin(); e!=nil {
  return
 }

 if s, e = tx.Prepare(qry);e!=nil {
  tx.Close()
 }
 return
}


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

qry := "INSERT INTO ticket ( event_id, entry_id, column_headers, column_data, hash, salt ) VALUES ( ?, ?, ?, ?, ?, ? )"

tx,stmt,e:=PrepareTx(db,qry)
if e!=nil {
 panic(e)
}

defer tx.Rollback()
for i := 0; i < 10000; i++{
 ticket:=models.Ticket{int64(0), storedEvent.Id, int64(i),"", "", "", "", int64(0), int64(0)}
 stmt.Exec(ticket.EventId, ticket.EntryId, ticket.ColumnHeaders, ticket.ColumnData, ticket.Hash, ticket.Salt)

 // To avoid huge transactions
 if i % 1000 == 0 {
  if e:=tx.Commit();e!=nil {
   panic(e)
  } else {
   // can only commit once per transaction
   tx,stmt,e=PrepareTx(db,qry)
   if e!=nil {
    panic(e)
   }
  }
 }
}

// Handle left overs - should also check it isn't already committed
if e:=tx.Commit();e!=nil {
 panic(e)
}
于 2015-01-01T01:02:13.153 回答