SELECT
*
FROM 테이블
WHERE 조건문
ORDER BY id (FK) DESC
OFFSET 페이지 번호
LIMIT 페이지 사이즈
보통 페이징 쿼리는 OFFSET / LIMIT으로 다음과 같은 형태로 데이터를 가져오는데 이 부분에서 한가지 문제가 발생한다.
데이터가 비교적 적을 때는 무슨 문제가 있나 느낌이 오지 않지만
뒤로 갈수록 느려진다는 큰 문제점이 있다.
왜 뒤로 갈수록 늦어지냐?
앞에서 읽었던 행을 다시 읽는다는 문제가 있기 때문이다.
예로 들어서 offset 50,000 / limit 20 이라고 가정한다면 최종적으로 50,020개의 행을 읽어야 한다는 것이다.
행을 50,020개를 읽는다는 부분도 문제지만 20개의 데이터를 얻기 위해 앞의 50,000 개 행이 버려진다는 것이다.
이런 부분 떄문에 데이터가 많으면 많아질수록 버리지만 읽어야 할 행의 개수가 점점 많아져 느려진다는 것이다.
No Offset 방식은 이런 문제를 해결하기 위해 조회 시작 부분을 인덱스로
빠르게 찾아 매번 첫 페이지만 읽도록 하는 방식이다.
SELECT
*
FROM 테이블
WHERE 조건문
AND id < 직전 조회 결과의 마지막 id
ORDER BY id (FK) DESC
LIMIT 페이지사이즈
보통 페이지 번호가 있는 1번에서 9번으로 가는 케이스인 경우에는 해당 No Offset을 사용하기에는 무리수가 있으나,
인피니티 스크롤 혹은 더보기인 경우에는 No Offset을 사용할 수 있다.
아래 인피니티 스크롤 기준으로 작성한 PHP 예제 코드이다.
<?php
namespace app\Controllers;
use app\Repositories\TestRepository;
use core\{
Controller
};
class TestExample extends Controller
{
private $testRepository;
public function __construct()
{
$this->testRepository = new TestRepository();
}
public function infinityScrollIndex(int $pageId = NULL): void
{
$result = $this->testRepository->infinityScrollIndex($pageId);
print_r($result); exit;
}
}
<?php
namespace app\Interfaces;
interface TestInterface
{
public function infinityScrollIndex(?int $pageId): array;
}
<?php
namespace app\Repositories;
use app\Interfaces\TestInterface;
use app\Models\TestModel;
class TestRepository implements TestInterface
{
private $testModel;
public function __construct()
{
$this->testModel = new TestModel();
}
public function infinityScrollIndex(?int $pageId): array
{
$result = $this->testModel->infinityScrollIndex($pageId);
print_r($result); exit;
}
}
<?php
namespace app\Models;
use core\Model;
class TestModel extends Model
{
public function infinityScrollIndex(?int $pageId): array
{
$rowSql = '';
$rowField = [];
// 만약 직전 조회의 마지막 id 값이 있다면 해당 id 기준으로 미만인 데이터 조회
if (empty($pageId) === false) {
$rowSql .= "WHERE id < :pageId";
$rowField['pageId'] = $pageId;
}
$sql = "SELECT
*
FROM 테이블
{$rowSql}
ORDER BY id DESC
LIMIT 0, 페이지 사이즈";
// 상속받은 Model의 메소드
return $this->getData($sql, $rowField);
}
}
결과적으로 시작 기준 지점은 직전 조회의 마지막 결과 id보다 미만인 데이터를 가져오기에
속도 차원에서는 매우 큰 이점이 있다.
평소에 OFFSET / LIMIT 방식을 많이 사용하여 목록 API 같은 경우 불러와질 때 테스트 데이터가 많을 경우
응답 시간이 길어졌었는데 No Offset으로 변경한 후로는 큰 변동없이 무난하게 데이터를 뽑아와
페이지 내 퍼포먼스가 향상되었다.
심지어 인덱스를 타는거라 해당 인덱스 기준으로 조회를 해서 큰 문제는 없겠다라는 생각이었는데
그게 아니라는 것도 이번에 알게 되어 아직 부족함이 매우 많구나라는 생각이 들어 더욱 더 의욕이 생긴다.
'언어 & 프레임워크 > PHP' 카테고리의 다른 글
[Laravel] window 환경에서 프로젝트 생성 시 에러 (0) | 2022.11.09 |
---|---|
PHP 스프레드 연산자 (0) | 2022.11.09 |
PHP PDO json_encode strings as numbers 반환 오류 (2) | 2022.09.20 |
PHP 오토로드 (AutoLoad) (0) | 2022.09.20 |
PHP 매치 (Match) (1) | 2022.09.19 |