为什么MySQL InnoDB插入如此缓慢?

我使用大的随机数字作为关键字(从另一个系统进来)。 插入和更新相当小(如在几百万行)表所需要的时间比我认为的要长得多。

我已经提取了一个非常简单的testing来说明。 在testing表中,我试图尽可能简单地做到这一点。 我真正的代码没有这样简单的布局,并有关系和额外的指标等。 但是,更简单的设置会显示相同的性能。

结果如下:

creating the MyISAM table took 0.000 seconds creating 1024000 rows of test data took 1.243 seconds inserting the test data took 6.335 seconds selecting 1023742 rows of test data took 1.435 seconds fetching 1023742 batches of test data took 0.037 seconds dropping the table took 0.089 seconds creating the InnoDB table took 0.276 seconds creating 1024000 rows of test data took 1.165 seconds inserting the test data took 3433.268 seconds selecting 1023748 rows of test data took 4.220 seconds fetching 1023748 batches of test data took 0.037 seconds dropping the table took 0.288 seconds 

将1M行插入MyISAM需要6秒; 进入InnoDB需要3433秒

我究竟做错了什么? 什么是错误configuration? (MySQL是默认的普通Ubuntu安装)

以下是testing代码:

 import sys, time, random import MySQLdb as db # usage: python script db_username db_password database_name db = db.connect(host="127.0.0.1",port=3306,user=sys.argv[1],passwd=sys.argv[2],db=sys.argv[3]).cursor() def test(engine): start = time.time() # fine for this purpose db.execute(""" CREATE TEMPORARY TABLE Testing123 ( k INTEGER PRIMARY KEY NOT NULL, v VARCHAR(255) NOT NULL ) ENGINE=%s;"""%engine) duration = time.time()-start print "creating the %s table took %0.3f seconds"%(engine,duration) start = time.time() # 1 million rows in 100 chunks of 10K data = [[(str(random.getrandbits(48)) if a&1 else int(random.getrandbits(31))) for a in xrange(10*1024*2)] for b in xrange(100)] duration = time.time()-start print "creating %d rows of test data took %0.3f seconds"%(sum(len(rows)/2 for rows in data),duration) sql = "REPLACE INTO Testing123 (k,v) VALUES %s;"%("(%s,%s),"*(10*1024))[:-1] start = time.time() for rows in data: db.execute(sql,rows) duration = time.time()-start print "inserting the test data took %0.3f seconds"%duration # execute the query start = time.time() query = db.execute("SELECT k,v FROM Testing123;") duration = time.time()-start print "selecting %d rows of test data took %0.3f seconds"%(query,duration) # get the rows in chunks of 10K rows = 0 start = time.time() while query: batch = min(query,10*1024) query -= batch rows += len(db.fetchmany(batch)) duration = time.time()-start print "fetching %d batches of test data took %0.3f seconds"%(rows,duration) # drop the table start = time.time() db.execute("DROP TABLE Testing123;") duration = time.time()-start print "dropping the table took %0.3f seconds"%duration test("MyISAM") test("InnoDB") 

InnoDB不能很好地处理“随机”主键。 尝试一个顺序键或自动递增,我相信你会看到更好的performance。 你的“真正的”关键字段仍然可以编入索引,但是对于批量插入,你可能最好在完成插入之后在一次命中中删除和重新创build索引。 将有兴趣看到你的基准!

一些相关的问题

  • 用随机的PRIMARY KEY列的值缓慢插入到InnoDB表中
  • 为什么MySQL InnoDB在大表上插入/更新会有一些索引时变得非常慢?
  • InnoDB插入速度非常慢,速度也很慢

InnoDB具有事务支持,您不使用显式事务,因此innoDB必须在每条语句( “为每个插入执行日志刷新到磁盘” )之后进行提交。

在你的循环之前执行这个命令:

 START TRANSACTION 

而这个循环之后

 COMMIT 

我需要在MyISAM和InnoDB中同时testing一个重载应用程序。 有一个单一的设置,解决了我遇到的速度问题。 尝试设置以下内容:

 innodb_flush_log_at_trx_commit = 2 

通过阅读这里的设置,确保你了解风险。

另请参阅https://dba.stackexchange.com/questions/12611/is-it-safe-to-use-innodb-flush-log-at-trx-commit-2/12612和https:://dba.stackexchange。; COM / A /九千四百零五分之二万九千九百七十四

我在系统上得到了非常不同的结果,但是这不是使用默认值。 你可能是在innodb-log-file-size上的瓶颈,默认是5M。 在innodb-log-file-size = 100M我得到这样的结果(所有的数字都是以秒为单位):

  MyISAM InnoDB create table 0.001 0.276 create 1024000 rows 2.441 2.228 insert test data 13.717 21.577 select 1023751 rows 2.958 2.394 fetch 1023751 batches 0.043 0.038 drop table 0.132 0.305 

增加innodb-log-file-size将会加速几秒钟。 通过设置innodb-flush-log-at-trx-commit=20来降低持久性保证,也会稍微提高插入数量。

InnoDB的默认值实际上很糟糕。 InnoDB非常依赖RAM,如果你调整了设置,你可能会发现更好的结果。 以下是我使用InnoDB优化基础的指南

你的innodb缓冲池大小是多less? 确保你已经把它设置到你的RAM的75%。 InnoDB主键顺序通常插入效果更好。 但是,如果游泳池很大,你应该看到很好的速度。

这是一个老话题,但经常被search。 只要您意识到在最后一秒钟左右丢失承诺交易的风险(如上文@ philip Koshy所述),在大规模更新之前,您可以设置这些全局参数

 innodb-flush-log-at-trx-commit=0 sync_binlog=0 

然后在更新完成后再打开(如果需要的话)。

 innodb-flush-log-at-trx-commit=1 sync_binlog=1 

完全符合ACID标准。

写入/更新性能在closures和打开时都有很大差异。 根据我的经验,上面讨论的其他东西有一些差异,但只有边缘。

另一个影响update/insert东西是全文索引。 在一种情况下,具有全文索引的两个文本字段的表格,插入2mil行需要6个小时,并且全文索引被移除后仅需要10分钟。 更多的索引,更多的时间。 因此,在大量插入/更新之前,除了唯一键和主键之外的search索引可能会被删除。

加快插入的东西:

  • 在大表插入之前,我已经从表中删除了所有的键
  • 然后发现我有一个问题,索引不适合内存。
  • 也发现我有sync_binlog = 0(应该是1)即使binlog不使用。
  • 还发现我没有设置innodb_buffer_pool_instances

mysql 5.7:我现在做了一个testing,我没有注意到随机主键和自动递增之间的任何明显的区别。