【问题标题】:InnoDB inserts very slow and slowing downInnoDB 插入速度非常慢并且速度变慢
【发布时间】:2012-02-25 05:14:34
【问题描述】:

我最近将我的项目表切换到 InnoDB(认为关系将是一件好事)。我正在使用 PHP 脚本一次索引大约 500 个产品。

存储单词/ids关联的表:

    CREATE TABLE `windex` (
 `word` varchar(64) NOT NULL,
 `wid` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `count` int(11) unsigned NOT NULL DEFAULT '1',
 PRIMARY KEY (`wid`),
 UNIQUE KEY `word` (`word`)
) ENGINE=InnoDB AUTO_INCREMENT=324551 DEFAULT CHARSET=latin1

另一个表存储产品 id/word id 关联:

CREATE TABLE `indx_0` (
 `wid` int(7) unsigned NOT NULL,
 `pid` int(7) unsigned NOT NULL,
 UNIQUE KEY `wid` (`wid`,`pid`),
 KEY `pid` (`pid`),
 CONSTRAINT `indx_0_ibfk_1` FOREIGN KEY (`wid`) REFERENCES `windex` (`wid`) ON DELETE CASCADE ON UPDATE CASCADE,
 CONSTRAINT `indx_0_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `product` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1

该脚本使用 MyISAM 进行了测试,它索引产品的速度相对较快(比 InnoDB 快得多)。第一次在 InnoDB 中运行它非常慢,但是在将更多值嵌套在一起之后,我最终加快了很多(但还不够)。

我认为 innodb 对于这种类型的事情会更快,因为行级锁,但事实并非如此。

我构造了一个类似于以下内容的查询:

SELECT
title,keywords,upc,...
FROM product
WHERE indexed = 0
LIMIT 500

我创建一个循环并用需要添加到 windex 的所有单词以及需要添加到 indx_0 的所有单词 id/产品 id 对来填充一个数组。

因为每当我执行因重复值而失败的“REPLACE INTO”或“INSERT IGNORE INTO”时,innodb 都会不断增加我的自动增量值,所以我需要确保我添加的值不存在。为此,我首先使用如下查询选择所有存在的值:

SELECT wid,word
FROM windex
WHERE
word = "someword1" or word = "someword2" or word = "someword3" ... ...

然后我根据现有结果过滤掉我的数组,这样我添加的所有新词都是 100% 新的。

这需要大约 20% 的总执行时间。另外 80% 用于将对值添加到 indx_0 中,其中还有更多值。

这是我得到的一个例子。

0.4806 秒选择产品。 (总共 0.4807 秒)。
0.0319 秒收集 500 件物品。 (总共 0.5126 秒)。
5.2396 秒选择 windex 值进行比较。 (总共 5.7836 秒)。
1.8986 秒更新计数。 (总共 7.6822 秒)。
0.0641 秒添加 832 个windex 记录。 (总共 7.7464 秒)。
添加 3435 个 pid/wid 对的索引需要 17.2725 秒。 (总共 25.7752 秒)。
索引 500 个产品的操作耗时 26.07 秒。

3435 对都在一个查询中执行,例如:

INSERT INTO indx_0(pid,wid)
VALUES (1,4),(3,9),(9,2)... ... ...

在我的情况下,为什么 InnoDB 比 MyISAM 慢这么多?

【问题讨论】:

  • 单词索引的想法是创建某种搜索功能吗?如果是这种情况,请查看真正的搜索引擎,例如 solr 或 mysql 全文搜索。无法胜过此类特定任务。

标签: mysql performance innodb


【解决方案1】:

InnoDB 提供比 MyIsam (FOREIGN KEYS) 更复杂的键结构,并且在 InnoDB 中重新生成键非常慢。您应该将所有更新/插入语句包含在一个事务中(这些实际上在 InnoDB 中非常快,一旦我在具有 2 个索引的 InnoDb 表上进行了大约 300 000 个插入查询并且大约需要 30 分钟,一旦我将每 10 000 个插入包含到 @ 987654322@ 和 COMMIT 花了不到 2 分钟)。

我推荐使用:

BEGIN TRANSACTION;
SELECT ... FROM products;
UPDATE ...;
INSERT INTO ...;
INSERT INTO ...;
INSERT INTO ...;
COMMIT;

这将导致 InnoDB 只刷新一次索引而不是几百次。

让我知道它是否有效

【讨论】:

  • 我相信它肯定会带来一些改进。我有一个类似的问题 Vyktor。看来这会奏效。谢谢-Uday
  • 我在游标中遇到了一个问题,这个问题已修复(从 90 秒到 0.9!)我慢慢地了解 InnoDB 的要求
  • @Vyktor,关于“我在BEGIN TRANSACTIONCOMMIT 中添加了每10 000 个插入,只用了不到2 分钟”,你为什么要分成10k 批?为什么不在一个事务中包含所有语句?
  • @Pacerier 更容易恢复,通常我会尽量避免非常大的事务,这样我就不会遇到内部表锁的麻烦......
  • @Vyktor,现在我不确定我是否理解你,当我们正确执行批量插入时,该事务肯定是唯一一个正在运行的.... .. 另外,“更容易恢复”是什么意思?
【解决方案2】:

我遇到了类似的问题,似乎 InnoDB 默认启用了 innodb_flush_log_at_trx_commit,它会刷新硬盘日志文件上的每个插入/更新查询。硬盘的写入速度是这个过程的瓶颈。

所以尝试修改你的mysql配置文件

  `innodb_flush_log_at_trx_commit  = 0`

重启mysql服务。

我在插入时经历了大约 100 倍的加速。

【讨论】:

  • 请注意,应用此选项会丢失事务安全性...如果您在告诉客户端完成后断电,但在实际写入磁盘之前将意味着它永远丢失。
猜你喜欢
  • 2012-08-04
  • 1970-01-01
  • 2011-08-07
  • 1970-01-01
  • 2013-11-27
  • 1970-01-01
  • 2016-11-25
  • 1970-01-01
  • 2016-10-10
相关资源
最近更新 更多