[mysql] MYSQL이 더 높은 LIMIT 오프셋으로 인해 쿼리 속도가 느려지는 이유는 무엇입니까?

요약 시나리오 : 1600 만 개가 넘는 레코드 (2GB 크기)의 테이블. SELECT로 LIMIT 오프셋이 높을수록 ORDER BY * primary_key *를 사용할 때 쿼리 속도가 느려집니다.

그래서

SELECT * FROM large ORDER BY `id`  LIMIT 0, 30 

보다 훨씬 덜 걸립니다

SELECT * FROM large ORDER BY `id` LIMIT 10000, 30 

그것은 단지 30 개의 레코드만을 주문하고 어쨌든 동일합니다. 따라서 ORDER BY의 오버 헤드가 아닙니다.
이제 최신 30 행을 가져올 때 약 180 초가 걸립니다. 간단한 쿼리를 어떻게 최적화 할 수 있습니까?



답변

쿼리가 첫 번째 OFFSET + LIMIT레코드 를 계산하고 LIMIT그중 하나만 가져야 하므로 오프셋이 높을수록 쿼리 속도가 느려집니다 . 이 값이 높을수록 쿼리 실행 시간이 길어집니다.

OFFSET첫째, 레코드 길이가 다를 수 있고, 둘째, 삭제 된 레코드와의 간격이있을 수 있기 때문에 쿼리를 바로 진행할 수 없습니다 . 각 레코드를 확인하고 계산해야합니다.

그 가정하면 idA는 PRIMARY KEY(A)의 MyISAM테이블이 트릭을 사용하여 속도를 높일 수 있습니다 :

SELECT  t.*
FROM    (
        SELECT  id
        FROM    mytable
        ORDER BY
                id
        LIMIT 10000, 30
        ) q
JOIN    mytable t
ON      t.id = q.id

이 기사를 참조하십시오 :


답변

나는 똑같은 문제가 있었다. 특정 30 세트가 아닌 많은 양의 데이터를 수집하려는 경우 루프를 실행하고 오프셋을 30 씩 증가시킬 수 있습니다.

따라서 대신 할 수있는 일은 다음과 같습니다.

  1. 일련의 데이터 (30)의 마지막 ID를 유지합니다 (예 : lastId = 530)
  2. 조건 추가 WHERE id > lastId limit 0,30

따라서 항상 ZERO 오프셋을 가질 수 있습니다. 성능 향상에 놀랄 것입니다.


답변

MySQL은 10000 번째 레코드 (또는 제안하는대로 80000 바이트)로 직접 이동할 수 없습니다. 포장 / 정렬 된 것으로 가정 할 수 없기 때문에 (또는 1 ~ 10000의 연속 값이 있다고 가정 할 수 없기 때문에). 실제로는 그렇게 될 수 있지만 MySQL은 구멍 / 간격 / 삭제 된 ID가 없다고 가정 할 수 없습니다.

bobs가 지적했듯이 MySQL은 id30을 반환하기 전에 10000 개의 행을 가져와야합니다 (또는 인덱스의 10000 번째 항목을 통과 해야 함 ).

편집 : 내 요점을 설명하기 위해

그럼에도 불구하고

SELECT * FROM large ORDER BY id LIMIT 10000, 30 

느린 (어) ,

SELECT * FROM large WHERE id >  10000 ORDER BY id LIMIT 30 

빠른 (어) 와 같은 결과가 실종이 없음을 제공 반환 id의 (즉, 간격).


답변

SELECT 쿼리 ORDER BY id LIMIT X, Y를 최적화하는 흥미로운 예를 찾았습니다. 나는 35million의 행을 가지고 있으므로 행 범위를 찾는 데 2 ​​분이 걸렸습니다.

요령은 다음과 같습니다.

select id, name, address, phone
FROM customers
WHERE id > 990
ORDER BY id LIMIT 1000;

마지막 ID로 WHERE를 넣으면 성능이 많이 향상됩니다. 나를 위해 그것은 2 분에서 1 초였습니다 🙂

여기에 다른 흥미로운 트릭 : http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/

문자열과도 작동합니다.


답변

두 쿼리에서 시간이 많이 걸리는 부분은 테이블에서 행을 검색하는 것입니다. 논리적으로 말하면, LIMIT 0, 30버전에서는 30 개의 행만 검색하면됩니다. 에서 LIMIT 10000, 30버전, 10000 개 행이 평가 30 개 행이 반환됩니다. 데이터 읽기 프로세스에서 일부 최적화를 수행 할 수 있지만 다음을 고려하십시오.

쿼리에 WHERE 절이 있으면 어떻게합니까? 엔진은 자격을 갖춘 모든 행을 반환 한 다음 데이터를 정렬하고 마지막으로 30 개의 행을 가져와야합니다.

ORDER BY 순서로 행이 처리되지 않는 경우도 고려하십시오. 리턴 할 행을 판별하려면 모든 규정 행을 정렬해야합니다.


답변

비교와 수치에 관심이있는 사람들을 위해 🙂

실험 1 : 데이터 집합에 약 1 억 개의 행이 있습니다. 각 행에는 여러 개의 BIGINT, TINYINT 및 약 1k 문자를 포함하는 두 개의 TEXT 필드가 있습니다 (고의적으로).

  • 파란색 : = SELECT * FROM post ORDER BY id LIMIT {offset}, 5
  • 주황색 : = @Quassnoi의 방법. SELECT t.* FROM (SELECT id FROM post ORDER BY id LIMIT {offset}, 5) AS q JOIN post t ON t.id = q.id
  • 물론 세 번째 방법 ... WHERE id>xxx LIMIT 0,5은 시간이 일정해야하기 때문에 여기에 나타나지 않습니다.

실험 2 : 한 행에 3 개의 BIGINT 만 있다는 점을 제외하고는 비슷한 것입니다.

  • 녹색 : = 전에 파란색
  • 빨강 : = 오렌지 전

여기에 이미지 설명을 입력하십시오


답변