2013-04-15 22 views
7

我有理由新MySQL和我想选择一组不同的使用此语句行:选择在MySQL DISTINCT语句正在10分钟

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 ...返回此:

enter image description here

+2

如果您省略了“DISTINCT”限定符,该操作如何执行?当你在查询中使用EXPLAIN时,你会得到什么? –

+3

解释计划是什么?将其粘贴到pastebin或要点中 –

+1

我不确定我会如何测试,因为如果我省略了限定符,那么大约会返回2.5亿行。对不起,如果这看起来像废话,我有点新的测试/调试查询。 –

回答

6

看起来你正在努力寻找停车点的集合,根据一定的标准。而且,您正在使用SELECT DISTINCT来避免重复的停止点。是对的吗?

看起来atcoCode是您stoppoints表的唯一关键。是对的吗?

如果是的话,试试这个:

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使查询有点更容易阅读。)

这应该加快你了一下。但是,必须说,四分之一的高卢桌是一张大桌子。

+0

+1,用于思考实际的SQL并发现这些优化。我甚至不知道你可以使用'JOIN(SELECT ...)'作为有效的语法。这使我的查询时间缩短了一半,谢谢。只是要检查其他建议的答案。 –

+0

这确实是对我所做的重大优化,感谢您的出色答案。 –

+0

顺便说一句,@Carlos P,让你不需要的东西离开你选择的列表。你真的在应用程序中使用'name'和'atcoCode'值吗?如果不是这样,不要问他们:这个结果集只是megarow的三分之一左右,要从服务器到客户端洗掉这么多数据需要花费一些时间。 –

3

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

+0

你能解释一下你的答案你的意思是“分片”吗?谢谢。 – Jocelyn

+0

他的意思是这个http://xeround.com/blog/2011/11/mysql-sharding-vs-mysql-partitioning在这里http://en.wikipedia.org/wiki/Shard_(database_architecture) –

+0

出于好奇,这将如何工作?因为我们正在寻找不同的项目,合并结果集本身不会像原来的工作那么耗时? –

2

诀窍是减少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 
+0

谢谢,我试过这个,但它实际上增加了查询时间。我用一个更小的查询('WHERE代理。agency_id = 1'),通常需要4-5秒,大约需要8秒。 'sp.name','sp.longitude','sp.latitude'没有被索引,这可能是原因吗?我不确定我是否理解这四个栏目的分组逻辑;这是否有必要,如果是的话,我应该索引它们吗?我担心索引它们所带来的性能下降可能会超过它的好处。 –

+0

在你看来,这种做法比@Ollie Jones的回答更好,因为他的建议似乎更快。 –

+0

我还可以补充说我在我的问题中犯了一个错误,DISTINCT列是sp.atcoCode - 这可能解释了异常? –

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); 
+0

谢谢,尽管我相信@Ollie Jones已经发现了这一点?但请欣赏答案。 –