MySQL에서 STRAIGHT_JOIN을 사용하는 경우


88

작업 중이었던 상당히 복잡한 쿼리가 실행되는 데 8 초가 걸렸습니다. EXPLAIN은 이상한 테이블 순서를 보여 주었고 FORCE INDEX 힌트를 사용하더라도 인덱스가 모두 사용되지 않았습니다. STRAIGHT_JOIN 조인 키워드를 발견하고 일부 INNER JOIN 키워드를이 키워드로 교체하기 시작했습니다. 상당한 속도 향상을 발견했습니다. 결국이 쿼리에 대해 모든 INNER JOIN 키워드를 STRAIGHT_JOIN으로 교체했으며 이제 .01 초 안에 실행됩니다.

제 질문은 언제 STRAIGHT_JOIN을 사용하고 언제 INNER JOIN을 사용합니까? 좋은 쿼리를 작성하는 경우 STRAIGHT_JOIN을 사용하지 않을 이유가 있습니까?

답변:


73

정당한 이유없이 STRAIGHT_JOIN을 사용하지 않는 것이 좋습니다. 내 경험으로는 MySQL 쿼리 최적화 프로그램이 내가 원하는 것보다 더 자주 잘못된 쿼리 계획을 선택하지만 일반적으로이를 우회해야 할만큼 충분하지 않은 것입니다.

내 권장 사항은 모든 쿼리를 일반 JOIN으로 남겨 두는 것입니다. 한 쿼리가 차선의 쿼리 계획을 사용하고 있음을 발견하면 먼저 쿼리를 다시 작성하거나 재구성하여 최적화 프로그램이 더 나은 쿼리 계획을 선택하는지 확인하는 것이 좋습니다. 또한 적어도 innodb의 경우 인덱스 통계가 오래된 것이 아니라는 것을 확인하십시오 ( ANALYZE TABLE ). 이로 인해 최적화 프로그램이 잘못된 쿼리 계획을 선택할 수 있습니다. 일반적으로 옵티 마이저 힌트는 마지막 수단이어야합니다.

쿼리 힌트를 사용하지 않는 또 다른 이유는 데이터 분포가 시간이 지남에 따라 변경되거나 테이블이 커짐에 따라 인덱스 선택성이 변경 될 수 있다는 것입니다. 현재 최적의 쿼리 힌트는 시간이 지남에 따라 차선이 될 수 있습니다. 그러나 최적화 프로그램은 현재 오래된 힌트 때문에 쿼리 계획을 조정할 수 없습니다. 옵티마이 저가 결정을 내 리도록 허용하면 더 유연하게 유지됩니다.


59
이 대답은 실제로는 설명하지 않습니다 사용할 때 straight_join .
Pacerier 2015 년

23

에서 MySQL을 참조 가입 :

"STRAIGHT_JOIN은 왼쪽 테이블이 항상 오른쪽 테이블보다 먼저 읽힌다는 점을 제외하면 JOIN과 유사합니다. 이것은 조인 최적화 프로그램이 테이블을 잘못된 순서로 배치하는 경우에 사용할 수 있습니다."


28
감사합니다. 이미 MySQL 매뉴얼을 읽었습니다. 추가 설명을 기대합니다.
Greg

20

다음은 최근에 직장에서 발생한 시나리오입니다.

A, B, C의 세 테이블을 고려하십시오.

A에는 3,000 개의 행이 있습니다. B에는 300,000,000 개의 행이 있습니다. C에는 2,000 개의 행이 있습니다.

외래 키는 B (a_id), B (c_id)로 정의됩니다.

다음과 같은 쿼리가 있다고 가정합니다.

select a.id, c.id
from a
join b on b.a_id = a.id
join c on c.id = b.c_id

내 경험상 MySQL은이 경우 C-> B-> A로 이동할 수 있습니다. C는 A보다 작고 B는 거대하며 모두 동등 조인입니다.

문제는 MySQL이 (C.id와 B.c_id) 대 (A.id와 B.a_id) 사이의 교차점 크기를 반드시 고려하지 않는다는 것입니다. B와 C 사이의 조인이 B만큼 많은 행을 반환하는 경우 매우 잘못된 선택입니다. A로 시작하여 B를 A만큼 많은 행으로 필터링했다면 훨씬 더 나은 선택이었을 것입니다. straight_join이 명령을 다음과 같이 강제하는 데 사용할 수 있습니다.

