MySQL中的SELECT DISTINCT语句需要10分钟

时间:2013-04-15 15:17:30

标签: mysql performance

我是MySQL的新手,我正在尝试使用此语句选择一组不同的行:

SELECT DISTINCT sp.atcoCode, sp.name, sp.longitude, sp.latitude
FROM `transportdata`.stoppoints as sp
INNER JOIN `vehicledata`.gtfsstop_times as st ON sp.atcoCode = st.fk_atco_code
INNER JOIN `vehicledata`.gtfstrips as trip ON st.trip_id = trip.trip_id
INNER JOIN `vehicledata`.gtfsroutes as route ON trip.route_id = route.route_id
INNER JOIN `vehicledata`.gtfsagencys as agency ON route.agency_id = agency.agency_id
WHERE agency.agency_id IN (1,2,3,4);

然而,选择声明大约需要10分钟,因此显而易见。

一个重要因素是表gtfsstop_times很大。 (约2.5亿条记录)

索引似乎设置得当;所有上述连接都使用索引列。表格大小大致为:

gtfsagencys - 4 rows
gtfsroutes - 56,000 rows
gtfstrips - 5,500,000 rows
gtfsstop_times - 250,000,000 rows
`transportdata`.stoppoints - 400,000 rows

服务器有22Gb的内存,我已经将InnoDB缓冲池设置为8G,而我使用的是MySQL 5.6。

有人能看到让这种运行更快的方法吗?或者确实如此!

停止点表位于不同的模式中是否重要?

编辑: EXPLAIN SELECT ...返回:

enter image description here

4 个答案:

答案 0 :(得分:6)

看起来你正试图根据某些标准找到一个停止点集合。并且,您正在使用SELECT DISTINCT来避免重复停止点。是吗?

看起来atcoCode是您的停止点表的唯一键。是吗?

如果是这样,试试这个:

SELECT sp.name, sp.longitude, sp.latitude, sp.atcoCode
  FROM `transportdata`.stoppoints` AS sp
  JOIN ( 
     SELECT DISTINCT st.fk_atco_code AS atcoCode
       FROM `vehicledata`.gtfsroutes AS route
       JOIN `vehicledata`.gtfstrips AS trip ON trip.route_id = route.route_id
       JOIN `vehicledata`.gtfsstop_times AS st  ON trip.trip_id = st.trip_id
       WHERE route.agency_id BETWEEN 1 AND 4
  ) ids ON sp.atcoCode = ids.atcoCode

这做了一些事情:它消除了你似乎不需要的表(代理)。它会将agency_id上的搜索从IN(a,b,c)更改为范围搜索,这可能会有所帮助,也可能没有帮助。最后,它将DISTINCT处理从必须处理大量数据的情况重新定位到子查询情况,只需要处理ID值。

JOININNER JOIN相同。我使用JOIN使查询更容易阅读。)

这会加速你的速度。但是,必须要说的是,四分之一的gigarow表是一张大桌子。

答案 1 :(得分:3)

拥有250M记录,我会在一列上对gtfsstop_times表进行分片。然后,每个分片表可以在一个单独的查询中连接,该查询可以在不同的线程中并行运行,您只需要合并结果集。

答案 2 :(得分:2)

诀窍是减少SQL必须评估的 gtfsstop_times 行数。在这种情况下,SQL首先评估 gtfsstop_times transportdata。stoppoints 的内部联接中的每一行,对吧? transportdata。停止点有多少行?然后,SQL计算WHERE子句,然后计算DISTINCT。它是如何做到DISTINCT的?通过多次查看每一行来确定是否有其他类似的行。这需要永远,对吧?

然而,GROUP BY快速地将所有匹配的行拼凑在一起,而不是评估每一行。我通常使用连接来快速减少查询需要评估的行数,然后我查看我的分组。

在这种情况下,您希望将DISTINCT替换为分组。

试试这个;

SELECT sp.name, sp.longitude, sp.latitude, sp.atcoCode

FROM `transportdata`.stoppoints as sp
    INNER JOIN `vehicledata`.gtfsstop_times as st ON sp.atcoCode = st.fk_atco_code
    INNER JOIN `vehicledata`.gtfstrips as trip ON st.trip_id = trip.trip_id
    INNER JOIN `vehicledata`.gtfsroutes as route ON trip.route_id = route.route_id
    INNER JOIN `vehicledata`.gtfsagencys as agency ON route.agency_id = agency.agency_id

WHERE agency.agency_id IN (1,2,3,4)

GROUP BY sp.name
    , sp.longitude
    , sp.latitude
    , sp.atcoCode

答案 3 :(得分:1)

您的问题还有其他有价值的答案,我的补充也是如此。我假设sp.atcoCodest.fk_atco_code是其表格中的索引列。

如果您可以验证并确保WHERE子句中的代理ID有效,则可以在JOINS中取消加入`vehicledata。gtfsagencys`,因为您没有从表中获取任何记录。

SELECT DISTINCT sp.atcoCode, sp.name, sp.longitude, sp.latitude
FROM `transportdata`.stoppoints as sp
INNER JOIN `vehicledata`.gtfsstop_times as st ON sp.atcoCode = st.fk_atco_code
INNER JOIN `vehicledata`.gtfstrips as trip ON st.trip_id = trip.trip_id
INNER JOIN `vehicledata`.gtfsroutes as route ON trip.route_id = route.route_id
WHERE route.agency_id IN (1,2,3,4);