优化 MySQL 计数查询,提供完整信息
Posted
技术标签:
【中文标题】优化 MySQL 计数查询,提供完整信息【英文标题】:Optimize MySQL count query, full info provided 【发布时间】:2013-02-07 10:58:35 【问题描述】:我的这个查询运行了 3 分钟以上。我希望优化以在几秒钟内运行:
SELECT Count(DISTINCT l.licitatii_id) c
FROM licitatii_ue l
INNER JOIN domenii_licitatii dl
ON l.licitatii_id = dl.licitatii_id
AND dl.tip_licitatie = '2'
INNER JOIN domenii d
ON dl.domenii_id = d.domenii_id
AND d.tip_domeniu = '1'
AND d.status = 1
WHERE l.status = 1
AND l.data_limita >= '1357768800'
解释显示:
*** row 1 ***
table: d
type: ref
possible_keys: PRIMARY,key_status_tip_domeniu,ind_v1
key: key_status_tip_domeniu
key_len: 9
ref: const,const
rows: 39
Extra: Using where; Using index
*** row 2 ***
table: dl
type: ref
possible_keys: PRIMARY,licitatii_id,licitatii_id_2
key: PRIMARY
key_len: 5
ref: web1db1.d.domenii_id,const
rows: 1882
Extra: Using index
*** row 3 ***
table: l
type: eq_ref
possible_keys: PRIMARY,data_limita
key: PRIMARY
key_len: 4
ref: web1db1.dl.licitatii_id
rows: 1
Extra: Using where
架构:
licitatii_ue
PRIMARY KEY (`licitatii_id`),
UNIQUE KEY `nr_ue` (`nr_ue`),
KEY `nume` (`nume`),
KEY `tip_licitatie` (`tip_licitatie`),
KEY `surse_ue_id` (`surse_ue_id`),
KEY `data_publicarii` (`data_publicarii`),
KEY `tara_id` (`tara_id`),
KEY `creat` (`creat`),
KEY `data_limita` (`data_limita`),
FULLTEXT KEY `nume_fulltext` (`nume`,`descriere`)
) ENGINE=MyISAM
/*Index Information*/
---------------------
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
------------ ---------- --------------- ------------ --------------- --------- ----------- -------- ------ ------ ---------- ---------
licitatii_ue 0 PRIMARY 1 licitatii_id A 136456 (NULL) (NULL) BTREE
licitatii_ue 0 nr_ue 1 nr_ue A (NULL) (NULL) (NULL) YES BTREE
licitatii_ue 1 nume 1 nume A 68228 (NULL) (NULL) BTREE
licitatii_ue 1 tip_licitatie 1 tip_licitatie A 13 (NULL) (NULL) YES BTREE
licitatii_ue 1 surse_ue_id 1 surse_ue_id A 1 (NULL) (NULL) BTREE
licitatii_ue 1 data_publicarii 1 data_publicarii A 136456 (NULL) (NULL) YES BTREE
licitatii_ue 1 tara_id 1 tara_id A 41 (NULL) (NULL) YES BTREE
licitatii_ue 1 creat 1 creat A 136456 (NULL) (NULL) YES BTREE
licitatii_ue 1 data_limita 1 data_limita A 770 (NULL) (NULL) YES BTREE
licitatii_ue 1 nume_fulltext 1 nume (NULL) 1 (NULL) (NULL) FULLTEXT
licitatii_ue 1 nume_fulltext 2 descriere (NULL) 1 (NULL) (NULL) YES FULLTEXT
domenii_licitatii
PRIMARY KEY (`domenii_id`,`tip_licitatie`,`licitatii_id`),
KEY `licitatii_id` (`licitatii_id`,`tip_licitatie`),
KEY `licitatii_id_2` (`licitatii_id`)
) ENGINE=InnoDB
/*Index Information*/
---------------------
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
----------------- ---------- -------------- ------------ ------------- --------- ----------- -------- ------ ------ ---------- ---------
domenii_licitatii 0 PRIMARY 1 domenii_id A 20 (NULL) (NULL) BTREE
domenii_licitatii 0 PRIMARY 2 tip_licitatie A 228 (NULL) (NULL) BTREE
domenii_licitatii 0 PRIMARY 3 licitatii_id A 430634 (NULL) (NULL) BTREE
domenii_licitatii 1 licitatii_id 1 licitatii_id A 430634 (NULL) (NULL) BTREE
domenii_licitatii 1 licitatii_id 2 tip_licitatie A 430634 (NULL) (NULL) BTREE
domenii_licitatii 1 licitatii_id_2 1 licitatii_id A 430634 (NULL) (NULL) BTREE
domenii
PRIMARY KEY (`domenii_id`),
KEY `key_status_tip_domeniu` (`status`,`tip_domeniu`),
KEY `ind_v1` (`domenii_id`,`tip_domeniu`,`status`),
KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB
/*Index Information*/
---------------------
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
------- ---------- ---------------------- ------------ ----------- --------- ----------- -------- ------ ------ ---------- ---------
domenii 0 PRIMARY 1 domenii_id A 79 (NULL) (NULL) BTREE
domenii 1 key_status_tip_domeniu 1 status A 4 (NULL) (NULL) YES BTREE
domenii 1 key_status_tip_domeniu 2 tip_domeniu A 11 (NULL) (NULL) BTREE
domenii 1 ind_v1 1 domenii_id A 79 (NULL) (NULL) BTREE
domenii 1 ind_v1 2 tip_domeniu A 79 (NULL) (NULL) BTREE
domenii 1 ind_v1 3 status A 79 (NULL) (NULL) YES BTREE
domenii 1 parent_id 1 parent_id A 79 (NULL) (NULL) BTREE
【问题讨论】:
【参考方案1】:您的表确实需要复合索引。
table index
licitatii ( status, data_limita, licitatii_id )
domenii_licitatii ( licitatii_id, tip_licitatie )
domenii ( domenii_id, tip_domeniu, status )
您应该拥有有助于优化联接 AND where 条件的复合键。此外,正如我将它们包含在内,查询引擎可以直接从索引中找到连接的值,而不是返回到实际数据的页面。如果索引中只有一列,则查询将转到原始数据进行连接。看看它对你有什么作用。
好的,是的,其他两个索引和您添加的第三个索引 - 没有帮助。然后我会尝试反转查询......不完全了解数据,但看起来它们是某种类型的查找类别元素。试试
select STRAIGHT_JOIN
Count(DISTINCT dl.licitatii_id) c
from
domenii d
join domenii_licitatii dl
on d.domenii_id = dl.domenii_id
AND dl.tip_licitatie = '2'
JOIN licitatii_ue L
on dl.licitatii_id = L.licitatii_id
AND L.status = 1
AND L.Data_Limita >= '1357768800'
where
d.tip_domeniu = '1'
and d.status = 1
由 Pentium10 添加
解释显示如下:
+---+--------+----+--------+---------------------------------------+------------------------+---+----------------------------+------+--------------------------+
| 1 | SIMPLE | d | ref | PRIMARY,key_status_tip_domeniu,ind_v1 | key_status_tip_domeniu | 9 | const,const | 39 | Using where; Using index |
| 1 | SIMPLE | dl | ref | PRIMARY,licitatii_id,licitatii_id_2 | PRIMARY | 5 | web1db1.d.domenii_id,const | 1882 | Using index |
| 1 | SIMPLE | L | eq_ref | PRIMARY,data_limita,i1 | PRIMARY | 4 | web1db1.dl.licitatii_id | 1 | Using where |
+---+--------+----+--------+---------------------------------------+------------------------+---+----------------------------+------+--------------------------+
【讨论】:
我添加了第一个索引,另一个也已经存在。它没有帮助。它根本没有被捡起。 @Pentium10,查看修改后的选项 我已将解释添加到您的答案中。仍然只拾取主索引。重写查询背后的逻辑是什么? @Pentium10,在不知道关于在什么级别/条件下有多少记录的数据的情况下,我试图强制使用一种替代方法,该方法可能在第一个表位置具有较小的记录集。这和使用 STRAIGHT_JOIN 在过去帮助许多查询覆盖优化器想要做的事情。有时有效,有时无效,但只是针对数据的另一种方法。【参考方案2】:这里我把末尾的WHERE
子句移到了开头的子查询中,这样就不用过滤更多末尾INNER JOIN
s创建的行了,已经及时正确过滤了对于第一个INNER JOIN
。这会运行 2 个查询,但应该会更快。
SELECT Count(DISTINCT l.licitatii_id) c
FROM (SELECT DISTINCT licitatii_id
FROM licitatii_ue l
WHERE l.status = 1
AND l.data_limita >= '1357768800'
) l
INNER JOIN domenii_licitatii dl
ON l.licitatii_id = dl.licitatii_id
AND dl.tip_licitatie = '2'
INNER JOIN domenii d
ON dl.domenii_id = d.domenii_id
AND d.tip_domeniu = '1'
AND d.status = 1
【讨论】:
这没有用,它运行了 4 分 56 秒。 原始WHERE
子句是否有组合索引?看起来他们在INNER JOIN
s 中的其他列中。尝试在表licitatii_ue
中的l.status
和l.data_limita
上创建组合索引,然后再次运行上述操作。以上是关于优化 MySQL 计数查询,提供完整信息的主要内容,如果未能解决你的问题,请参考以下文章