跨多个表优化mysql查询
Posted
技术标签:
【中文标题】跨多个表优化mysql查询【英文标题】:optimize mysql query across multiple tables 【发布时间】:2015-03-22 04:42:54 【问题描述】:我在优化以下连接多个表的查询时遇到了一些问题。
SELECT count(*) as count, `snapshot_id`, `snapshot_guid`, `image`, `subject`, `name`, `brands`.`facebook`, `brands`.`brand_id`, `brand_guid`, `date_sent`
FROM (`snapshots`)
INNER JOIN `brands` ON `snapshots`.`brand_id` = `brands`.`brand_id`
WHERE `snapshots`.`status` = 1
AND `brands`.`status` = 1
AND `brands`.`archive` = 0
GROUP BY `snapshots`.`brand_id`, `snapshots`.`subject`
ORDER BY `date_sent` desc
LIMIT 20
执行时间:4.9 秒
使用解释:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE brands ALL PRIMARY,brand_id,status,brand_status NULL NULL NULL 338 Using where; Using temporary; Using filesort
1 SIMPLE snapshots ref brand_id,status,snapshot_brand_status snapshot_brand_status 5 mockd_catalog.brands.brand_id,const 166
描述品牌和快照的表格:
描述品牌
Field Type Null Key Default Extra
brand_id int(11) NO PRI NULL auto_increment
brand_guid char(12) NO MUL NULL
friendly varchar(128) YES NULL
name varchar(128) NO NULL
url varchar(2048) YES NULL
logo text YES NULL
cover text YES NULL
facebook varchar(2048) YES NULL
address_1 varchar(128) YES NULL
address_2 varchar(128) YES NULL
city varchar(50) YES NULL
state varchar(50) YES NULL
postal varchar(20) YES NULL
country_code varchar(128) YES NULL
date_created datetime YES NULL
date_modified datetime YES NULL
hp_snapshot tinyint(1) NO 0
status tinyint(1) YES MUL 0
archive tinyint(1) NO 0
描述快照
Field Type Null Key Default Extra
snapshot_id int(11) NO PRI NULL auto_increment
snapshot_guid char(36) YES MUL NULL
brand_id int(11) NO MUL 0
email varchar(256) NO MUL NULL
seed_email varchar(256) NO NULL
date_sent datetime NO MUL NULL
date_created datetime NO NULL
date_modified datetime YES NULL
content_type varchar(10) YES NULL
subject varchar(256) NO NULL
source longtext YES NULL
html longtext NO NULL
html_error text YES NULL
thumbnail text YES NULL
image text YES NULL
status tinyint(1) NO MUL 0
archive tinyint(1) NO 0
tags text YES NULL
我已经尝试了所有我能想到的方法,但似乎无法进一步优化此查询。任何帮助表示赞赏。
瑞克
编辑 2015 年 3 月 22 日@美国东部时间上午 10:27:
CREATE TABLE `snapshots` (
`snapshot_id` int(11) NOT NULL AUTO_INCREMENT,
`snapshot_guid` char(36) DEFAULT NULL,
`brand_id` int(11) NOT NULL DEFAULT '0',
`email` varchar(256) NOT NULL,
`seed_email` varchar(256) NOT NULL,
`date_sent` datetime NOT NULL,
`date_created` datetime NOT NULL,
`date_modified` datetime DEFAULT NULL,
`content_type` varchar(10) DEFAULT NULL,
`subject` varchar(256) NOT NULL,
`source` longtext,
`html` longtext NOT NULL,
`html_error` text,
`thumbnail` text,
`image` text,
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '-1= error, 0 = new, 1 = approved, 2 = review ',
`archive` tinyint(1) NOT NULL DEFAULT '0',
`tags` text,
PRIMARY KEY (`snapshot_id`),
KEY `snapshot_id` (`snapshot_id`) USING BTREE,
KEY `brand_id` (`brand_id`),
KEY `email` (`email`(255)),
KEY `status` (`status`),
KEY `snapshot_guid` (`snapshot_guid`) USING BTREE,
KEY `subject` (`subject`(255)),
KEY `archive` (`archive`),
KEY `archive_status` (`archive`,`status`),
KEY `date_sent` (`date_sent`) USING BTREE,
KEY `recent_snapshots` (`snapshot_id`,`snapshot_guid`,`archive`,`status`,`brand_id`,`date_sent`,`subject`(255)) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=95002 DEFAULT CHARSET=utf8;
品牌表:
CREATE TABLE `brands` (
`brand_id` int(11) NOT NULL AUTO_INCREMENT,
`brand_guid` char(12) NOT NULL,
`friendly` varchar(128) DEFAULT NULL,
`name` varchar(128) NOT NULL,
`url` varchar(2048) DEFAULT NULL,
`logo` text,
`cover` text,
`facebook` varchar(2048) DEFAULT NULL,
`address_1` varchar(128) DEFAULT NULL,
`address_2` varchar(128) DEFAULT NULL,
`city` varchar(50) DEFAULT NULL,
`state` varchar(50) DEFAULT NULL,
`postal` varchar(20) DEFAULT NULL,
`country_code` varchar(128) DEFAULT NULL,
`date_created` datetime DEFAULT NULL,
`date_modified` datetime DEFAULT NULL,
`hp_snapshot` tinyint(1) NOT NULL DEFAULT '0',
`status` tinyint(1) DEFAULT '0',
`archive` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`brand_id`),
KEY `brand_guid` (`brand_guid`) USING BTREE,
KEY `brand_id` (`brand_id`),
KEY `status` (`status`) USING BTREE,
KEY `archive_status` (`archive`,`status`),
KEY `archive` (`archive`)
) ENGINE=InnoDB AUTO_INCREMENT=423 DEFAULT CHARSET=utf8;
选择语句:
SELECT
COUNT(*) AS count,
`snapshot_id`,
`snapshot_guid`,
`image`,
`subject`,
`name`,
`brands`.`facebook`,
`brands`.`brand_id`,
`brand_guid`,
`date_sent`
FROM
(`snapshots`)
INNER JOIN
`brands` ON `snapshots`.`brand_id` = `brands`.`brand_id`
WHERE
`snapshots`.`archive` = 0
AND `snapshots`.`status` = 1
AND `brands`.`archive` = 0
AND `brands`.`status` = 1
GROUP BY `snapshots`.`brand_id` , `snapshots`.`subject`
ORDER BY `date_sent` DESC
LIMIT 20;
解释:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE snapshots ref brand_id,status,archive,archive_status status 1 const 48304 Using where; Using temporary; Using filesort
1 SIMPLE brands eq_ref PRIMARY,brand_id,status,archive_status,archive PRIMARY 4 mockd_catalog.snapshots.brand_id 1 Using where
【问题讨论】:
能不能把上面两张表的SQL DDL贴出来,而不是“DESC table_name”? 您在寻找创建语句吗? 是的,我正在寻找“创建表”语句。 请参阅上面的编辑。谢谢! 非常感谢任何帮助或指导。 【参考方案1】:有几个问题:
date_send
上的密钥在 order by date_sent
中使用时定义为 using hash
。
您有一个带有brand.status
和brand.archive
的where 子句。 archive
仅在组合键 brand_id, status, archive
中,但由于 where 子句中未使用第一列 (brand_id),因此无法使用。仅为archive
或复合(status, archive)
创建索引。
subject
需要自己的索引。目前其指数深埋在综合指数中。
一般来说,复合索引中列的顺序很重要。该索引仅在使用的列是第一个列时才有用。此外,第一列越宽,它们的效果就越差。这意味着
KEY `snap_indx` (`snapshot_id`,`snapshot_guid`,`email`(255),`date_sent`,`subject`(255),`status`,`archive`)
效率低,因为 subject
的 255 个字节位于 status
和 archive
之前,它们每个只有 4 个字节。
【讨论】:
您好 Lorenz,根据您的反馈进行了一些更改,但仍有问题。请看我上面的编辑。 关于我应该添加什么索引的任何想法?此时查询仍然需要 4 - 5 秒。 清理你的索引,保留你需要的。然后,使用解释尝试创建最佳索引。避免使用Using temporary
和Using filesort
。以上是关于跨多个表优化mysql查询的主要内容,如果未能解决你的问题,请参考以下文章