在MySQL中插入行会非常慢吗?

在MySQL中插入行会非常慢吗?,mysql,go,Mysql,Go,所以我一直在重写一个旧的PHP系统,寻找一些性能上的改进,但我没有得到任何改进。问题似乎出在我对Mysql的插入上 因此,PHP对CSV文件进行一些处理,进行一些散列,并在MySQL中插入大约10k行,这需要40秒(未优化的代码) 另一方面,现在去掉任何处理,同样插入10k(空)行需要110秒 这两个测试都在同一台机器上运行,我使用的是go mysql驱动程序 现在了解一些Go代码: 这是一个非常简单的代码,与PHP相比,它只需不到一半的时间就可以完成,但仍然需要将近2分钟 db := GetD

所以我一直在重写一个旧的PHP系统,寻找一些性能上的改进,但我没有得到任何改进。问题似乎出在我对Mysql的插入上

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

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

这两个测试都在同一台机器上运行,我使用的是go mysql驱动程序

现在了解一些Go代码:

这是一个非常简单的代码,与PHP相比,它只需不到一半的时间就可以完成,但仍然需要将近2分钟

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
}
db:=GetDbCon()
延迟db.Close()
stmt,u:=db.Prepare(“插入票证(事件id、条目id、列标题、列数据、哈希、salt)值(?,,,,,,,,,?)”)
对于i:=0;i<10000;我++{
//CreateTicket(列、行、storedEvent)
StoreTicket(models.Ticket{int64(0),storedEvent.Id,int64(i),
“”,“”,“”,“”,“”,int64(0),int64(0)},*stmt)
}
//额外功能
func StoreTicket(票证模型.ticket,stmt sql.stmt){
stmt.Exec(ticket.EventId、ticket.EntryId、ticket.columnheader、ticket.ColumnData、ticket.Hash、ticket.Salt)
}
func GetDbCon()(sql.DB){
db,:=sql.Open(“mysql”,“bla:bla@/bla”)
返回*db
}

这是我的代码,go mysql驱动程序,还是这很正常,PHP插入记录的速度真的很快

==编辑==

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


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

这是一个老问题,但仍然-迟做总比不做强;你可以享受一下:

将所有数据以制表符分隔、换行符终止和不带引号的行(如果文本导致问题,则必须首先转义)放入
字节.Buffer
。NULL必须编码为
\N

在“流内”下使用并注册一个返回该缓冲区的函数。接下来,调用
LOAD DATA LOCAL infle“Reader::instream”将数据加载到表中…
-这是将数据注入MySQL的一种非常快速的方法(我从stdin管道传输的文件中测得的Go速度约为19 MB/秒,而从stdin上载数据时,MySQL命令行客户端的速度为18 MB/秒)


据我所知,该驱动程序是无需文件即可加载数据的唯一方法。

我注意到您没有使用事务,如果您使用InnoDB的普通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)
}
func GetDbCon()(sql.DB){
db,:=sql.Open(“mysql”,“bla:bla@/bla”)
返回*db
}
func PrepareTx(db*db.db,qry字符串)(tx*db.tx,s*db.Stmt,e错误){
如果tx,e=db.Begin();e!=nil{
返回
}
如果s,e=tx.Prepare(qry);e!=nil{
关闭
}
返回
}
db:=GetDbCon()
延迟db.Close()
qry:=“插入票证(事件id、条目id、列标题、列数据、散列、salt)值(?,,,,,,,?)”
tx,stmt,e:=PrepareTx(db,qry)
如果e=零{
恐慌(e)
}
延迟发送回滚()
对于i:=0;i<10000;我++{
票证:=模型。票证{int64(0),storedEvent.Id,int64(i),“”,“”,“”,“”,“”,int64(0),int64(0)}
stmt.Exec(ticket.EventId、ticket.EntryId、ticket.columnheader、ticket.ColumnData、ticket.Hash、ticket.Salt)
//避免巨额交易
如果i%1000==0{
如果e:=tx.Commit();e!=nil{
恐慌(e)
}否则{
//每个事务只能提交一次
tx,stmt,e=PrepareTx(db,qry)
如果e!=nil{
恐慌(e)
}
}
}
}
//处理遗留问题-还应检查它是否尚未提交
如果e:=tx.Commit();e=零{
恐慌(e)
}

同一台机器可以,但它是同一个数据库和表吗?你定义了额外的索引吗?胡乱猜测。它们在不同的数据库上运行,命名略有不同(结构相同),但索引完全相同,并且在每次测试之前数据库都会被截断。您使用的是什么Go版本和MySQL驱动程序?go1.1.2 linux/amd和来自githubFinally的最新Go MySQL驱动程序文件,虽然这不是对您问题的回答,但您可能希望尝试Mysql的多行insert语句(
insert into…values(row1)、(row2)…
),并根据基准找到每条语句的最佳行数。如果插入顺序与应用程序无关,还可以使用goroutines并行发出多条语句。