对InnoDB的简单INSERT查询占用太多

时间:2012-05-12 10:04:15

标签: mysql sql performance innodb

我有这个简单的查询:

INSERT IGNORE INTO beststat (bestid,period,rawView) VALUES ( 4510724 , 201205 , 1 ) 

在桌子上:

CREATE TABLE `beststat` (
 `bestid` int(11) unsigned NOT NULL,
 `period` mediumint(8) unsigned NOT NULL,
 `view` mediumint(8) unsigned NOT NULL DEFAULT '0',
 `rawView` mediumint(8) unsigned NOT NULL DEFAULT '0',
 PRIMARY KEY (`bestid`,`period`),
) ENGINE=InnoDB AUTO_INCREMENT=2020577 DEFAULT CHARSET=utf8

完成时需要1秒。


旁注:实际上它不需要总是1秒。有时它甚至在0.05秒内完成。但通常需要1秒


此表(beststat)目前有 ~500'000 记录,其大小为: 40MB 。我有 4GB RAM innodb buffer pool size = 104,857,600 ,其中包含:Mysql: 5.1.49-3

这是我数据库中唯一的InnoDB表(其他是MyISAM)

ANALYZE TABLE beststat显示:确定

也许InnoDB设置有问题?

2 个答案:

答案 0 :(得分:2)

我在3年前进行了一些模拟,作为客户评估项目的一部分。他们要求能够搜索不断添加数据的表格,他们想要最新的一分钟。

InnoDB在开始时已经显示出更好的结果,但很快就会恶化(远在1mil记录之前),直到我删除了所有索引(包括主要索引)。在那时,InnoDB在执行插入/更新时已经优于MyISAM。 (我的硬件要差得多,只能在我的笔记本电脑上执行测试。)

结论:如果你有索引,插入将永远受到影响,尤其是唯一的。

我建议进行以下优化:

  1. 从beststat表中删除所有索引并将其用作简单转储。
  2. 如果你真的需要这些独特的索引,可以考虑一些可编程的解决方案(比如一直记住最大的bestid,并坚持新记录超过这个数字 - 并立即增加这个数字。(但你真的需要这么多吗?)独特的领域 - 它们对我来说就像索引一样。)
  3. 让后台线程将新记录从InnoDB移动到另一个表(可以是MyISAM),在那里将它们编入索引。
  4. 考虑暂时删除索引,然后在批量更新后重新索引表,可能会切换两个表,以便查询永远不会中断。
  5. 我承认,这些是理论上的解决方案,但考虑到你的问题,这是我能说的最好的。

    哦,如果您的桌面计划增长到数百万,请考虑使用NoSQL解决方案。

答案 1 :(得分:1)

所以你在桌子上有两个独特的索引。您的主键是自动编号。由于这不是数据的一部分,因为当您将数据添加到数据时,它就是您称之为人工主键的数据。现在你有一个关于bestid和period的唯一索引。如果bestid和period应该是唯一的,那么它将是主键的一个很好的候选者。

Innodb将表存储为树或堆。如果您没有在innodb表上定义主键,那么如果您定义主键,它就是一个堆,它被定义为磁盘上的树。因此,在您的情况下,树基于自动编号密钥存储在磁盘上。因此,当您创建第二个索引时,它实际上会在磁盘上创建第二个树,其中包含索引中的bestid和period值。索引不包含表中的其他列,仅包括bestid,period和主键值。

好的,所以现在你首先插入数据就是确保唯一索引始终是唯一的。因此,它读取索引以查看您是否尝试插入重复值。这是减速发挥作用的地方。如果它通过测试写入数据,它首先必须确保唯一性。然后它还必须将bestid,period和primary key值插入唯一索引。因此总操作将是1读取索引,值1插入行到表1插入bestid和句点到索引。共有三个操作。如果您删除了自动编号并仅使用唯一索引作为主键,那么如果将唯一插入到表中,它将读取表。在这种情况下,您将具有以下操作数1读取表以检查值1插入表中。这是两个操作对三个。因此,通过删除冗余自动编号,您可以减少33%的工作量。

我希望这很清楚,因为我正在从我的Android输入并且自动更正继续将innodb更改为天生。希望我在电脑前。