explain select * from users u join wallet w on w.userId=u.uuid where w.userId='8319611142598331610'; //Index is taken
explain select * from users u join wallet w on w.userId=u.uuid where w.currencyId=8; //index is not taken
从上面可以看出,索引userIdIdx用于后一种情况,但不使用前一种情况。
以下是两个表的架构-
CREATE TABLE `users` (
`uuid` varchar(600) DEFAULT NULL,
KEY `uuidIdx` (`uuid`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `wallet` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`userId` varchar(200) NOT NULL DEFAULT '',
`currencyId` int(11) NOT NULL,
PRIMARY KEY (`Id`),
KEY `userIdIdx` (`userId`),
KEY `currencyIdIdx` (`currencyId`)
) ENGINE=InnoDB AUTO_INCREMENT=279668 DEFAULT CHARSET=latin1;
如何强制MySql考虑userIdIdx
或uuidIdx
索引?
答案 0 :(得分:1)
有两种方法可以改善此问题。
方法1:
为两个查询添加多列索引wallet(userId, currencyId)
似乎更好。
参见演示https://www.db-fiddle.com/f/aesNYevEzwopmXrnQJRPoS/0
方法2
重写查询。
这适用于当前的表结构。
查询
SELECT
*
FROM (
SELECT
wallet.userId
FROM
wallet
WHERE
wallet.currencyId = 8
) AS wallet
INNER JOIN
users
ON
wallet.userId = users.uuid
参见演示https://www.db-fiddle.com/f/aesNYevEzwopmXrnQJRPoS/3
p.s我还建议您在将InnoDB用作表引擎时也向用户表中添加Id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY
。
我的这篇帖子解释了为什么https://dba.stackexchange.com/a/48184/27070
答案 1 :(得分:0)
这两个查询在您提供的内容上都尽力而为。
select *
from users u
join wallet w ON w.userId=u.uuid
where w.userId='8319611142598331610';
select *
from users u
join wallet w ON w.userId=u.uuid
where w.currencyId=8;
如果表中只有一行(例如users
),则优化器采用不同的路径。这似乎是第一个查询发生的情况。
否则,由于正在进行过滤,两个查询都将以wallet
开头。 wallet
中的每个辅助键对于其中一个查询都很方便。甚至会更好
INDEX(userId, currencyId, id) -- for first query
INDEX(currencyId, userId, id) -- for second query
第一列用于WHERE
;其他两列使索引“覆盖”,因此它不需要在索引和数据之间反弹。
(Geez,这些表的列数很少。)
在w
中进行过滤后,它将移至u
并使用INDEX(uuid)
。由于那是表中的唯一列(没有名称?),它可以是“正在使用索引”,即“正在覆盖”。
进入u
的唯一原因是验证是否存在一个值与w.userId
匹配的用户。既然您可能总是这样,为什么在查询中从JOIN
到users
呢?