我正在尝试优化涉及两个表的左连接,但我无法理解可能的索引以加快速度。 表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 |
+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+
任何有用的建议将不胜感激。 (我不是系统的开发人员,我不负责代码 - 但如果可以改进的话,我想向程序员提供输入......)
非常感谢! 塞巴斯蒂安
答案 0 :(得分:2)
不要盲目使用VARCHAR(255)
。使用对数据有意义的数据类型。其中许多列听起来像数字,而不是字符串。
假设年+月+日只是DATE
的一部分,请使用数据类型为DATE
的单个列。然后,使用DAY(date_col)
提取日期。
每个InnoDB表都应该有一个PRIMARY KEY
。也许(text_id, beginPosition)
的组合是独特的,可能是PK?
每列都是NULL
??我对此表示怀疑。除非您有NOT NULL
的原因,否则请NULL
。{/ p>
refnumber
是AUTO_INCREMENT
,但不是PRIMARY KEY
??是什么给了什么?
进行上述更改有助于部分。但是所述的查询注定要扫描整个2M行表并进入另一个表。事情可以做到。但它们将涉及构建和维护摘要表。
答案 1 :(得分:1)
您的EXPLAIN报告显示您已经在使用两个表的索引,并且您没有为GROUP BY使用临时表,并且这两个表都使用覆盖索引("使用索引&#34)。
除了创建索引之外,您还可以做其他一些事情:
NOT NULL
。这可能会摆脱"使用哪里"注意,这意味着它必须将表达式作为搜索的一部分进行评估。这可能会稍微有点效率。innodb_buffer_pool_size
增加到足以使索引可以缓存在内存中。如果查询仅从缓冲池读取索引页,则可以获得更好的性能和更少的磁盘I / O. 重新评论:
BTW,表db_dist_fb8ddyk760可能只使用一次或两次然后丢弃。
那么为什么要将它存储在持久数据库中呢?
考虑使用像Redis这样的内存中键/值存储。使每个键对应一天,每个值是一个包含命中数和不同text_id的集合的结构。这基本上是一个汇总表(你也可以在SQL中做),但是Redis在内存中。