MySQL离开了group by - 优化索引

时间:2016-12-29 22:04:41

标签: mysql optimization indexing left-join

我正在尝试优化涉及两个表的左连接,但我无法理解可能的索引以加快速度。 表1包含2171289行:

text_metadata_for_nzcorpus | CREATE TABLE `text_metadata_for_nzcorpus` (
    `text_id` varchar(255) NOT NULL,
    `newspaper` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
    `year` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
    `month` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
    `day` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
    `section` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
    `subsection` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
    `topics` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
    `words` int(11) NOT NULL DEFAULT '0',
    `cqp_begin` bigint(20) unsigned NOT NULL DEFAULT '0',
    `cqp_end` bigint(20) unsigned NOT NULL DEFAULT '0',
    PRIMARY KEY (`text_id`),
    KEY `newspaper` (`newspaper`),
    KEY `year` (`year`),
    KEY `month` (`month`),
    KEY `day` (`day`),
    KEY `section` (`section`),
    KEY `subsection` (`subsection`),
    KEY `topics` (`topics`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

第二个表只包含8584行:

db_dist_fb8ddyk760 | CREATE TABLE `db_dist_fb8ddyk760` (
    `text_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
    `beginPosition` int(11) DEFAULT NULL,
    `endPosition` int(11) DEFAULT NULL,
    `refnumber` mediumint(9) NOT NULL AUTO_INCREMENT,
    KEY `refnumber` (`refnumber`),
    KEY `text_id` (`text_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16384 DEFAULT CHARSET=utf8 COLLATE=utf8_bin |

我需要运行以下类型的查询:

SELECT md.day as handle, count(db.text_id) as hits, 
    count(distinct db.text_id) as files FROM text_metadata_for_nzcorpus as md 
    LEFT JOIN db_dist_fb8ddyk760 as db on md.text_id = db.text_id 
    GROUP BY md.day;

目前处理时间超过5秒。因为它是我在网页上显示输出之前需要运行的很多查询之一,所以如果可能的话,我想加快速度。这是“解释”的输出:

+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref                  | rows    | Extra                    |
+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+
|  1 | SIMPLE      | md    | index | day           | day     | 768     | NULL                 | 2452080 | Using index              |
|  1 | SIMPLE      | db    | ref   | text_id       | text_id | 768     | cqpweb_db.md.text_id |       1 | Using where; Using index |
+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+

任何有用的建议将不胜感激。 (我不是系统的开发人员,我不负责代码 - 但如果可以改进的话,我想向程序员提供输入......)

非常感谢! 塞巴斯蒂安

2 个答案:

答案 0 :(得分:2)

不要盲目使用VARCHAR(255)。使用对数据有意义的数据类型。其中许多列听起来像数字,而不是字符串。

假设年+月+日只是DATE的一部分,请使用数据类型为DATE的单个列。然后,使用DAY(date_col)提取日期。

每个InnoDB表都应该有一个PRIMARY KEY。也许(text_id, beginPosition)的组合是独特的,可能是PK?

每列都是NULL ??我对此表示怀疑。除非您有NOT NULL的原因,否则请NULL。{/ p>

refnumberAUTO_INCREMENT,但不是PRIMARY KEY ??是什么给了什么?

进行上述更改有助于部分。但是所述的查询注定要扫描整个2M行表并进入另一个表。事情可以做到。但它们将涉及构建和维护摘要表。

答案 1 :(得分:1)

您的EXPLAIN报告显示您已经在使用两个表的索引,并且您没有为GROUP BY使用临时表,并且这两个表都使用覆盖索引("使用索引&#34)。

除了创建索引之外,您还可以做其他一些事情:

  • 将db_dist_fb8ddyk760.text_id定义为NOT NULL。这可能会摆脱"使用哪里"注意,这意味着它必须将表达式作为搜索的一部分进行评估。这可能会稍微有点效率。
  • 将db_dist_fb8ddyk760.text_id定义为该表的PRIMARY KEY,如果这是有意义的 - 换句话说,如果text_id在该表中是唯一的。那样"类型:ref"将成为"键入:eq_ref"意味着一个独特的键查找,这更有效。但是,如果这个表需要记录每个text_id的多次点击,当然可以忽略这个建议。
  • innodb_buffer_pool_size增加到足以使索引可以缓存在内存中。如果查询仅从缓冲池读取索引页,则可以获得更好的性能和更少的磁盘I / O.
  • 利用MySQL Query Cache,如果再次运行相同的查询,它将重复使用上一个查询的结果。但是,如果这些表中的数据更改频率高于执行查询,则查询缓存可能没什么用。
  • 考虑将结果缓存在应用程序内存或memcached中。

重新评论:

  BTW,表db_dist_fb8ddyk760可能只使用一次或两次然后丢弃。

那么为什么要将它存储在持久数据库中呢?

考虑使用像Redis这样的内存中键/值存储。使每个键对应一天,每个值是一个包含命中数和不同text_id的集合的结构。这基本上是一个汇总表(你也可以在SQL中做),但是Redis在内存中。