[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
첫째, 레코드 길이가 다를 수 있고, 둘째, 삭제 된 레코드와의 간격이있을 수 있기 때문에 쿼리를 바로 진행할 수 없습니다 . 각 레코드를 확인하고 계산해야합니다.
그 가정하면 id
A는 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 씩 증가시킬 수 있습니다.
따라서 대신 할 수있는 일은 다음과 같습니다.
- 일련의 데이터 (30)의 마지막 ID를 유지합니다 (예 : lastId = 530)
- 조건 추가
WHERE id > lastId limit 0,30
따라서 항상 ZERO 오프셋을 가질 수 있습니다. 성능 향상에 놀랄 것입니다.
답변
MySQL은 10000 번째 레코드 (또는 제안하는대로 80000 바이트)로 직접 이동할 수 없습니다. 포장 / 정렬 된 것으로 가정 할 수 없기 때문에 (또는 1 ~ 10000의 연속 값이 있다고 가정 할 수 없기 때문에). 실제로는 그렇게 될 수 있지만 MySQL은 구멍 / 간격 / 삭제 된 ID가 없다고 가정 할 수 없습니다.
bobs가 지적했듯이 MySQL은 id
30을 반환하기 전에 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 만 있다는 점을 제외하고는 비슷한 것입니다.
- 녹색 : = 전에 파란색
- 빨강 : = 오렌지 전