2009-07-27 5 views
1

게임에서 모든 플레이어의 게임 결과를 합산하고 조건에 맞는 플레이어를 표시하는 쿼리입니다.이 MySQL 쿼리를 최적화하는 가장 좋은 방법은 무엇입니까?

select *, 
     (kills/deaths) as killdeathratio, 
     (totgames - wins) as losses 
from  (select gp.name    as name, 
        gp.gameid   as gameid, 
        gp.colour   as colour, 
        Avg(dp.courierkills) as courierkills, 
        Avg(dp.raxkills)  as raxkills, 
        Avg(dp.towerkills) as towerkills, 
        Avg(dp.assists)  as assists, 
        Avg(dp.creepdenies) as creepdenies, 
        Avg(dp.creepkills) as creepkills, 
        Avg(dp.neutralkills) as neutralkills, 
        Avg(dp.deaths)  as deaths, 
        Avg(dp.kills)  as kills, 
        sc.score    as totalscore, 
        Count(*)   as totgames, 
        Sum(case 
         when ((dg.winner = 1 and dp.newcolour < 6) or 
           (dg.winner = 2 and dp.newcolour > 6)) 
         then 1 
         else 0 
         end) as wins 
      from  gameplayers as gp, 
        dotagames as dg, 
        games  as ga, 
        dotaplayers as dp, 
        scores  as sc 
      where dg.winner <> 0 
        and dp.gameid = gp.gameid 
        and dg.gameid = dp.gameid 
        and dp.gameid = ga.id 
        and gp.gameid = dg.gameid 
        and gp.colour = dp.colour 
        and sc.name = gp.name 
      group by gp.name 
      having totgames >= 30 
     ) as h 
order by totalscore desc 

지금 내가 갈 수있는 가장 좋은 방법은 무엇 너무 확실하지 않다하지만 당신의 의견에 어떤 것이 쿼리를 최적화 할 수?

Q6600 @ 2.4ghz, 4GB RAM, 64 비트 Linux Ubuntu 9.04 시스템을 실행하며이 쿼리는 최대 6.7 초가 걸릴 수 있습니다 (거대한 데이터베이스가 있음). 또한

나뿐만 아니라이 쿼리의 상단에 추가 조건을 실행하는 결과를 페이지를 매기 싶습니다이

내가 프론트 엔드로 파이썬을 사용하여 포함하므로, 어떠한 방법을 장고를 사용 .... 너무 느리다 +/- 장고 방법은 위대한 것입니다. MySQL, Apache2 개조도 환영합니다. 물론 쿼리를 변경하여 쿼리를 더 빠르게 실행할 수 있습니다.

내 질문을 읽어 주셔서 감사합니다. 답을 읽기를 고대합니다!

편집 : EXPLAIN QUERY 모두의

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 PRIMARY  <derived2> ALL  NULL NULL NULL NULL 783  Using filesort 
2 DERIVED  sc ALL  name,name_2  NULL NULL NULL 2099 Using temporary; Using filesort 
2 DERIVED  gp ref  gameid,colour,name name 17 development.sc.name  2  
2 DERIVED  ga eq_ref PRIMARY,id,id_2  PRIMARY  4 development.gp.gameid 1 Using index 
2 DERIVED  dg ref  gameid,winner gameid 4 development.ga.id 1 Using where 
2 DERIVED  dp ref  gameid_2,colour  gameid_2 4 development.ga.id 10 Using where 
+1

EXPLAIN이 쿼리에 대해 말하는 것은 무엇입니까? 당신이 만들 수있는 잠재적 인 지수에 대한 어떤 힌트를 제공합니까? –

+0

설명 결과가 업데이트되었습니다. 해당 키를 추가하고 결과가 변경되었는지 확인합니다. – fivetwentysix

+0

또한 파생 테이블에서 선택하므로 파생 테이블을 뷰로 "이동"할 수 있습니다. 뷰에 "힌트"를 사용할 수 있어야하기 때문에 쿼리 속도를 약간 높일 수 있습니다 (MS SqlServer에서 가져온 지식이므로 MySQL에 적용되는 것이 확실하지 않습니다). –

답변

2

첫 번째 결과는 SQL은 형식이 잘못된된다. 가장 명백한 오류는 각 AS 절 앞에있는 줄 분리입니다. 두 번째 명백한 문제는 명시 적으로 INNER JOIN ... ON ...을 사용하는 대신 암시 적 조인을 사용하는 것입니다.

이제 실제 질문에 답하십시오.

데이터 또는 환경을 모르는 상태에서 가장 먼저 보는 것은 sort_bufferkey_buffer과 같은 일부 MySQL 서버 설정입니다. 이 중 하나라도 변경하지 않았다면 그들에게 읽으십시오. 기본값은 극도로 보수적이며 종종 기본값보다 10 배 이상 올릴 수 있습니다. 특히 당신처럼 큰 아이언에서는 그렇습니다.

