【发布时间】:2017-01-21 18:39:05
【问题描述】:
我有以下架构:
CREATE TABLE `news` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`news_category_id` int(10) unsigned NOT NULL,
`news_type_id` int(10) unsigned NOT NULL,
`news_pictures_main_id` int(10) unsigned DEFAULT NULL,
`title` tinytext COLLATE latin1_general_ci,
`body` text COLLATE latin1_general_ci,
`tmstp` timestamp NULL DEFAULT NULL,
`subcategory` varchar(64) COLLATE latin1_general_ci DEFAULT NULL,
`source` varchar(128) COLLATE latin1_general_ci DEFAULT NULL,
`old_id` int(10) unsigned DEFAULT NULL,
`tags` text COLLATE latin1_general_ci,
PRIMARY KEY (`id`),
KEY `news_time_idx` (`tmstp`),
KEY `fk_news_news_pictures1` (`news_pictures_main_id`),
KEY `fk_news_news_category1` (`news_category_id`),
KEY `fk_news_news_type1` (`news_type_id`),
CONSTRAINT `fk_news_news_category1` FOREIGN KEY (`news_category_id`) REFERENCES `news_category` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_news_news_pictures1` FOREIGN KEY (`news_pictures_main_id`) REFERENCES `news_pictures` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `fk_news_news_type1` FOREIGN KEY (`news_type_id`) REFERENCES `news_type` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB
CREATE TABLE `news_pictures` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`path` text COLLATE latin1_general_ci,
`description` text COLLATE latin1_general_ci,
`author` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
`news_id` int(10) unsigned DEFAULT NULL,
`temp_id` varchar(40) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `filename_old_id_unq` (`path`(20),`temp_id`(6)),
KEY `fk_news_pictures_news1` (`news_id`),
KEY `temp_id_idx` (`temp_id`(8)),
CONSTRAINT `fk_news_pictures_news1` FOREIGN KEY (`news_id`) REFERENCES `news` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB
CREATE TABLE `news_category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
CREATE TABLE `news_type` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
`slug` varchar(45) COLLATE latin1_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
KEY `news_type_slug_idx` (`slug`)
) ENGINE=InnoDB
由此得出以下观点:
CREATE OR REPLACE VIEW `news_full` AS select `n`.`id` AS `id`,
`n`.`title` AS `title`,
`n`.`body` AS `body`,
`n`.`tmstp` AS `tmstp`,
`n`.`subcategory` AS `subcategory`,
`n`.`source` AS `source`,
`n`.`old_id` AS `old_id`,
`n`.`news_type_id` AS `news_type_id`,
`n`.`tags` AS `tags`,
`nt`.`name` AS `news_type_name`,
`nt`.`slug` AS `news_type_slug`,
`n`.`news_pictures_main_id` AS `news_pictures_main_id`,
`np`.`path` AS `news_pictures_main_path`,
`np`.`description` AS `news_pictures_main_description`,
`np`.`author` AS `news_pictures_main_author`,
`np`.`temp_id` AS `news_pictures_main_temp_id`,
`n`.`news_category_id` AS `news_category_id`,
`nc`.`name` AS `news_category_name`
from (((`news` `n`
left join `news_pictures` `np` on((`n`.`news_pictures_main_id` = `np`.`id`)))
join `news_category` `nc` on((`n`.`news_category_id` = `nc`.`id`)))
join `news_type` `nt` on((`n`.`news_type_id` = `nt`.`id`)));
但是,如果我尝试运行以下查询:
select * from news_full order by tmstp limit 100
我得到以下执行计划(请点击图片展开):
注意第一步中的Using temporary; Using filesort 字段。但这很奇怪,因为tmstp 字段在基表中被索引。
首先我认为这是由于视图上的left join,但我已将其更改为inner join,我得到了相同的结果。
编辑
正如@Michael-sqlbot 巧妙地注意到的那样,查询优化器正在反转基表的顺序,将news_category (nc) 放在首位。
如果我将创建视图的查询更改为仅使用 LEFT JOINs,它似乎可以工作:
不满意,我使用原始查询创建了另一个视图,添加了STRAIGHT_JOIN 语句。所以,查询计划如下:
所以,它没有使用索引。
但是,如果我为基本查询运行计划并添加相同的 ORDER BY 和 LIMIT 子句,它确实使用索引:
【问题讨论】:
-
MySQL 不能在视图中使用索引。所以在 MySQL 中视图没有多大意义。
-
我听说你不能在 MySQL 的视图中创建索引,但它确实使用来自基础表的索引来运行查询。不过,我没有任何参考资料。如果你有一个证明我错了,你能把它贴在这里吗?
-
我必须在@Strawberry 上给你打电话(一切都是第一次)。只要视图可以使用
MERGE算法,就可以按预期使用索引,因为调用视图的语句与视图定义合并,结果查询得到最佳处理:dev.mysql.com/doc/refman/5.7/en/view-algorithms.html ...这里的问题是这个:如果直接运行原始查询(视图定义)会生成什么计划,使用ORDER BY和LIMIT? -
@Strawberry 这个查询计划表明服务器正在使用
MERGE将外部查询集成到视图定义中(不渲染整个视图然后应用ORDER和LIMIT)。 If it weren't, it would show aDERIVEDtable。我相信这里的实际问题是,即使没有在视图中使用,该查询也不会使用该索引进行排序,因为优化器正在重新排序表。要从索引中ORDER BY,具有索引的表必须在计划中排在第一位,以便服务器最初可以按索引顺序读取这些行。
标签: mysql database-performance