带有SELECT性能问题的巨大innodb表

时间:2016-12-21 23:55:32

标签: mysql select optimization innodb

我有两个巨大的innodb表(page:+ 40M行,+ 30Gb和stat:+ 45M行,+ 10Gb)。我有一个查询从这两个表的连接中选择行,它过去需要大约一秒钟来执行。最近它需要超过20秒(有时长达几分钟)才能完成完全相同的查询。我怀疑有很多插入和更新可能需要优化。我使用phpMyAdmin在桌面上运行OPTIMIZE TABLE,但没有任何改进。我已经搜索了很多,但在这种情况下无法找到任何帮助我的内容。

我之前提到的查询如下所示:

SELECT `c`.`unique`, `c`.`pub`
    FROM `pages` `c`
    LEFT JOIN `stat` `s` ON `c`.`unique`=`s`.`unique`
    WHERE `s`.`isc`='1'
      AND `s`.`haa`='0'
      AND (`pubID`='24')
    ORDER BY `eid` ASC LIMIT 0, 10

这些是表结构:

CREATE TABLE `pages` (
  `eid` int(10) UNSIGNED NOT NULL,
  `ti` text COLLATE utf8_persian_ci NOT NULL,
  `fat` text COLLATE utf8_persian_ci NOT NULL,
  `de` text COLLATE utf8_persian_ci NOT NULL,
  `fad` text COLLATE utf8_persian_ci NOT NULL,
  `pub` varchar(100) COLLATE utf8_persian_ci NOT NULL,
  `pubID` int(10) UNSIGNED NOT NULL,
  `pubn` text COLLATE utf8_persian_ci NOT NULL,
  `unique` tinytext COLLATE utf8_persian_ci NOT NULL,
  `pi` tinytext COLLATE utf8_persian_ci NOT NULL,
  `kw` text COLLATE utf8_persian_ci NOT NULL,
  `fak` text COLLATE utf8_persian_ci NOT NULL,
  `te` text COLLATE utf8_persian_ci NOT NULL,
  `fae` text COLLATE utf8_persian_ci NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci;
ALTER TABLE `pages`
  ADD PRIMARY KEY (`eid`),
  ADD UNIQUE KEY `UNIQ` (`unique`(128)),
  ADD KEY `pub` (`pub`),
  ADD KEY `unique` (`unique`(128)),
  ADD KEY `pubID` (`pubID`) USING BTREE;
ALTER TABLE `pages` ADD FULLTEXT KEY `faT` (`fat`);
ALTER TABLE `pages` ADD FULLTEXT KEY `faA` (`fad`,`fae`);
ALTER TABLE `pages` ADD FULLTEXT KEY `faK` (`fak`);
ALTER TABLE `pages` ADD FULLTEXT KEY `pubn` (`pubn`);
ALTER TABLE `pages` ADD FULLTEXT KEY `faTAK` (`fat`,`fad`,`fak`,`fae`);
ALTER TABLE `pages` ADD FULLTEXT KEY `ab` (`de`,`te`);
ALTER TABLE `pages` ADD FULLTEXT KEY `Ti` (`ti`);
ALTER TABLE `pages` ADD FULLTEXT KEY `Kw` (`kw`);
ALTER TABLE `pages` ADD FULLTEXT KEY `TAK` (`ti`,`de`,`kw`,`te`);
ALTER TABLE `pages`
  MODIFY `eid` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;


CREATE TABLE `stat` (
  `sid` int(10) UNSIGNED NOT NULL,
  `unique` tinytext COLLATE utf8_persian_ci NOT NULL,
  `haa` tinyint(1) UNSIGNED NOT NULL,
  `isc` tinyint(1) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci;
ALTER TABLE `stat`
  ADD PRIMARY KEY (`sid`),
  ADD UNIQUE KEY `Unique` (`unique`(128)),
  ADD KEY `isc` (`isc`),
  ADD KEY `haa` (`haa`),
ALTER TABLE `stat`
  MODIFY `sid` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;

以下查询只花了0.0126秒,总结果为38685601,如phpMyAdmin所述:

SELECT `sid` FROM `stat` WHERE `s`.`isc`='1' AND `s`.`haa`='0'

并且这一次花了0.0005秒,总结果为5159484

SELECT `eid`, `unique`, `pubn`, `pi` FROM `pages` WHERE `pubID`='24'

我错过了什么吗?有人可以帮忙吗?

2 个答案:

答案 0 :(得分:3)

减速可能是由于扫描了这么多行,现在已经超过了缓存范围。所以,让我们尝试改进查询。

  • INDEX(pubID)替换为INDEX(pubID, eid) - 这可能允许索引处理WHEREORDER BY,从而避免排序。
  • TINYTEXT替换为VARCHAR(255)或更小的限制。这可能会加速tmp表。
  • 不要在eid上使用前缀索引 - 它是INT
  • 不要说UNIQUE前缀 - UNIQUE(x(128))只检查前128列的唯一性!
  • 更改为VARCHAR(255)(或更低)后,您可以将UNIQUE应用于整个列。
  • 最大的性能问题是对两个表进行过滤 - 您可以将状态标志移动到主表中吗?
  • LEFT JOIN更改为JOIN
  • unique的样子是什么?如果它是" UUID",那可以进一步解释麻烦。
  • 如果这是一个39个字符的UUID,则可以将该字符串转换为16字节的列,以进一步节省空间(和加速)。如有必要,让我们进一步讨论。

500万的结果是0.5ms是假的 - 它是从查询缓存中获取的。关闭质量控制或使用SELECT SQL_NO_CACHE...

运行

答案 1 :(得分:1)

+1给@RickJames回答,但是接下来我做了一个测试。

我还建议你不要使用名称unique作为列名,因为它是一个SQL保留字。

ALTER TABLE pages 
  CHANGE `unique` objectId VARCHAR(128) NOT NULL COMMENT 'Document Object Identifier',
  DROP KEY pubId,
  ADD KEY bktest1 (pubId, eid, objectId, pub);

ALTER TABLE stat 
    CHANGE `unique` objectId VARCHAR(128) NOT NULL COMMENT 'Document Object Identifier',
    DROP KEY `unique`,
    ADD UNIQUE KEY bktest2 (objectId, isc, haa);

mysql> explain SELECT `c`.`objectId`, `c`.`pub`     FROM `pages` `c` JOIN `stat` `s` ON `c`.`objectId`=`s`.`objectId`     WHERE `s`.`isc`='1'       AND `s`.`haa`='0'       AND (`pubID`='24')     ORDER BY `eid` ASC LIMIT 0, 10;
+----+-------------+-------+------------+--------+-------------------------+---------+---------+-----------------------------+------+----------+--------------------------+
| id | select_type | table | partitions | type   | possible_keys           | key     | key_len | ref                         | rows | filtered | Extra                    |
+----+-------------+-------+------------+--------+-------------------------+---------+---------+-----------------------------+------+----------+--------------------------+
|  1 | SIMPLE      | c     | NULL       | ref    | unique,unique_2,bktest1 | bktest1 | 4       | const                       |    1 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | s     | NULL       | eq_ref | bktest2,haa,isc         | bktest2 | 388     | test.c.objectId,const,const |    1 |   100.00 | Using index              |
+----+-------------+-------+------------+--------+-------------------------+---------+---------+-----------------------------+------+----------+--------------------------+

通过创建多列索引,这使它们覆盖索引,并且您可以看到"使用索引"在EXPLAIN报告中。

eid秒放入bktest1索引非常重要,这样可以避免使用文件排序。

这是您希望优化此查询而不对表进行非规范化或分区的最佳方法。

接下来,您应确保缓冲池足够大,以容纳所有请求的数据。

相关问题