리뷰를 통해 나는 속도를보기 위해 쿼리 조각을 실행하고 무엇을 EXPLAIN이 말하고 있는지 살펴 보았습니다. 인덱싱의 효과는 심오하지만 MySQL은 테이블 당 둘 이상을 사용할 수없는 "손가락 및 발가락"문제가 있습니다. 그리고 필터링을 사용하는 JOIN에는 2 가지가 필요할 수 있습니다. 따라서 다른 수표를 찾기 위해 행 스캔으로 내려와야합니다. 하지만 쿼리를 뜯어보고 ​​여러 조합을 시도하면 문제가 시작되는 지점이 표시됩니다.

이제 "전환점"이 어디인지를 알 수 있습니다. 여기에서 추출해야하는 것과 같이 일부 원시 데이터 크기가 약간 커지면 일부 내부 데이터로 인해 성능이 크게 저하됩니다 구조가 너무 커진다. 이 시점에서 임시 테이블 크기를 늘려야 할 것입니다. 이러한 종류의 최적화는 약간의 흑인 예술임을 유의하십시오. :-)

그러나 다른 접근법이 있습니다. 비정규 화입니다. 간단한 구현에서 정기적으로 예약 된 스크립트는 때때로 값 비싼 쿼리를 실행하고 표시하고자하는 것과 훨씬 가까운 구조의 별도 테이블에 데이터를 집어 넣습니다. 이 방법에는 여러 가지 변형이 있습니다. 응용 프로그램이나 테이블 트리거를 사용하여이 기능을 최신 상태로 유지할 수 있습니다. 다른 극단에서는 응용 프로그램이 값 비싼 쿼리를 가끔씩 실행하도록 허용 할 수 있지만 잠시 동안 결과를 캐시합니다. 이는 많은 사람들이 자주 호출하는 경우 가장 효과적입니다. 초당 15 회 실행되는 요청에서 2 초 캐시조차도 눈에 띄는 향상을 보입니다.

일부 데이터를 반환하고 데이터를 후 처리하는 여러 가지 쿼리를 실행하여 동일한 데이터를 생성하는 방법을 찾을 수 있습니다. 더 많은 데이터를 반환하는 원래 쿼리의 버전을 실행할 수도 있습니다 (필터링이 적기 때문에 훨씬 빨라질 수 있음). 나는 5 번 더 간단하고 더 작은 쿼리가 훨씬 더 빨라질 수 있다는 것을 여러 번 발견했습니다. 하나의 큰 쿼리가 모든 쿼리를 처리하려고 시도하는 것보다 두 배 정도 더 빠릅니다.

0

전체 테이블을 스캔 한 이후로 색인이 도움이되지 않습니다. 데이터베이스가 커짐에 따라 쿼리가 항상 느려집니다.

모든 게임 종료 후 해당 게임에 대한 행을 삽입하고 플레이어 행에 카운터를 증가 시키십시오. 그러면 정보를 사용할 수 있으므로 count() 및 sum()을 수행 할 필요가 없습니다.

0
  • 선택 * 나쁜 대부분의 시간 - 당신은 케이스 부분이 부속
  • 와 함께 할 수있다 (
  • 합을 필요로 할 때 임시 테이블을 사용하여 여러 간단한 선택으로 선택
  • 휴식이 필요에만 열을 선택
  • MySQL은 또는 표현식에 아주 나쁜 성능을 가지고있다. 이는 당신에게 노동 조합이 선택을 사용하여 함께
0

작은 개선

select *, (kills/deaths) as killdeathratio, (totgames - wins) as losses from (select gp.name as name, gp.gameid as gameid, gp.colour as colour, Avg(dp.courierkills) as courierkills, Avg(dp.raxkills) as raxkills, Avg(dp.towerkills) as towerkills, Avg(dp.assists) as assists, Avg(dp.creepdenies) as creepdenies, Avg(dp.creepkills) as creepkills, Avg(dp.neutralkills) as neutralkills, Avg(dp.deaths) as deaths, Avg(dp.kills) as kills, sc.score as totalscore, Count(1) as totgames, Sum(case when ((dg.winner = 1 and dp.newcolour < 6) or (dg.winner = 2 and dp.newcolour > 6)) then 1 else 0 end) as wins from gameplayers as gp, ( select * from dotagames dg1 where dg.winner <> 0) as dg, games as ga, dotaplayers as dp, scores as sc where and dp.gameid = gp.gameid and dg.gameid = dp.gameid and dp.gameid = ga.id and gp.gameid = dg.gameid and gp.colour = dp.colour and sc.name = gp.name group by gp.name having totgames >= 30 ) as h order by totalscore desc

변화 : 1 카운트 (*)는 행의 수가 감소되고, 발 (1) 2 카운트 chnaged.

+0

질문을 편집하고 변경 한 내용과 쿼리가 어떻게 개선 될지에 대한 설명을 추가 할 수 있습니까? – andrewsi

관련 문제