【问题标题】:mysql returning a subset of a query is slower than returning the superset of a querymysql 返回查询的子集比返回查询的超集慢
【发布时间】:2013-11-22 21:56:31
【问题描述】:

假设我这样做

EXPLAIN SELECT * FROM xyz e
            JOIN abc cs ON e.rss = 'text' AND e.rdd = cs.xid
            JOIN def c ON cs.cid = c.xid
            JOIN jkl s ON c.sid = s.nid
          WHERE s.flag = 0;

这将揭示:

1, 'SIMPLE', 's', 'ref', 'PRIMARY,Index_8', 'x1', '1', 'const', 1586, 'Using index; Using temporary'
1, 'SIMPLE', 'c', 'ref', 'PRIMARY,sid', 'x2', '4', 's.nid', 40, 'Using index'
1, 'SIMPLE', 'cs', 'ref', 'PRIMARY,cid', 'x3', '4', 'c.nid', 1, 'Using index'
1, 'SIMPLE', 'e', 'ref', 'rss,rdd', 'x4', '141', 'const,cs.nid', 12, 'Using where; Using index; Distinct'

但是,假设我这样做了

EXPLAIN SELECT * FROM xyz e
            JOIN abc cs ON e.rss = 'text' AND e.rdd = cs.xid
            JOIN def c ON cs.cid = c.xid
            JOIN jkl s ON c.sid = s.nid
          WHERE s.flag = 0 AND c.range_field <= 10;

这将揭示

1, 'SIMPLE', 'c', 'ALL', 'PRIMARY,school_nid,Index_5', '', '', '', 56074, 'Using where; Using temporary'
1, 'SIMPLE', 's', 'eq_ref', 'PRIMARY,Index_8', 'PRIMARY', '4', 'c.school_nid', 1, 'Using where'
1, 'SIMPLE', 'cs', 'ref', 'PRIMARY,cid', 'x3', '4', 'c.nid', 1, 'Using index'
1, 'SIMPLE', 'e', 'ref', 'rss,rdd', 'x4', '141', 'const,cs.nid', 12, 'Using where; Using index; Distinct'

即。第一个查询只扫描 1586 行,而这个查询扫描超过 56074 行

尽管事实上第二个查询应该返回第一个查询结果的子集。

即。在第一个查询的 1586 个结果中,返回 c.range_field

有没有办法修改这个查询,以便扫描的行数将是

【问题讨论】:

  • 您是否尝试过使用第一个查询作为子查询进行选择?换句话说,SELECT * FROM (SELECT * ...) x WHERE x.range_field &lt;= 10

标签: mysql sql performance select subset


【解决方案1】:

当您按c.range_field 过滤时,def cFROM 子句中的第三个表,由于没有索引,因此过滤发生在三个表的连接集的结果集上。我建议你按照 Sebas 的回答在 c.range_field 上创建一个索引。

我自己会使用的替代方法是将def 设置为驾驶桌。这意味着,以def 表开头FROM 子句,最好是jkl。这将过滤第一个和第二个表中的行,然后将它们与第三个和第四个表连接起来。

【讨论】:

    【解决方案2】:

    从性能的角度来看,第二个查询是第一个查询的子集这一事实并不重要。

    在第一个查询中,c 表不涉及过滤器,而在第二个查询中,c.range_field 上有一个过滤器。 正如您在第一个解释计划 (Using index) 中看到的那样,第一个查询只能使用索引计算结果集,这是一种快速操作(从索引中,mysql 可以推断出所需行的位置并且只读取这些那些解释了较低的扫描量)。在第二个解释计划中,MYSQL 必须使用公共数据库 hd 块计算结果集,这是一个缓慢的操作(全表扫描:行被逐一读取并以这种方式评估)。

    您的解决方案是评估将c.range_field 列包含到第二个解释计划的c 列中注释的possible keys 索引之一的可能性。

    【讨论】:

    • 是的,第二个解释中的 Index_5 索引包含 c.range_field (它是该索引中的唯一列)......那么这个查询是否注定要执行那么多行扫描?
    • @pillarOfLight,mysql 可能评估 c.range_field &lt;= 10 的子集没有足够的代表性以使用索引,并选择继续进行 FTS。如果受影响的列选择性不够,有时索引会适得其反。
    • @Sebas 设置得比我预想的要好,所以我就在这里标记它:它 is 使用不同的驱动表,因为您添加的条件。它在第二个查询中选择了def,它在第一个查询中使用了jkl
    • @pillarOfLight 进行调查,请运行以下查询:select count(*)/count(distinct range_field) from def。如果结果高于 20,这可能会验证我的方案。
    猜你喜欢
    • 2021-06-18
    • 1970-01-01
    • 2022-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多