select a.id, c.id
from a
straight_join b on b.a_id = a.id
join c on c.id = b.c_id

이제 a이전에에 가입해야합니다 b.

일반적으로 결과 집합의 행 수를 최소화하는 순서로 조인을 수행하려고합니다. 따라서 작은 테이블로 시작하여 결과 조인도 작아 지도록 조인하는 것이 이상적입니다. 작은 테이블에서 시작하여 더 큰 테이블에 결합하면 큰 테이블만큼 커지면 모든 것이 배 모양이됩니다.

하지만 통계에 따라 다릅니다. 데이터 분포가 변경되면 계산이 변경 될 수 있습니다. 또한 결합 메커니즘의 구현 세부 사항에 따라 다릅니다.

필자가 본 최악의 경우는 필수 straight_join이거나 공격적인 인덱스 힌팅을 제외하고는 모두 가벼운 필터링을 사용하여 엄격한 정렬 순서로 많은 데이터에 페이지를 매기는 쿼리입니다. MySQL은 정렬보다 필터 및 조인에 인덱스를 사용하는 것을 강력히 선호합니다. 이는 대부분의 사람들이 전체 데이터베이스를 정렬하려고하는 것이 아니라 쿼리에 응답하는 행의 제한된 하위 집합을 가지고 있기 때문에 이치에 맞습니다. 그리고 제한된 하위 집합을 정렬하는 것이 정렬 여부에 관계없이 전체 테이블을 필터링하는 것보다 훨씬 빠릅니다. 아니. 이 경우 인덱싱 된 열이있는 테이블 바로 뒤에 스트레이트 조인을 넣어 고정 된 항목으로 정렬하고 싶었습니다.


문제를 해결하기 위해 직선 조인을 어떻게 사용 하시겠습니까?
Hannele

@Hannele straight_join은 오른쪽 전에 왼쪽 테이블을 평가합니다. 따라서 A -> B -> C내 예에서 이동하려는 경우 첫 번째 join키워드를 straight_join.
Barry Kelly

아 깔끔해. 답변에 예제로 포함하는 것이 유용 할 것입니다. :)
Hannele

18

MySQL은 복잡한 쿼리에서 조인 순서를 선택하는 데 꼭 필요한 것은 아닙니다. 복잡한 쿼리를 straight_join으로 지정하면 쿼리는 지정된 순서대로 조인을 실행합니다. 테이블을 최소 공통 분모가되도록 먼저 배치하고 straight_join을 지정하면 쿼리 성능을 향상시킬 수 있습니다.


11

STRAIGHT_JOIN,이 절을 사용하여 JOIN순서를 제어 할 수 있습니다 . 즉, 외부 루프에서 스캔되는 테이블과 내부 루프에있는 테이블을 제어 할 수 있습니다 .


외부 루프와 내부 루프는 무엇입니까?
Istiaque Ahmed 2011

@IstiaqueAhmed 테이블은 중첩 된 루프로 조인됩니다 (테이블 A에서 첫 번째 행을 가져오고 테이블 B를 루프 던진 다음 두 번째 행을 가져옵니다 ... 등등. 여기에서 테이블 A는 외부 루프에 있습니다)
Accountant م

6

STRAIGHT_JOIN을 사용해야하는 이유를 알려 드리겠습니다.

  • 나는 한 성능 쿼리와 문제를.
  • 쿼리를 단순화하여 쿼리가 갑자기 더 효율적이었습니다.
  • 어떤 특정 부분이 문제를 일으켰는지 알아 내려고했지만 그럴 수 없었습니다. (왼쪽 조인 2 개는 느리고 각각 독립적으로 빠름)
  • 그런 다음 느리고 빠른 쿼리로 EXPLAIN을 실행했습니다 (왼쪽 조인 중 하나를 추가).
  • 놀랍게도 MySQL은 두 쿼리 사이의 JOIN 순서를 완전히 변경했습니다.

