为什么 MySQL InnoDB insert 这么慢?

问题描述 投票:0回答:9

我使用大随机数作为密钥(来自另一个系统)。在相当小的(如几百万行)表上插入和更新所花费的时间比我认为合理的要长得多。

我提炼了一个非常简单的测试来说明。在测试表中,我试图让它尽可能简单;我的真实代码没有这么简单的布局,并且有关系和附加索引等。但是,更简单的设置显示出相同的性能。

结果如下:

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

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

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

测试代码如下:

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")
mysql performance innodb
9个回答
70
投票

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

在循环之前执行此命令:

START TRANSACTION

循环后的这个

COMMIT

48
投票

InnoDB 不能很好地处理“随机”主键。尝试顺序键或自动递增,我相信你会看到更好的性能。您的“真实”键字段仍然可以被索引,但是对于批量插入,您最好在插入完成后一次删除并重新创建该索引。有兴趣看到你的基准!

一些相关问题


23
投票

我需要同时在 MyISAM 和 InnoDB 中测试大量插入的应用程序。有一个设置可以解决我遇到的速度问题。尝试设置以下内容:

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/12612https:// dba.stackexchange.com/a/29974/9405


6
投票

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


6
投票

我在我的系统上得到非常不同的结果,但这不是使用默认值。您可能在 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=2
0
来降低耐久性保证也会在一定程度上改善插入数量。


2
投票

你的 innodb 缓冲池大小是多少?确保已将其设置为 RAM 的 75%。 通常在 InnoDB 的主键顺序中插入会更好。但是如果池大小很大,你应该会看到不错的速度。


2
投票

解决方案

  1. 创建与当前主键相同的新唯一键
  2. 添加新列
    id
    是无符号整数,auto_increment
  3. 在新的
    id
    列上创建主键

Bam,立即 10 倍以上的插入改进。


1
投票

加速插入的东西:

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

1
投票

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

innodb_flush_log_at_trx_commit=0
sync_binlog=0

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

innodb_flush_log_at_trx_commit=1
sync_binlog=1

完全符合 ACID 标准。

关闭和打开这两者时,写入/更新性能会有很大差异。根据我的经验,上面讨论的其他内容会有所不同,但只是微不足道的。

另一个对

update/insert
影响很大的是全文索引。在一个案例中,一个包含两个具有全文索引的文本字段的表,插入 200 万行需要 6 个小时,而在删除全文索引后同样只需要 10 分钟。更多索引,更多时间。因此,在大量插入/更新之前,可能会删除除唯一键和主键之外的搜索索引。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.