MySQL多连接速度非常慢
这是一个问题,我不确定根本原因可能在哪里,所以我将提供详细信息和我想到的问题点。任何帮助都会很棒(如果你住在附近,我就请你喝啤酒)。我有这三张桌子: 实践:MySQL多连接速度非常慢,mysql,database,performance,Mysql,Database,Performance,这是一个问题,我不确定根本原因可能在哪里,所以我将提供详细信息和我想到的问题点。任何帮助都会很棒(如果你住在附近,我就请你喝啤酒)。我有这三张桌子: 实践: `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(125) NOT NULL, `description` text, `deleted` int(11) unsigned DEFAULT NULL, `created` timestamp NOT NULL DEFA
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(125) NOT NULL,
`description` text,
`deleted` int(11) unsigned DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(70) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_update_by` varchar(70) NOT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`)
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`practice_fk` int(11) unsigned NOT NULL,
`phone` char(12) DEFAULT NULL,
`fax` char(12) DEFAULT NULL,
`address` varchar(125) DEFAULT NULL,
`address_two` varchar(125) DEFAULT NULL,
`city` varchar(40) NOT NULL,
`state` char(2) NOT NULL,
`zip` char(5) DEFAULT NULL,
`lat` decimal(7,5) DEFAULT NULL,
`lng` decimal(7,5) DEFAULT NULL,
`deleted` int(11) unsigned DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(70) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_update_by` varchar(70) NOT NULL,
`email` varchar(150) DEFAULT NULL,
`practice_name_temp` text,
PRIMARY KEY (`id`)
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`location_fk` int(11) unsigned NOT NULL,
`practice_fk` int(11) unsigned NOT NULL,
`fname` varchar(25) NOT NULL,
`lname` varchar(45) NOT NULL,
`phone` varchar(35) DEFAULT NULL,
`mobile` char(12) DEFAULT NULL,
`email` varchar(125) DEFAULT NULL,
`title` varchar(100) DEFAULT NULL,
`description` text,
`deleted` int(11) unsigned DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(70) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_update_by` varchar(70) NOT NULL,
PRIMARY KEY (`id`)
位置:
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(125) NOT NULL,
`description` text,
`deleted` int(11) unsigned DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(70) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_update_by` varchar(70) NOT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`)
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`practice_fk` int(11) unsigned NOT NULL,
`phone` char(12) DEFAULT NULL,
`fax` char(12) DEFAULT NULL,
`address` varchar(125) DEFAULT NULL,
`address_two` varchar(125) DEFAULT NULL,
`city` varchar(40) NOT NULL,
`state` char(2) NOT NULL,
`zip` char(5) DEFAULT NULL,
`lat` decimal(7,5) DEFAULT NULL,
`lng` decimal(7,5) DEFAULT NULL,
`deleted` int(11) unsigned DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(70) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_update_by` varchar(70) NOT NULL,
`email` varchar(150) DEFAULT NULL,
`practice_name_temp` text,
PRIMARY KEY (`id`)
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`location_fk` int(11) unsigned NOT NULL,
`practice_fk` int(11) unsigned NOT NULL,
`fname` varchar(25) NOT NULL,
`lname` varchar(45) NOT NULL,
`phone` varchar(35) DEFAULT NULL,
`mobile` char(12) DEFAULT NULL,
`email` varchar(125) DEFAULT NULL,
`title` varchar(100) DEFAULT NULL,
`description` text,
`deleted` int(11) unsigned DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(70) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_update_by` varchar(70) NOT NULL,
PRIMARY KEY (`id`)
联系人:
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(125) NOT NULL,
`description` text,
`deleted` int(11) unsigned DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(70) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_update_by` varchar(70) NOT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`)
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`practice_fk` int(11) unsigned NOT NULL,
`phone` char(12) DEFAULT NULL,
`fax` char(12) DEFAULT NULL,
`address` varchar(125) DEFAULT NULL,
`address_two` varchar(125) DEFAULT NULL,
`city` varchar(40) NOT NULL,
`state` char(2) NOT NULL,
`zip` char(5) DEFAULT NULL,
`lat` decimal(7,5) DEFAULT NULL,
`lng` decimal(7,5) DEFAULT NULL,
`deleted` int(11) unsigned DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(70) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_update_by` varchar(70) NOT NULL,
`email` varchar(150) DEFAULT NULL,
`practice_name_temp` text,
PRIMARY KEY (`id`)
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`location_fk` int(11) unsigned NOT NULL,
`practice_fk` int(11) unsigned NOT NULL,
`fname` varchar(25) NOT NULL,
`lname` varchar(45) NOT NULL,
`phone` varchar(35) DEFAULT NULL,
`mobile` char(12) DEFAULT NULL,
`email` varchar(125) DEFAULT NULL,
`title` varchar(100) DEFAULT NULL,
`description` text,
`deleted` int(11) unsigned DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(70) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_update_by` varchar(70) NOT NULL,
PRIMARY KEY (`id`)
架构背后的基本思想是有一个实践列表。一个机构可以有多个位置,但一个位置如果不与一个机构关联,就不能存在。然后,一个机构也可以有多个联系人,但是一个联系人必须与一个机构和一个位置相关联。[这就是问题的一部分可能开始的地方]。因此,我有一个疑问:
SELECT DISTINCT p.id AS practice_id,
p.name,
l.id AS location_id,
address AS location_address,
l.phone AS disp_phone,
CONCAT(pc.fname, ' ', pc.lname) AS practiceContact,
CONCAT(lc.fname, ' ', lc.lname) AS locationContact,
pcc.qty AS practice_only_contact_qty,
lcc.qty AS location_contact_qty,
(pcc.qty + lcc.qty) AS contactQty
FROM practices p
LEFT JOIN practice_locations l on l.practice_fk=p.id
LEFT JOIN (
SELECT count(id) AS qty, practice_fk
FROM practice_contacts
GROUP BY practice_fk
) pcc ON pcc.practice_fk=p.id
LEFT JOIN practice_contacts pc ON pc.practice_fk=pcc.practice_fk AND pcc.qty=1
LEFT JOIN (
SELECT count(id) AS qty, location_fk
FROM practice_contacts
GROUP BY location_fk
) lcc ON lcc.location_fk=l.id
LEFT JOIN practice_contacts lc ON lc.location_fk=lcc.location_fk AND lcc.qty=1
WHERE p.name IS NOT NULL AND p.deleted IS NULL
GROUP BY p.id
ORDER BY p.name ASC, l.state, l.city, l.address;
这个查询应该做的是:
- 收集机构ID和名称
- 如果只有一个位置,请抓取它的地址。否则,获取第一个位置的地址
- 如果有一个联系人与该机构相关,请记下他们的姓名。否则,请获取第一个联系人的姓名
- 计算有多少联系人与该机构关联
- 计算与该机构关联的位置数
- 根据机构ID将所有这些信息分组,然后根据机构名称按字母顺序排列,然后按位置排列
- 第一部分:我应该引入一个参考表[有点像视图]来存储这些关系吗
但是如果我这样做,我不确定如何构造查询以根据需要提取数据?它是否会提供任何性能优势`id` int(11) unsigned not null, `practice_fk` int(11) unsigned not null, `location_fk` int(11) unsigned not null, `contact_fk` int(11) unsigned not null, PRIMARY KEY(id), KEY(practice_fk), KEY(location_fk), KEY(contact_fk)
- 第二部分:我没有适当的索引。在浏览了MySQL文档并结结巴巴地阅读了这篇文章()之后,我开始明白InnoDB是一只慢猪。从用户体验的角度来看,这是不可接受的,但从架构的角度来看,我被锁定在这个引擎中。如何正确设置索引以使查询返回到次秒范围
- 第三部分:我的查询是垃圾。我认为这可能是最大的罪魁祸首。我仍在学习如何构造这些更复杂的SQL查询,这本身需要一些努力,因此任何关于如何使这件事不那么像猪的指针都会很好
我对我的查询尝试了各种各样的操作(删除groupby、删除orderby等等),几乎没有任何变化。查询始终以28到33秒的速度运行。任何指导都将不胜感激。您可以使用行限制语法始终返回第一行,而不是计算联系人和练习的数量。我不能保证这将有助于表现,但这是两个少加入担心
LEFT JOIN (
SELECT *
FROM practice_contacts
GROUP BY practice_fk LIMIT 1
) pcc ON pcc.practice_fk=p.id
LEFT JOIN (
SELECT *
FROM practice_locations
GROUP BY practicec_fk LIMIT 1
) lcc ON lcc.practice_fk = p.id
我没有验证sql是否可以工作,但是您知道了。如果需要特定的联系人或位置(例如,最近的联系人或位置),可以在子选择中包含ORDER BY子句
请参见并非所有这些问题都可以“修复”,但它们会向我发出性能危险信号:
- 不要将
和不同的
混在一起。他们也会做同样的事情分组方式
- 不要使用InnoDB;你引用的链接遭到了断然驳斥——作者承认了这一点
- 如果
满足您的需求,请不要使用JOIN
LEFT JOIN
表示“right”表可能缺少行LEFT
通常无法优化,但左连接(选择…
连接可能会优化
- 这尤其低效:
(选择…)JOIN(选择…)
- “爆炸内爆”:
增加行数<代码>分组依据然后放气。这是性能问题的常见原因。(也许我可以更具体地说。)连接
检查COUNT(x)
是否为x
。通常,您真正想要的是NULL
COUNT(*)
左连接时的“正确性”,请仅在中的中设置“连接”条件
;将“筛选”条件放入中的。我认为和pcc.qty=1
应该从ON
移动到WHERE
。(我认为这可能会改变结果集。)
可能的索引:
p: INDEX(deleted, name, id)
l: INDEX(practice_fk)
执行解释选择…
。如果你没有看到“自动键”,那么你有一个旧版本的MySQL;考虑升级。“自动键”表示我对(选择…)加入(选择…
的评论不适用。否则,考虑两个<代码>创建临时表< /代码>,并在<代码>……fk < /COD>中添加索引。然后使用tmp表,而不是左连接(选择…
两次
对我的评论尽你所能,然后带着修改后的查询回来,再加上解释以获得进一步的评论(如果必要的话)
关于创建索引的更多信息:看来您实际上不需要按子查询中的任何内容排序。所以,您可以显式地按NULL设置ORDER,以提高子查询的性能
修改的查询:
SELECT
DISTINCT p.id AS practice_id,
p.name,
l.id AS location_id,
l.address AS location_address,
l.phone AS disp_phone,
CONCAT(pc.fname,
' ',
pc.lname) AS practiceContact,
CONCAT(lc.fname,
' ',
lc.lname) AS locationContact,
pcc.qty AS practice_only_contact_qty,
lcc.qty AS location_contact_qty,
(pcc.qty + lcc.qty) AS contactQty
FROM
practices p
LEFT JOIN
practice_locations l
ON l.practice_fk = p.id
LEFT JOIN
(
SELECT
COUNT(practice_contacts.id) AS qty,
practice_contacts.practice_fk
FROM
practice_contacts
GROUP BY
practice_contacts.practice_fk
ORDER BY
NULL
) pcc
ON pcc.practice_fk = p.id
LEFT JOIN
practice_contacts pc
ON pc.practice_fk = pcc.practice_fk
AND pcc.qty = 1
LEFT JOIN
(
SELECT
COUNT(practice_contacts.id) AS qty,
practice_contacts.location_fk
FROM
practice_contacts
GROUP BY
practice_contacts.location_fk
ORDER BY
NULL
) lcc
ON lcc.location_fk = l.id
LEFT JOIN
practice_contacts lc
ON lc.location_fk = lcc.location_fk
AND lcc.qty = 1
WHERE
p.name IS NOT NULL
AND p.deleted IS NULL
GROUP BY
p.id
ORDER BY
p.name ASC,
l.state,
l.city,
l.address
另外,添加以下索引可能会优化查询:
ALTER TABLE `practices` ADD INDEX `practices_index_1` (`deleted`,`id`,`name`);
ALTER TABLE `practice_contacts` ADD INDEX `practice_contacts_index_1` (`practice_fk`,`location_fk`);
ALTER TABLE `practice_contacts` ADD INDEX `practice_contacts_index_2` (`location_fk`);
ALTER TABLE `practice_locations` ADD INDEX `practice_locations_index_1` (`practice_fk`,`id`);
你是否在你的练习之间创建了外键,locat