Mysql 范围检查而不是内连接上的索引使用

问题描述 投票:0回答:3

我在使用 MySQL (innoDB) 5.0 时遇到严重问题。

一个非常简单的 SQL 查询是用一个非常意外的查询计划执行的。

查询:

SELECT 
SQL_NO_CACHE
mbCategory.*

FROM 
MBCategory mbCategory 

INNER JOIN ResourcePermission as rp
    ON rp.primKey = mbCategory.categoryId 

where mbCategory.groupId = 12345 AND mbCategory.parentCategoryId = 0 
limit 20;

MBCategory - 包含 216583 行

ResourcePermission - 包含 3098354 行。

在 MBCategory 中,我有多个索引(列顺序与索引中相同):

Primary (categoryId)
A (groupId,parentCategoryId,categoryId)
B (groupId,parentCategoryId)

在 ResourcePermission 中,我有多个索引(列顺序与索引中相同):

Primary - on some column
A (primKey).

当我查看查询计划时,Mysql 更改表顺序并首先从 ResourcePermission 中选择行,然后它加入 MBCategory 表(疯狂的想法),这需要很长时间。所以我添加了

STRAIGHT_JOIN
来强制innodb引擎使用正确的表顺序:

SELECT

STRAIGHT_JOIN SQL_NO_CACHE
mbCategory.*

FROM 
MBCategory
 mbCategory 

INNER JOIN ResourcePermission as rp
    ON rp.primKey = mbCategory.categoryId 

where mbCategory.groupId = 12345 AND mbCategory.parentCategoryId = 0 
limit 20;

但是这里有第二个问题: 在我看来,mysql应该在连接操作上使用

index A (primKey)
,而不是对每条记录执行范围检查(索引映射:0x400),这又需要很长时间! 强制索引没有帮助,mysql仍然对每条记录执行范围检查。

MBCategory 中只有 23 行满足 where 条件,连接后只有 75 行。 我怎样才能让mysql在此操作上选择正确的索引?

mysql indexing range inner-join
3个回答
54
投票

好的, 基本问题。 我欠自己一杯啤酒。 我最近调整的系统不是我开发的系统 - 我的管理层指派我使用它来提高性能(原始团队对此主题不了解)。

经过数周的改进 SQL 查询、索引、应用程序正在执行的 SQL 查询数量后,我没有检查本例中最重要的事情之一!!

柱类型不同!

编写过此类代码的开发人员应该得到很大的讨论。

感谢您的帮助!


3
投票

我遇到了同样的问题,但原因不同。我正在加入一个大表,ON 子句使用 OR 将主键 (ii.itemid) 与两个不同的列进行比较:

SELECT *
FROM share_detail sd
JOIN box_view bv ON sd.container_id = bv.id
JOIN boxes b ON b.id = bv.shared_id
JOIN item_index ii ON ii.itemid = bv.shared_id OR b.parent_itemid = ii.itemid;

幸运的是,事实证明parent_itemid比较是多余的,所以我能够删除它。现在该索引正在按预期使用。否则,我将尝试将 item_index 连接拆分为两个单独的连接。


0
投票

对 @Wojtek Rudziński 的绝对正确答案的一点补充:

就我而言,原因是 varchar 列的字符集/排序规则不同。也检查一下。

© www.soinside.com 2019 - 2024. All rights reserved.