我有以下查询,该查询应该按给定的linker
号码查找每个相关记录的额外信息。例如,此查询会查找Title
单个Author
的所有文档的Date
,linkTo
和linker=86sgv_ksg:0040608
。
SELECT
`r`.`linker`,
IF(`s`.`isSecond`='1', `c2`.`title`, `c1`.`title`) AS `Title`,
IF(`s`.`isSecond`='1', `c2`.`author`, `c1`.`author`) AS `Author`,
IF(`s`.`isSecond`='1', `c2`.`date`, `c1`.`date`) AS `Date`
FROM
(SELECT `linker` FROM `my_rel` WHERE `linkTo`='86sgv_ksg:0040608') `r`
INNER JOIN `my_stat` `s` ON `r`.`linker`=`s`.`linker`
LEFT JOIN `my_content_1` `c1` ON (`s`.`isSecond`='0' AND `s`.`linker`=`c1`.`linker`)
LEFT JOIN `my_content_2` `c2` ON (`s`.`isSecond`='1' AND `s`.`linker`=`c2`.`linker`);
这是EXPLAIN
结果:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 38702 NULL
1 PRIMARY s eq_ref Unique Unique 767 r.linker 1 NULL
1 PRIMARY c1 ref linker linker 767 r.linker 1 Using where
1 PRIMARY c2 ref linker linker 767 r.linker 1 Using where
2 DERIVED my_rel ref Link Link 767 const 38702 Using index condition
此查询取决于找到的记录数量需要几秒钟(每1000行找到几乎一秒)
# Query_time: 20.393228 Lock_time: 0.000115 Rows_sent: 19917 Rows_examined: 99672
使用此服务器:
CPU: Intel® Core™ i7-6700
RAM: 64 GB DDR4
Hard Drive: 2 x 500 GB SATA 6 Gb/s
|_ SSD: Software-RAID 0 = 1000GB
我的操作系统(Linux)和MySQL数据库都在SSD上。但是查询仍需要几秒钟。
表my_rel
(~200M行)包含文档之间的所有关系,这些文档分为两个表:my_content_1
(~5M行)和my_content_2
(~65M行)取决于他们的类型。表my_stat
(约70M行)标识每个文档的位置(my_content_1
或my_content_2
)。这四个表的SHOW CREATE TABLE
如下:
CREATE TABLE `my_content_1` /*similarly `my_content_2`*/ (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` text COLLATE utf8_general_ci NOT NULL,
`author` tinytext COLLATE utf8_general_ci NOT NULL,
`date` date NOT NULL,
`linker` varchar(255) COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `linker` (`linker`) USING BTREE,
KEY `date` (`date`)
) ENGINE=InnoDB AUTO_INCREMENT=67654117 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=COMPRESSED
CREATE TABLE `my_rel` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`linker` varchar(255) COLLATE utf8_general_ci NOT NULL,
`order` int(10) unsigned NOT NULL,
`linkTo` varchar(255) COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `Unique` (`linker`, `order`) USING BTREE,
KEY `Link` (`linkTo`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=248383246 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=COMPRESSED
CREATE TABLE `my_stat` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`linker` varchar(255) COLLATE utf8_general_ci NOT NULL,
`isSecond` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `Unique` (`linker`) USING BTREE,
KEY `isSecond` (`isSecond`)
) ENGINE=InnoDB AUTO_INCREMENT=111412100 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=COMPRESSED
最初我的查询是这样的:
SELECT
`r`.`linker`,
IF(`s`.`isSecond`='1', `c2`.`title`, `c1`.`title`) AS `Title`,
IF(`s`.`isSecond`='1', `c2`.`author`, `c1`.`author`) AS `Author`,
IF(`s`.`isSecond`='1', `c2`.`date`, `c1`.`date`) AS `Date`
FROM `my_rel` `r`
LEFT JOIN `my_stat` `s` ON `r`.`linker`=`s`.`linker`
LEFT JOIN `my_content_1` `c1` ON (`s`.`isSecond`='0' AND `s`.`linker`=`c1`.`linker`)
LEFT JOIN `my_content_2` `c2` ON (`s`.`isSecond`='1' AND `s`.`linker`=`c2`.`linker`)
WHERE `r`.`linkTo`='86sgv_ksg:0040608' AND `r`.`linker`!='86sgv_ksg:0040608'
GROUP BY `r`.`linker`
ORDER BY `Date` DESC;
它通过分组和排序比我当前的查询花了一点点时间,我删除了它以获得更好的速度。当前查询仍需要很长时间才能完成所有链接。
我的查询可以更快吗?
答案 0 :(得分:0)
如果您同时拥有AUTO_INCREMENT
和UNIQUE
密钥,请考虑删除AUTO_INCREMENT
并将UNIQUE
提升为PRIMARY
。 通常这将
my_rel
,...
SELECT `linker` FROM `my_rel` WHERE `linkTo`='86sgv_ksg:0040608'
FROM `my_rel` `r`
... ON `r`.`linker`...
WHERE `r`.`linkTo`='86sgv_ksg:0040608'
AND `r`.`linker`!='86sgv_ksg:0040608'
这两个都要求(linker, linkTo)
的索引,通过更改
PRIMARY KEY (`id`),
UNIQUE KEY `Unique` (`linker`, `order`) USING BTREE,
KEY `Link` (`linkTo`) USING BTREE
到
PRIMARY KEY (`linker`, `order`),
KEY (`linkTo`, linker)
由于我们正在查看200M中的38K记录,因此从辅助密钥到主密钥的弹跳可能会超过38K磁盘命中,这可能需要380秒才能在旋转驱动器上运行。 (由于缓存而减少了一些,它可能会降低到你遇到的整个20秒。)
通过使用&#39;复合材料&#39;我建议,该索引是&#34;覆盖&#34;,因此将击中磁盘可能是380次而不是38000次。这可能是100倍的加速(但更有可能是10倍)。
一旦到达c1
和c2
,此更改可以节省另外38K磁盘点击次数:
PRIMARY KEY (`id`),
UNIQUE KEY `linker` (`linker`) USING BTREE,
KEY `date` (`date`)
- &GT;
PRIMARY KEY `linker` (`linker`),
KEY `date` (`date`)
在进行这些更改时,请考虑降低255
中的VARCHARs
。此外,如果链接器值看起来像&#39; 86sgv_ksg:0040608&#39;,请考虑linker
和link_to
是CHARACTER SET ascii
。
另外,my_stat
...
PRIMARY KEY (`id`),
UNIQUE KEY `Unique` (`linker`) USING BTREE,
KEY `isSecond` (`isSecond`)
- &GT;
PRIMARY KEY (`linker`)
注意:标记(isSecond
)上的单列索引不太可能被使用。
(InnoDB索引的默认值为BTree
。唯一的例外是FULLTEXT
和SPATIAL
。)
答案 1 :(得分:0)
从解释来看,这里的主要问题似乎是子查询没有被正确编入索引。 我会添加以下索引:
ALTER TABLE `my_rel` ADD INDEX `my_rel_index_1` (`linkTo`, `linker`);
此外,我可以在查询中看到许多地方,您将数字列与字符串进行比较。例如:
`s`.`isSecond` = '0'
我会避免这种情况并删除&#39; 0&#39;周围的引号(在你做同样事情的所有地方)。这样的比较可能会导致隐式转换,这可能会阻止这些过滤器使用正确的索引。