MySQL 索引未按预期使用
Posted
技术标签:
【中文标题】MySQL 索引未按预期使用【英文标题】:MySQL index not being used as expected 【发布时间】:2013-05-03 02:43:46 【问题描述】:如何让 mysql 使用预期的索引?
我有 4 个表,其中两个包含资源,另一个包含历史变化。
一对正确使用索引,另一对没有,但两者的结构几乎相同。
我尝试更改主键的顺序和其他键的顺序,我尝试更改表结构,以便它们在两个表中使用相同的名称,并且都具有相同的键名,但没有似乎使查询使用了正确的索引。
为简洁起见,已删除不相关的列。
这两个表按预期工作。
CREATE TABLE `players` (
`player_id` varbinary(36) NOT NULL DEFAULT '',
`pop_rank_score` double NOT NULL DEFAULT '0',
PRIMARY KEY (`player_id`),
KEY `pop_rank_score` (`pop_rank_score`),
KEY `weblinc_id` (`weblinc_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `poprankhistory` (
`day_id` int(11) NOT NULL,
`player_id` varbinary(36) NOT NULL DEFAULT '',
`total` double NOT NULL DEFAULT '0',
`today` double NOT NULL DEFAULT '0',
PRIMARY KEY (`day_id`,`player_id`),
KEY `day_id` (`day_id`),
KEY `player_id` (`player_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
explain select p.`player_id`, p.pop_rank_score + 0.5 * COALESCE(h1.total,0) as pop_rank_score
from fpme_lua.`Players` p, fpme_lua.PopRankHistory h1
where ( p.`player_id` = h1.`player_id` AND h1.day_id = (SELECT Max(h2.day_id) AS day_id
FROM fpme_lua.poprankhistory h2
WHERE h2.day_id <= 15786 and h2.player_id = p.`player_id` ));
+----+--------------------+-------+--------+--------------------------+-----------+---------+-----------------------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+--------+--------------------------+-----------+---------+-----------------------+-------+--------------------------+
| 1 | PRIMARY | h1 | ALL | PRIMARY,day_id,player_id | NULL | NULL | NULL | 25391 | |
| 1 | PRIMARY | p | eq_ref | PRIMARY | PRIMARY | 38 | fpme_lua.h1.player_id | 1 | Using where |
| 2 | DEPENDENT SUBQUERY | h2 | ref | PRIMARY,day_id,player_id | player_id | 38 | fpme_lua.p.player_id | 2 | Using where; Using index |
+----+--------------------+-------+--------+--------------------------+-----------+---------+-----------------------+-------+--------------------------+
这些表未按预期工作(必需)。
CREATE TABLE `pictures` (
`id` varchar(36) NOT NULL DEFAULT '',
`pcr_score` double NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `owner_id` (`owner_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE `picpcrhistory` (
`day_id` int(11) NOT NULL,
`target_id` varchar(36) NOT NULL DEFAULT '',
`total` double NOT NULL DEFAULT '0',
`today` double NOT NULL DEFAULT '0',
PRIMARY KEY (`day_id`,`target_id`),
KEY `target_id` (`target_id`),
KEY `day_id` (`day_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
explain select p.`id`, p.pcr_score + 0.5 * COALESCE(h1.total,0) as pcr_score
from fpme_lua.`Pictures` p, fpme_lua.PicPcrHistory h1
where ( p.`id` = h1.`target_id` AND h1.day_id = (SELECT Max(h2.day_id) AS day_id
FROM fpme_lua.PicPcrHistory h2
WHERE h2.day_id <= 15786 and h2.`target_id` = p.`id` ));
+----+--------------------+-------+--------+----------------+---------+---------+------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+--------+----------------+---------+---------+------+-------+--------------------------+
| 1 | PRIMARY | h1 | ALL | PRIMARY,day_id | NULL | NULL | NULL | 65310 | |
| 1 | PRIMARY | p | eq_ref | PRIMARY | PRIMARY | 110 | func | 1 | Using where |
| 2 | DEPENDENT SUBQUERY | h2 | range | PRIMARY,day_id | day_id | 4 | NULL | 21824 | Using where; Using index |
+----+--------------------+-------+--------+----------------+---------+---------+------+-------+--------------------------+
【问题讨论】:
第一个查询包括 pk,并且两个索引都是 as possible_keys,但是第二个查询不包括 target_id,这是我想要使用的,即使我已经指定了两个键where 子句。 【参考方案1】:这些表有不同的字符集,这就是索引不起作用的原因。 我更改了字符集,因此两者都是 utf8 并为三列添加了索引。
ALTER TABLE `fpme_lua`.`colpcrhistory` CHARACTER SET = utf8 ;
ALTER TABLE `fpme_lua`.`picpcrhistory` CHARACTER SET = utf8 ;
ALTER TABLE `fpme_lua`.`picpcrhistory`
ADD INDEX `indx_tar_day_tot` USING BTREE (`target_id` ASC, `day_id` ASC, `total` ASC) ;
然后我将查询更改为...
SELECT p.id,
p.pcr_score + 0.5 * COALESCE(h1.total,0) AS pcr_score
FROM
fpme_lua.Pictures AS p
JOIN
fpme_lua.PicPcrHistory AS h1
ON h1.target_id = p.id
JOIN
( SELECT hh.target_id,
Max(hh.day_id) AS day_id
FROM fpme_lua.PicPcrHistory AS hh
WHERE hh.day_id <= 15786
GROUP BY hh.target_id
) AS h2
ON h2.target_id = h1.target_id
AND h2.day_id = h1.day_id ;
【讨论】:
啊,很好,感谢您记录它。 (好吧,你实际上并没有接受你的答案,所以我们仍然不能 100% 确定。;) 【参考方案2】:如果要使用特定索引,可以在表名后使用FORCE INDEX(index_name)
。
尽管 MySQL 应该足够聪明地选择“最佳”索引。
【讨论】:
以上是关于MySQL 索引未按预期使用的主要内容,如果未能解决你的问题,请参考以下文章