在Go中插入行非常慢?

时间:2013-10-16 08:41:52

标签: mysql go

所以我一直在重写一个旧的PHP系统去寻找一些性能提升,但我没有得到任何。而问题似乎是在我正在进入Mysql的插件中。

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

现在另一方面剥离任何处理,只需插入10k(空)行需要110秒。

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

现在有些Go代码:

这是非常愚蠢的代码,这仍然需要将近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
}

Profiler result

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

== EDIT ==

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

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

2 个答案:

答案 0 :(得分:3)

这是一个古老的问题,但仍然 - 迟到总比没有好;你正在享受一种享受:

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

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

据我所知,这个驱动程序是加载DATA LOCAL INFILE而不需要文件的唯一方法。

答案 1 :(得分:1)

我注意到你没有使用一个事务,如果你使用一个带有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)
}