Mysql Innodb插入速度非常慢:空表中1K条目的插入时间为65秒

Mysql Innodb插入速度非常慢:空表中1K条目的插入时间为65秒,mysql,insert,innodb,database-performance,Mysql,Insert,Innodb,Database Performance,我注意到我的mysql插入非常慢。为了测试和演示,我使用了下表: +----------+-----------------------------------------------+ | Table | Create Table | +----------+-----------------------------------------------+ | ik_b64_8 | CREATE TABLE `incr_tb

我注意到我的mysql插入非常慢。为了测试和演示,我使用了下表:

+----------+-----------------------------------------------+
| Table    | Create Table                                  |
+----------+-----------------------------------------------+
| ik_b64_8 | CREATE TABLE `incr_tbl` (
  `cnt` int(11) NOT NULL,
  PRIMARY KEY (`cnt`)
) ENGINE=InnoDB DEFAULT CHARSET=ascii COLLATE=ascii_bin |
+----------+-----------------------------------------------+
和python代码:

def profile_basic(cnt):
    db = database.Connection("localhost","testing_delme","root", "")
    t1 = time.time()
    for ii in range(cnt):
        db.execute("INSERT INTO testing_delme.incr_tbl VALUES (%s)", ii)
    print time.time() - t1
当我在一个空表上运行这个只插入的代码时,1K个插入需要65秒。我将innodb_flush_log_设置为_trx_commit=1,我需要它,因为表不能丢失任何数据。我的问题是,有了这一套,插入会变得如此缓慢吗?还是我也遗漏了什么?

“不能丢失任何数据。”这只是程度的问题。如果不刷新,可能会丢失最后一秒的数据。使用法拉盛,您可能只会丢失当时正在编写的内容。你真的想在这件事上大受打击吗

此外,如果您的磁盘损坏,您将丢失自上次备份以来的所有数据,这从长远来看是不可避免的,而且会涉及更多的数据,因此我更担心频繁备份

禁用冲洗。我估计,由于所有的磁盘活动,每次插入即使不是几百毫秒,也很容易需要几十毫秒。表上有几个索引会使情况更糟


尽管如此,如果每次写入都必须刷新,那么如果将数据库放在SSD上,性能也会有很大提高。

我认为每秒事务数达到了极限。每次插入都被视为单独的事务,您必须处于自动提交模式

(1) 在一条SQL语句中插入多行,就可以了:

insert into incr_tbl values (1),(2),(3)....
Python等价物如下所示:

db.execute("insert into incr_tbl values %s" % ",".join(["(%s)" % i for i in range(xx)]))
(2) 或者,您可以显式启动事务:

db = ...(autocommit=False)
db.begin_transaction()
for i in xx: db.execute(... insert i ...)
db.commit()
如果你有一个像样的服务器,你的发送速率应该远远高于每秒15,所以检查你的机器和mysql设置。即使您提交到磁盘并每次等待(sqlite),您在商品硬盘上的传输速率也应该为100 tx/s。我只在数据库位于usb闪存时见过这么低的速率。另一种可能的解释是,您的python离数据库很远,网络延迟会降低性能,在本例中,(1)有帮助,(2)没有帮助。

使用
db.execute()
您正在逐个插入,插入速度会非常慢。使用循环构建一个列表,然后执行一次批量插入,即
db.executemany()


您是否尝试过使用一条语句将它们全部插入?启动事务;插入foo(c1)值(v1)、(v2)、(v3);犯罪我没有尝试过事务,因为在实际的表中(以上仅用于演示),我一次只能插入一个条目。它将位于Web服务器后面并捕获用户注册。因此,这是无济于事的。不过,如果这提供了任何线索,我可以在这张桌子上试试,然后让你们知道。仅供参考:我试过交易。对于1K条目,它报告70ms。您应该使用事务,无论如何-启动事务;插入foo(c1)值(v1);承诺;——或者回滚。我关心的表存储订单信息。正如您所说,最坏的情况是我丢失了1个插件。我的问题是:每次插入是否真的需要65毫秒,或者是否有其他因素(除了innodb_flush_log_at_trx_commit)让我错过了?好吧,试着关闭innodb_flush_log_at_trx_commit,看看有多大的不同。是的,我已经尝试过了。innodb在trx提交=2->140ms/1K插入时刷新日志。将其设置为0需要109ms。我还设置了innodb_file_per_table,将上述值略微降低到123ms和109ms。这已经很长时间了,但我认为我的情况是分析插入,就像新用户注册到webapp时插入一样。我想我处于自动提交模式,默认使用我正在使用的torndb。除了删除autocommit和使用transaction之外,还有其他mysql配置需要优化以获得更好的插入性能吗?即使使用autocommit=False,然后执行db.execute()并在循环后使用db.commit(),代码也会逐个添加,然后全部提交。我不得不添加600k个插入,每100个插入需要10秒(几乎一整天),然后我查看并将其更改为db.executemany(),它在一分钟内完成了所有600k个插入。关于你的评论“新用户注册时插入”,你在标题中的表现是每1k个插入65秒,因此大约每秒15次。如果每秒的用户数超过这个数,我会缓存新的注册,然后使用executemany()进行大容量插入。对于缓存Google“memcached”,我不能使用executemany()在go中插入倍数。让我这样说:我正在分析一个插入,以了解当用户在我的live网站上注册时,我的实例需要多长时间。当用户注册时,go中没有许多注册可插入。我们的mysql配置或执行方式肯定有问题。我想到了在redis中缓存(在我的服务器上是实时的)并进行批量处理。但是,缓存/延迟批量条目的插入以提高性能并不是一个好的设计。它增加了不必要的复杂性,以掩盖较低的问题。我使用redis作为只读缓存。
def profile_basic(cnt):
    import mysql.connector, time
    cnx = mysql.connector.connect("localhost","testing_delme","root", "")
    db = cnx.cursor()
    list_ii = []
    t1 = time.time()
    for ii in range(cnt):
        list_ii.append(ii)
    # One bulk insert
    db.executemany("INSERT INTO testing_delme.incr_tbl VALUES (%s)", list_ii)
    # Don't forget to commit
    db.commit()
    print time.time() - t1