Mysql의 Offset이란, 주로 LIMIT와 함께 사용되며 결과의 어디서부터 데이터를 가져올지를 지정하는 역할을 한다. 주로 페이징 처리를 할때 사용되며, 사용법은 아래와 같다.
// users 테이블의 21번째 행부터 10개의 데이터를 가져온다.
SELECT * FROM users LIMIT 10 OFFSET 20;
Offset을 사용해서 페이징을 구현하는 것이 쉽고 간단해 보일 수 있겠으나, 상황에 따라서 Offset을 사용하지 않으면 쿼리 성능이 향상되는 경우도 있기에 NoOffset이 필요한 상황과 그에 따른 개선 방법을 알아보도록 한다.
// users 테이블의 id 역순의 데이터 중 501번째 데이터부터 10개의 행을 리턴
SELECT * FROM users ORDER BY id DESC LIMIT 10 OFFSET 500
위와 같은 쿼리를 처리할 때, 옵티마이저는 저장소로부터 10개의 데이터를 받아오는 것이 아닌, 모든 행을 받아오게 된다. 그 이유는 쿼리의 실행 순서와 연관되어 있는데, LIMIT/OFFSET보다 ORDER BY가 우선적으로 실행되기에, 모든 행을 받아와 역순으로 정렬하고 그 후에 501번째 데이터에서부터 10개의 행을 리턴하는 과정을 거친다.
// Offset을 Where문으로 대체 (위의 쿼리와 결과 동일)
SELECT * FROM users WHERE id < 1000 - 500 ORDER BY id DESC LIMIT 10;
위의 쿼리처럼 ORDER BY의 동작 이전인 WHERE문에서 OFFSET으로부터 수행하고자 했던 "501번째 행부터"라는 처리를 넣음으로써, 옵티마이저는 저장소에 요청하는 데이터 수를 줄일 수 있다.
그렇기에 NoOffset의 핵심은 ORDER BY 동작 이전에 OFFSET에서 해야할 작업을 수행하는 것이라고 할 수 있겠다.
비단 NoOffset만이 아니더라도, 쿼리 튜닝을 함에 있어서 SQL의 실행 순서를 확실히 숙지하여 순서가 빠른 구문에서 뒷 순서인 구문에서의 조건/처리를 수행할 수 있는지를 고민하는 과정이 필요하다고 생각된다.