7

我对 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);

但是,select 语句大约需要 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。

任何人都可以看到让这个运行更快的方法吗?或者实际上,根本没有!

stoppoints 表在不同的模式中是否重要?

编辑: EXPLAIN SELECT... 返回:

在此处输入图像描述

4

4 回答 4

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 值的子查询情况。

JOIN并且INNER JOIN是相同的。我曾经JOIN使查询更易于阅读。)

这应该会加快你的速度。但是,不得不说,四分之一 gigarow 表是一张大表。

于 2013-04-15T15:40:13.033 回答
3

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

于 2013-04-15T15:30:44.757 回答
2

诀窍是减少gtfsstop_times SQL 必须评估的行数。在这种情况下,SQL 首先评估gtfsstop_timestransportdata.stoppoints的内部连接中的每一行,对吗?transportdata.stoppoints 有多少行?然后 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
于 2013-04-15T15:43:46.237 回答
1

您的问题还有其他有价值的答案,而我的答案是对它的补充。我假设sp.atcoCode并且st.fk_atco_code是他们表中的索引列。

如果您可以验证并确保WHERE子句中的机构 ID 有效,则可以消除`vehicledata在 JOINS 中加入 .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);
于 2013-04-15T18:18:33.020 回答