https://mariadb.com/docs/release-notes/mariadb-community-server-release-notes/old-releases/release-notes-mariadb-10-2-series/what-is-mariadb-102#explain 윈도우함수가 10.2에 처음등장햇음;;
아주 좋은 질문입니다! 결론부터 말씀드리면, LIMIT를 사용하는 것이 비교할 수 없을 정도로 훨씬 더 빠르고 효율적입니다.
100만 건 데이터가 있고 등록일(예: created_at)에 인덱스가 걸려있는 상황은 이 차이를 극명하게 보여주는 아주 좋은 예시입니다.
왜 LIMIT가 훨씬 빠를까요?
ORDER BY … LIMIT 구문은 이와 같은 “상위 N개 레코드 조회”에 완벽하게 최적화되어 있습니다.
LIMIT 사용 시 동작 방식 (가장 빠른 시나리오):
Generated sql
`SELECT * FROM your_table ORDER BY created_at DESC LIMIT 5;`
-
MariaDB 옵티마이저는 ORDER BY created_at DESC 구문을 봅니다.
-
created_at 컬럼에 인덱스가 있다는 것을 확인합니다.
-
전체 100만 건의 데이터를 스캔하거나 정렬하지 않습니다. 대신, 인덱스를 역순으로 읽기 시작합니다. (Index Reverse Scan)
-
인덱스에서 가장 큰 값(가장 최신 날짜)을 가진 첫 번째 항목을 찾고, 해당 데이터 행을 가져옵니다.
-
이 과정을 4번 더 반복합니다.
-
LIMIT 5에 도달했으므로, 5개의 행만 읽고 즉시 쿼리를 종료합니다.
핵심: 이 방식은 전체 100만 건 중 단 5건의 데이터에만 접근하므로 I/O와 CPU 사용량이 극도로 적습니다.
ROW_NUMBER()는 왜 느릴까요?
ROW_NUMBER()는 윈도우 함수(Window Function)로, 행의 순위를 매기는 데 사용됩니다. 이 작업은 본질적으로 더 많은 연산을 필요로 합니다.
ROW_NUMBER() 사용 시 동작 방식:
Generated sql
`SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY created_at DESC) as rn FROM your_table ) AS ranked_table WHERE rn <= 5;`
IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution. SQL
IGNORE_WHEN_COPYING_END
-
MariaDB는 서브쿼리(FROM (…)) 부분을 먼저 실행해야 합니다.
-
ROW_NUMBER() OVER (ORDER BY created_at DESC)는 your_table의 모든 행에 대해 created_at을 기준으로 순위를 매겨야 합니다.
-
이를 위해 옵티마이저는 인덱스를 사용하더라도, 1번부터 100만 번까지의 순번을 모두 매기기 위해 전체 100만 건의 데이터를 (논리적으로) 정렬하고 순번을 부여하는 과정을 거칩니다. 이는 막대한 양의 작업입니다.
-
이렇게 생성된 100만 건의 결과셋(원본 컬럼 + rn 컬럼)에서 WHERE rn ⇐ 5 조건을 통해 상위 5개를 필터링합니다.
핵심: 단 5건을 얻기 위해 100만 건 전체를 처리해야 하므로, LIMIT에 비해 엄청나게 비효율적입니다. 데이터가 많을수록 성능 저하가 기하급수적으로 커집니다.
※ 최신 버전의 DB 옵티마이저는 ROW_NUMBER() … WHERE rn ⇐ N 패턴을 ORDER BY … LIMIT N으로 자동 최적화해주는 경우도 있지만, 이를 보장할 수 없으며 명시적으로 LIMIT를 쓰는 것이 항상 가장 안전하고 확실한 방법입니다.
성능 비교 요약
| 항목 | LIMIT | ROW_NUMBER() |
| 동작 방식 | 인덱스를 이용해 필요한 5건만 접근 | 전체 데이터를 정렬하고 순번 부여 후 필터링 |
| 성능 (100만건) | 매우 빠름 (최적) | 매우 느림 (데이터가 많을수록 급격히 저하) |
| 자원 사용량 | 최소 (I/O, CPU, 메모리) | 높음 (임시 데이터셋 생성으로 인한 메모리/CPU 사용) |
| 주요 용도 | 단순 상위/하위 N건 조회 | 그룹 내 순위 매기기 등 복잡한 분석 쿼리 |
ROW_NUMBER()는 언제 사용하나요?
ROW_NUMBER()는 LIMIT로 해결하기 어려운, 더 복잡한 순위 분석에 사용될 때 강력한 힘을 발휘합니다. 예를 들어, “각 부서별로 가장 최근에 입사한 직원 2명씩 조회” 와 같은 경우입니다.
Generated sql
`-- 각 department_id 별로 hire_date가 최신인 2명씩 조회 SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY hire_date DESC) as rn FROM employees ) AS ranked_employees WHERE rn <= 2;`
IGNORE_WHEN_COPYING_START
content_copy download
Use code with caution. SQL
IGNORE_WHEN_COPYING_END
이 쿼리는 PARTITION BY를 사용하여 그룹(department_id) 내에서 순위를 매기므로 LIMIT로는 간단하게 구현할 수 없습니다. 이럴 때 ROW_NUMBER()가 제 역할을 하는 것입니다.
결론
“최신 등록된 5건 조회” 와 같이 단순한 상위 N건을 조회할 때는 고민 없이 ORDER BY … LIMIT 구문을 사용하세요. 이것이 가장 빠르고, 가장 효율적이며, 해당 목적을 위해 만들어진 표준적인 방법입니다.