2012-02-02 229 views
9

我最近将项目表切换到了InnoDB(认为关系将是一件好事)。我使用PHP脚本一次为大约500个产品编制索引。InnoDB插入速度很慢,速度很慢

表中存储字/ ID相关联:

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 /字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会因为rowlevel锁而对这种类型的事物快得多,但事实并非如此。

我建立一个查询,看起来像:

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

我创建了一个循环,并与所有需要被添加到WINDEX和所有需要的单词ID /产品ID对的话填充数组被添加到indx_0。

因为每当我做一个“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秒)。
17.2725秒添加3435个pid/wid对的索引。 (总共25.7752秒)。
操作耗时26.07秒,对500种产品进行索引。

的3435对正在在一个单一的查询执行,如:

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

为什么是这么的InnoDB远远低于在我的案件的MyISAM?

+0

创建某种搜索功能的单词索引的想法是什么?如果是这样的话,那么一直在那里做,检查一个真正的搜索引擎,例如solr或mysql全文搜索。无法超越这些特定的任务。 – 2012-02-02 15:54:31

回答

13

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

我建议使用:

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

这将导致InnoDB的刷新指标不几百倍一次。

让我知道它的工作

+0

它应该带来一些改进,我确信我相信。我有一个类似的问题Vyktor。看起来这会起作用。谢谢-Uday – Uday 2012-03-30 14:44:31

+2

我在游标中遇到了一个问题,修正了这个问题(从90秒到0.9!)慢慢地,我正在学习InnoDB的必要条件 – 2012-05-15 08:45:13

+0

@Vyktor,关于*“我将每10 000个插入内容放入'BEGIN TRANSACTION'和'COMMIT'花了不到2分钟“*,为什么你分成10k批次?为什么不在一次交易中附上**所有**陈述? – Pacerier 2015-04-09 13:31:50

4

我有一个类似的问题,它似乎InnoDB的默认拥有的innodb_flush_log_at_trx_commit启用它刷新在HDD日志文件中每插入/更新查询。您的硬盘写入速度是此过程的瓶颈。

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

`innodb_flush_log_at_trx_commit = 0` 

重新启动mysql服务。

我在插入时遇到过x100加速。

+1

请注意,使用此选项时,交易安全性会丢失,但如果在告诉客户端完成后失去能力,但是在它实际写入磁盘之前将意味着它永远丢失。 – Cine 2015-08-11 09:55:49