【问题标题】:Mysql range check instead of index usage on inner joinMysql 范围检查而不是内部连接上的索引使用情况
【发布时间】:2012-10-26 13:58:45
【问题描述】:

我在使用 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;

但是这里的第二个问题materialzie: 在我看来,mysql 应该在连接操作中使用index A (primKey),而不是对每条记录(索引映射:0x400)执行范围检查,这又需要很长时间! 强制索引没有帮助,mysql 仍在为每条记录执行 Range 检查。

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

【问题讨论】:

  • 我认为 MySQL 无法直接确定它必须读取多少行才能找到 20 个匹配的行;因此,它为 20 个匹配项选择较大的表,然后执行内部连接。如果取消限制会怎样?
  • 删除限制不会改变任何事情。我只需要 mysql 使用正确的索引而不是范围检查

标签: mysql indexing range inner-join


【解决方案1】:

好的, 初级问题。 我欠自己一杯啤酒。 我最近调整的系统不是我开发的系统 - 我的管理层已分配给我以提高性能(原来团队不了解此主题)。

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

列类型不同!

编写过类似代码的开发人员应该得到相当多的讨论。

感谢您的帮助!

【讨论】:

  • 值得指出的是,MySQL 5.7 改进了这种情况,将支持“动态范围”。请参阅此博客文章:mysqlserverteam.com/dynamic-range-access-and-recent-changes(链接到此处。)
  • 我还欠你一杯啤酒
  • 好吧,我会的——我也欠你一杯啤酒。
  • 现在已经太晚了,你现在已经喝够了啤酒,但是请也接受我的啤酒:) 这也解决了我的问题,真的很困惑。非常感谢。
  • 谢谢,@Morgan Tocker。在我的案例中,列类型全部排列,但是执行范围检查的查询需要一个小时才能运行。从 5.6 升级到 5.7,相同的查询现在运行速度提高了一个数量级。
【解决方案2】:

我遇到了同样的问题,但原因不同。我正在加入一个大表,并且 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 连接拆分为两个单独的连接。

【讨论】:

    猜你喜欢
    • 2022-01-05
    • 2018-11-18
    • 2020-05-19
    • 2013-06-09
    • 2014-01-12
    • 2018-05-24
    • 1970-01-01
    • 2021-09-14
    • 1970-01-01
    相关资源
    最近更新 更多