따라서 조인 중 하나를 straight_join으로 강제하여 이전 조인을 먼저 읽도록 강제했습니다. 이것은 MySQL이 실행 순서를 변경하는 것을 막고 매력처럼 작동했습니다!


2

내 짧은 경험에서 STRAIGHT_JOIN쿼리를 30 초에서 100 밀리 초로 줄인 상황 중 하나 는 실행 계획의 첫 번째 테이블이 열 순서가있는 테이블이 아니라는 것입니다.

-- table sales (45000000) rows
-- table stores (3) rows
SELECT whatever
FROM 
    sales 
    INNER JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id 
LIMIT 50;
-- there is an index on (date, id)

최적화 치고 선택하면 stores 첫 번째 가 발생할 수 Using index; Using temporary; Using filesort있기 때문에

ORDER BY 또는 GROUP BY에 조인 큐의 첫 번째 테이블이 아닌 테이블의 열이 포함 된 경우 임시 테이블이 생성됩니다.

출처

여기에서 옵티마이 저는 sales먼저 사용하여

sales STRAIGHT_JOIN stores

1
(나는 당신의 대답에 장식했습니다.)
Rick James

2

귀하의 질의 끝이 경우 ORDER BY... LIMIT..., 그것은 할 수있다 최적 일에 최적화를 속여 쿼리를 재구성하기 LIMIT 전에JOIN .

(이 답변은에 대한 원래 질문에만 적용되지 않으며의 STRAIGHT_JOIN모든 경우에 적용되지 않습니다 STRAIGHT_JOIN.)

@Accountant م 부터 시작하면 대부분의 상황에서 더 빠르게 실행됩니다. (그리고 힌트가 필요하지 않습니다.)

SELECT  whatever
    FROM  ( SELECT id FROM sales
                ORDER BY  date, id
                LIMIT  50
          ) AS x
    JOIN  sales   ON sales.id = x.id
    JOIN  stores  ON sales.storeId = stores.id
    ORDER BY  sales.date, sales.id;

메모:

  • 먼저 50 개의 ID를 가져옵니다. 이것은 특히 INDEX(date, id).
  • 그런 다음 다시 조인 하면 임시 테이블로 이동 하지 않고도sales 50 개의 "무엇이든"만 얻을 수 있습니다 .
  • 하위 쿼리는 정의에 따라 순서가 지정되지 않았으므로 ORDER BY외부 쿼리에서를 반복해야합니다. (최적화 프로그램은 실제로 다른 종류의 작업을 피하는 방법을 찾을 수 있습니다.)
  • 예, 더 지저분합니다. 그러나 일반적으로 더 빠릅니다.

나는 "오늘이 더 빠르더라도 내일은 더 빠르지 않을 수 있기 때문에"히트를 사용하는 것에 반대한다.


0

나는 그것이 조금 오래되었다는 것을 알고 있지만 여기에 시나리오가 있습니다. 특정 테이블을 채우기 위해 배치 스크립트를 수행했습니다. 어느 시점에서 쿼리가 매우 느리게 실행되었습니다. 특정 레코드에서 결합 순서가 올바르지 않은 것 같습니다.

  • 올바른 순서로

여기에 이미지 설명 입력

  • ID를 1 씩 늘리면 주문이 엉망이됩니다. '추가'필드를 확인하십시오.

여기에 이미지 설명 입력

  • straight_join을 사용하면 문제가 해결됩니다.

여기에 이미지 설명 입력

straight_join을 사용하는 동안 약 65 초 동안 잘못된 순서가 실행됩니다.


-5
--use 120s, 18 million data
    explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t
    WHERE d.taid = t.taid
      AND t.client_version >= '21004007'
      AND t.utdid IS NOT NULL
      AND d.recommend_day = '20170403'
    LIMIT 0, 10000

--use 3.6s repalce by straight join
 explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d
    STRAIGHT_JOIN 
      tvassist_taid_all t on d.taid = t.taid 
    WHERE 
     t.client_version >= '21004007'
       AND d.recommend_day = '20170403'

      AND t.utdid IS NOT NULL  
    LIMIT 0, 10000

3
이것은 직선 조인이 적절한시기를 파악하기에 충분한 정보를 제공하지 않습니다.
Hannele
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.