使用subselect优化MySQL查询

时间:2017-07-18 08:23:01

标签: mysql database optimization relational-database

我试图使以下查询运行速度超过180秒:

SELECT 
    x.di_on_g AS deviceid, SUM(1) AS amount
FROM
    (SELECT 
        g.device_id AS di_on_g
    FROM
        guide g
    INNER JOIN operator_guide_type ogt ON ogt.guide_type_id = g.guide_type_id
    INNER JOIN operator_device od ON od.device_id = g.device_id
    WHERE
        g.operator_id IN (1 , 1)
            AND g.locale_id = 1
            AND (g.device_id IN ("many (~1500) comma separated IDs coming from my code"))
    GROUP BY g.device_id , g.guide_type_id) x
GROUP BY x.di_on_g
ORDER BY amount;

来自EXPLAIN的屏幕截图: https://ibb.co/da5oAF

即使我将子查询作为单独的查询运行,它仍然非常慢......:

SELECT
        g.device_id AS di_on_g
    FROM
        guide g
    INNER JOIN operator_guide_type ogt ON ogt.guide_type_id = g.guide_type_id
    INNER JOIN operator_device od ON od.device_id = g.device_id
    WHERE
        g.operator_id IN (1 , 1)
            AND g.locale_id = 1
            AND (g.device_id IN (("many (~1500) comma separated IDs coming from my code")

EXPLAIN:

的屏幕截图

ibb.co/gJHRVF

我在g.device_id和其他适当的地方都有索引。

索引:

SHOW INDEX FROM guide;

ibb.co/eVgmVF

SHOW INDEX FROM operator_guide_type;

ibb.co/f0TTcv

SHOW INDEX FROM operator_device;

ibb.co/mseqqF

我尝试为ID创建一个新的临时表,并使用JOIN替换慢IN clause,但这不会使查询更快。

所有ID都是整数,我尝试为来自我的代码和JOIN表的id创建一个新的临时表而不是慢速的IN子句,但这并没有使查询更快。 (快10秒)

没有一个表有超过300,000行,而且mysql配置也不错。

视觉计划: Query Plan

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

让我们关注子查询。主要的问题是" inflate-deflate",但我马上就会谈到它。

添加综合索引:

INDEX(locale_id, operator_id, device_id)

为什么重复" 1"在

g.operator_id IN (1 , 1)

为什么GROUP BY有2列,只选择1?是否有使用GROUP BY代替DISTINCT的原因。 (后者似乎是你的意图。)

这些

的唯一原因
INNER JOIN operator_guide_type ogt ON ogt.guide_type_id = g.guide_type_id
INNER JOIN operator_device od ON od.device_id = g.device_id

将验证其他表中是否有指南和设备。那是对的吗?这些是PRIMARY KEYs,因此是唯一的吗?:ogt.guide_type_idod.device_id。如果是这样,为什么需要GROUP BY?基于EXPLAIN,听起来这两者都是相关的1:很多。所以......

SELECT g.device_id AS di_on_g
    FROM  guide g
    WHERE EXISTS( SELECT * FROM operator_guide_type WHERE guide_type_id = g.guide_type_id )
      AND EXISTS( SELECT * FROM operator_device WHERE device_id = g.device_id
      AND g.operator_id IN (1)
      AND g.locale_id = 1
      AND g.device_id IN (...)

注意:

  • 不再需要GROUP BY
  • " inflate-deflate" JOIN + GROUP BY已消失。解释指出了这一点 - 139K行充气至61M - 非常昂贵。
  • EXISTS是" semijoin",表示它不会收集所有匹配,但会在找到任何匹配时停止。

" mysql配置很好" - 你有多少RAM?桌子是什么引擎? innodb_buffer_pool_size的价值是什么?