我有一个声明如下的存储过程:
CREATE DEFINER=`blabla`@`%` PROCEDURE `getAllDomainsByCountry`(IN dom_id INT)
BEGIN
SELECT
domain.id,
IFNULL(domain.indexed, '-') AS indexed,
domain.name,
country.language_code,
IFNULL(ip_adress.adress, '-') AS adress,
IFNULL(GROUP_CONCAT(category.name
SEPARATOR ', '),
'-') AS categories,
IFNULL(GROUP_CONCAT(category.id
SEPARATOR ', '),
'-') AS categories_id,
(SELECT
IFNULL(GROUP_CONCAT(DISTINCT client.name
SEPARATOR ', '),
'-')
FROM
link
LEFT JOIN
client_site ON link.client_site = client_site.id
LEFT JOIN
client ON client.id = client_site.client
WHERE
link.from_domain = domain.id) AS clients,
IFNULL(domain_host.name, '-') AS domain_host_account,
IFNULL(content_host.name, '-') AS content_host,
status.id AS status,
status.name AS status_name
FROM
domain
LEFT JOIN
ip_adress ON domain.ip = ip_adress.id
LEFT JOIN
domain_category ON domain.id = domain_category.domain
LEFT JOIN
category ON domain_category.category = category.id
LEFT JOIN
country ON domain.country = country.id
LEFT JOIN
domain_host_account ON domain.domain_host_account = domain_host_account.id
LEFT JOIN
domain_host ON domain_host_account.host = domain_host.id
LEFT JOIN
content_host ON domain.content_host = content_host.id
LEFT JOIN
domain_status ON domain.id = domain_status.domain
LEFT JOIN
status ON domain_status.status = status.id
WHERE
domain.country = dom_id
GROUP BY domain.id
ORDER BY domain.name;
END
如果我将参数dom_id的用法替换为静态整数,即:
WHERE
domain.country = 1
MySQL版本:5.5.41
说明:
id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,PRIMARY,domain,ref,idx_domain_country,idx_domain_country,5,const,1858,"Using where; Using temporary; Using filesort"
1,PRIMARY,ip_adress,eq_ref,PRIMARY,PRIMARY,4,dominfo.domain.ip,1,
1,PRIMARY,domain_category,ref,FK_domain_category_domain_idx,FK_domain_category_domain_idx,5,dominfo.domain.id,1,
1,PRIMARY,category,eq_ref,PRIMARY,PRIMARY,4,dominfo.domain_category.category,1,
1,PRIMARY,country,const,PRIMARY,PRIMARY,4,const,1,
1,PRIMARY,domain_host_account,eq_ref,PRIMARY,PRIMARY,4,dominfo.domain.domain_host_account,1,
1,PRIMARY,domain_host,eq_ref,PRIMARY,PRIMARY,4,dominfo.domain_host_account.host,1,
1,PRIMARY,content_host,eq_ref,PRIMARY,PRIMARY,4,dominfo.domain.content_host,1,
1,PRIMARY,domain_status,ALL,NULL,NULL,NULL,NULL,1544,
1,PRIMARY,status,eq_ref,PRIMARY,PRIMARY,4,dominfo.domain_status.status,1,
2,"DEPENDENT SUBQUERY",link,ALL,NULL,NULL,NULL,NULL,8703,"Using where"
2,"DEPENDENT SUBQUERY",client_site,eq_ref,PRIMARY,PRIMARY,4,dominfo.link.client_site,1,
2,"DEPENDENT SUBQUERY",client,eq_ref,PRIMARY,PRIMARY,4,dominfo.client_site.client,1,"Using where"
SHOW CREATE TABLE域名:
CREATE TABLE `domain` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(67) DEFAULT NULL,
`domain_host_account` int(11) DEFAULT NULL,
`content_host` int(11) DEFAULT NULL,
`ip` varchar(45) DEFAULT NULL,
`historic_content` tinytext,
`redirected` int(11) DEFAULT NULL,
`ftp_account` tinyint(1) DEFAULT ''0'',
`comment` tinytext,
`country` int(11) DEFAULT NULL,
`redirected_text` varchar(45) DEFAULT NULL,
`status_text` varchar(500) DEFAULT NULL,
`dhost_text` varchar(500) DEFAULT NULL,
`chost_text` varchar(500) DEFAULT NULL,
`category_text` varchar(150) DEFAULT NULL,
`dhost_acc_text` varchar(45) DEFAULT NULL,
`indexed` tinyint(1) DEFAULT NULL,
`indexed_checked` date DEFAULT NULL,
`origin` tinyint(1) DEFAULT ''0'',
PRIMARY KEY (`id`),
KEY `FK_domain_host_account_idx` (`domain_host_account`),
KEY `idx_domain_ip` (`ip`),
KEY `idx_domain_country` (`country`),
KEY `idx_domain_domain_host_account` (`domain_host_account`),
KEY `idx_domain_content_host` (`content_host`)
) ENGINE=InnoDB AUTO_INCREMENT=12598 DEFAULT CHARSET=latin1
该过程将执行0.06秒,而使用参数" dom_id",传递整数值1,将导致执行时间为5.070秒。有什么想法吗?
答案 0 :(得分:8)
根据这个问题,@ sboss想知道:
的行为The procedure will take 0.06s to execute with static int whereas using the parameter
"dom_id", passing integer value of 1, it will result in an execution
time of 5.070s.
如果我们看到mysql引擎如何工作,这个行为很容易理解。
MySQL引擎缓存查询和结果。查询缓存存储文本 一个SELECT语句以及相应的结果 发送给客户。如果稍后收到相同的陈述,则 服务器从查询缓存中检索结果而不是解析 并再次执行该声明。
因此,在您的情况下,最有可能在查询实际由mysql引擎解析和执行时找到execution time of 5.070s
,并且在从查询缓存中检索结果集时找到execution time of 0.06s
。
请详细参阅文档:https://dev.mysql.com/doc/refman/5.5/en/query-cache.html
答案 1 :(得分:5)
优化1
部分减速是重复执行的“依赖子查询”:
( SELECT IFNULL(GROUP_CONCAT(DISTINCT client.name SEPARATOR ', '), '-')
FROM link
LEFT JOIN client_site ON link.client_site = client_site.id
LEFT JOIN client ON client.id = client_site.client
WHERE link.from_domain = domain.id
) AS clients
根据EXPLAIN
,它必须每次扫描所有〜{8703行link
。
我不认为它可以在同一个查询中简化。相反,我认为这将是有用的:
CREATE TEMPORARY TABLE t_clients (
PRIMARY KEY(from_domain)
)
SELECT link.from_domain,
IFNULL(GROUP_CONCAT(DISTINCT client.name SEPARATOR ', '), '-')
FROM link
LEFT JOIN client_site ON link.client_site = client_site.id
LEFT JOIN client ON client.id = client_site.client;
然后
SELECT domain.id, IFNULL(domain.indexed, '-') AS indexed, domain.name,
country.language_code, IFNULL(ip_adress.adress, '-') AS adress,
IFNULL(GROUP_CONCAT(category.name SEPARATOR ', '), '-') AS categories,
IFNULL(GROUP_CONCAT(category.id SEPARATOR ', '), '-') AS categories_id,
t_clients.clients AS clients, -- Changed
IFNULL(domain_host.name, '-') AS domain_host_account,
IFNULL(content_host.name, '-') AS content_host,
status.id AS status, status.name AS status_name
FROM domain
LEFT JOIN t_clients ON t_clients.from_domain = domain.id -- Added
LEFT JOIN ip_adress ON domain.ip = ip_adress.id
LEFT JOIN domain_category ON domain.id = domain_category.domain
LEFT JOIN category ON domain_category.category = category.id
LEFT JOIN country ON domain.country = country.id
LEFT JOIN domain_host_account ON domain.domain_host_account = domain_host_account.id
LEFT JOIN domain_host ON domain_host_account.host = domain_host.id
LEFT JOIN content_host ON domain.content_host = content_host.id
LEFT JOIN domain_status ON domain.id = domain_status.domain
LEFT JOIN status ON domain_status.status = status.id
WHERE domain.country = dom_id
GROUP BY domain.id
ORDER BY domain.name;
您可以尝试PREPARE
方法是否更快。在我做过的一个(更简单的)测试中,它似乎并不重要。
优化2
另一个潜在的加速是在子查询中执行GROUP_CONCATs
而不是收集大量行,然后进行折叠。请注意,您必须使用GROUP BY
。这种技术可能能够消除这种情况。例如:
IFNULL(GROUP_CONCAT(category.name SEPARATOR ', '), '-') AS categories,
LEFT JOIN category ON ...
- >
IFNULL(
( SELECT GROUP_CONCAT(category.name SEPARATOR ', ')
FROM category
WHERE category.id = domain_category.category
),
'-') AS categories,
如果你对你的变体和我的变体这样做,可能的原因可以被观察到:
SELECT COUNT(*) FROM ( the select, but without the GROUP BY or ORDER BY );
您的变体将(假设许多类别等)将具有更大的COUNT
。这意味着您的查询正在构建一个更大的tmp表,以便提供给GROUP BY
和ORDER BY
。因此速度较慢。
优化3
如果你设法摆脱所有聚合(GROUP_CONCAT
),那么添加INDEX(country, name)
应该通过删除两个FILESORTs
来进一步优化它。
答案 2 :(得分:4)
如果你一直动态,我用测试不要覆盖原来的proc,同时确保你没有testing
proc已经存在
DROP PROCEDURE IF EXISTS `testing`;
DELIMITER //
CREATE PROCEDURE `testing`(IN `test` INT)
BEGIN
SET @id=test;
SET @query="SELECT
domain.id,
IFNULL(domain.indexed, '-') AS indexed,
domain.name,
country.language_code,
IFNULL(ip_adress.adress, '-') AS adress,
IFNULL(GROUP_CONCAT(category.name
SEPARATOR ', '),
'-') AS categories,
IFNULL(GROUP_CONCAT(category.id
SEPARATOR ', '),
'-') AS categories_id,
(SELECT
IFNULL(GROUP_CONCAT(DISTINCT client.name
SEPARATOR ', '),
'-')
FROM
link
LEFT JOIN
client_site ON link.client_site = client_site.id
LEFT JOIN
client ON client.id = client_site.client
WHERE
link.from_domain = domain.id) AS clients,
IFNULL(domain_host.name, '-') AS domain_host_account,
IFNULL(content_host.name, '-') AS content_host,
status.id AS status,
status.name AS status_name
FROM
domain
LEFT JOIN
ip_adress ON domain.ip = ip_adress.id
LEFT JOIN
domain_category ON domain.id = domain_category.domain
LEFT JOIN
category ON domain_category.category = category.id
LEFT JOIN
country ON domain.country = country.id
LEFT JOIN
domain_host_account ON domain.domain_host_account = domain_host_account.id
LEFT JOIN
domain_host ON domain_host_account.host = domain_host.id
LEFT JOIN
content_host ON domain.content_host = content_host.id
LEFT JOIN
domain_status ON domain.id = domain_status.domain
LEFT JOIN
status ON domain_status.status = status.id
WHERE
domain.country = ?
GROUP BY domain.id
ORDER BY domain.name;"
PREPARE sqlquery FROM @query;
EXECUTE sqlquery USING @id;
END;
//
DELIMITER ;
然后使用
CALL `testing`(1);
答案 3 :(得分:0)
您在这里说,您可以在使用WHERE时快速获取数据 domain.country = 1 代替 哪里 domain.country = dom_id 但我无法在您的查询中看到domain.country上的where子句。你能澄清一下吗。
但总的来说发生了什么
如果您正在寻找其他任何内容,请与我们联系。
答案 4 :(得分:0)
首先清楚你的疑问,如果通过参数传递值,查询会花费时间。正如Rick和Jaydatt在帖子中提到的,可能是由于查询缓存,所以你可以先用额外的SQL_NO_CACHE关键字SELECT SQL_NO_CACHE domain.id, IFNULL(domain.indexed, '-') AS indexed,....
我想现在你应该得到约。同时由两个查询。
进一步优化此查询,即使Rick为您提供了许多有用的选项,您也可以选择以下选项。
首先检查link.from_domain字段是否已编入索引,因为解释显示所有行都是从链接表中扫描。
您还可以使用以下方法检查查询效果 -
SELECT
domain.id,
IFNULL(domain.indexed, '-') AS indexed,
domain.name,
country.language_code,
IFNULL(ip_adress.adress, '-') AS adress,
IFNULL(GROUP_CONCAT(category.name
SEPARATOR ', '),
'-') AS categories,
IFNULL(GROUP_CONCAT(category.id
SEPARATOR ', '),
'-') AS categories_id,
(SELECT
IFNULL(GROUP_CONCAT(DISTINCT client.name
SEPARATOR ', '),
'-')
FROM
link
LEFT JOIN
client_site ON link.client_site = client_site.id
LEFT JOIN
CLIENT ON client.id = client_site.client
WHERE
link.from_domain = domain.id) AS clients,
IFNULL(domain_host.name, '-') AS domain_host_account,
IFNULL(content_host.name, '-') AS content_host,
status.id AS STATUS,
status.name AS status_name
FROM
(
SELECT id,indexed,`name`,ip,country,domain_host_account,content_host
FROM domain
WHERE country = dom_id
) AS domain
LEFT JOIN
ip_adress ON domain.ip = ip_adress.id
LEFT JOIN
domain_category ON domain.id = domain_category.domain
LEFT JOIN
category ON domain_category.category = category.id
LEFT JOIN
country ON domain.country = country.id
LEFT JOIN
domain_host_account ON domain.domain_host_account = domain_host_account.id
LEFT JOIN
domain_host ON domain_host_account.host = domain_host.id
LEFT JOIN
content_host ON domain.content_host = content_host.id
LEFT JOIN
domain_status ON domain.id = domain_status.domain
LEFT JOIN
STATUS ON domain_status.status = status.id
GROUP BY domain.id
ORDER BY domain.name;