검색 결과 페이지로 나누기

웹 애플리케이션은 데이터를 사용자에게 표시할 때 페이지를 나누는 경우가 많습니다. 최종 사용자는 결과 페이지 하나를 받으며 다음 페이지로 이동하면 다음 결과가 일괄로 검색되어 표시됩니다. 이 페이지에서는 Spanner에서 전체 텍스트 검색을 수행할 때 페이지로 나누기를 검색 결과에 추가하는 방법을 설명합니다.

개요

Spanner에서 페이지로 나눈 쿼리를 구현하는 방법에는 키 기반 페이지로 나누기(권장) 및 오프셋 기반 페이지로 나누기 등 2가지가 있습니다.

키 기반 페이지로 나누기는 요청 전반에서 일관된 결과를 보장하면서 검색 결과를 더 작고 관리하기 쉬운 청크로 가져오는 방법입니다. 페이지 마지막 결과의 고유한 식별자('키')가 다음 결과 집합을 가져오는 참조 지점으로 사용됩니다.

Spanner에서는 일반적으로 키 기반 페이지로 나누기를 사용하는 것이 좋습니다. 오프셋 기반 페이지로 나누기는 구현하기 쉽지만 두 가지 중요한 단점이 있습니다.

  1. 더 높은 쿼리 비용: 오프셋 기반 페이지로 나누기는 같은 결과를 반복해서 가져오고 삭제하므로 비용이 증가하고 성능이 저하됩니다.
  2. 일관되지 않는 결과: 페이지로 나눈 쿼리에서는 일반적으로 다른 읽기 타임스탬프에서 각 페이지를 가져옵니다. 예를 들어 첫 번째 페이지는 오후 1시에 실행된 쿼리에서, 다음 페이지는 오후 1시 10분에 실행된 쿼리에서 가져온 페이지일 수 있습니다. 즉, 쿼리 간에 검색 결과가 달라질 수 있으며 이로 인해 페이지 간에 결과가 일관되지 않을 수 있습니다.

반면 키 기반 페이지로 나누기는 페이지 마지막 결과의 고유 식별자(키)를 사용하여 다음 결과 집합을 가져옵니다. 이로 인해 기본 데이터가 변경되더라도 효율적인 가져오기와 일관된 결과가 모두 보장됩니다.

페이지 결과의 안정성을 제공하기 위해 애플리케이션은 같은 타임스탬프에 여러 페이지에 대한 모든 쿼리를 실행할 수 있습니다. 그러나 쿼리가 버전 보관 기간(기본값: 1시간)을 초과하면 실패할 수 있습니다. 예를 들어 version_gc가 1시간이고 최종 사용자가 오후 1시에 첫 번째 결과를 가져와 오후 3시에 다음을 클릭하면 이 오류가 발생합니다.

키 기반 페이지로 나누기 사용

키 기반 페이지로 나누기는 이전 페이지의 마지막 항목을 기억하고 이를 다음 페이지 쿼리의 시작점으로 사용합니다. 이렇게 하려면 쿼리에서 ORDER BY 절에 지정된 열을 반환하고 LIMIT를 사용하여 행 수를 제한해야 합니다.

키 기반 페이지로 나누기가 작동하려면 쿼리에서 엄격한 전체 순서에 따라 결과를 정렬해야 합니다. 사용하기 가장 쉬운 방법은 총 순서를 선택한 후 필요한 경우 tie-breaker 열을 추가하는 것입니다. 대부분의 경우 총 순서는 검색 색인 정렬 순서이고 고유한 열 조합은 기본 테이블 기본 키입니다.

Google의 Albums 샘플 스키마를 사용하면 첫 번째 페이지의 쿼리는 다음과 같습니다.

SELECT AlbumId, ReleaseTimestamp
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
ORDER BY ReleaseTimestamp DESC, AlbumId
LIMIT 10;

ReleaseTimestamp는 키가 아니므로 AlbumId가 타이브레이커입니다. ReleaseTimestamp 값이 같은 서로 다른 앨범 2개가 있을 수 있습니다.

재개하려면 애플리케이션에서 같은 쿼리를 다시 실행하지만 이전 페이지의 결과를 제한하는 WHERE 절을 사용합니다. 추가 조건은 키 방향(오름차순 또는 내림차순), 타이브레이커, null 허용 열의 NULL 값 순서를 고려해야 합니다.

이 예시에서 AlbumId는 유일한 키 열(오름차순)이고 NULL이 될 수 없으므로 조건은 다음과 같습니다.

SELECT AlbumId, ReleaseTimestamp
FROM Albums
WHERE (ReleaseTimestamp < @last_page_release_timestamp
       OR (ReleaseTimestamp = @last_page_release_timestamp
           AND AlbumId > @last_page_album_id))
      AND SEARCH(AlbumTitle_Tokens, @p)
ORDER BY ReleaseTimestamp DESC, AlbumId ASC
LIMIT @page_size;

Spanner는 이러한 종류의 조건을 검색 가능으로 해석합니다. 즉, Spanner는 필터링하는 문서의 색인을 읽지 않습니다. 이 최적화로 인해 키 기반 페이지 나누기가 오프셋 기반 페이지 나누기보다 훨씬 효율적입니다.

오프셋 기반 페이지로 나누기 사용

오프셋 기반 페이지로 나누기는 SQL 쿼리의 LIMITOFFSET 절을 활용하여 페이지를 시뮬레이션합니다. LIMIT 값은 페이지당 결과 수를 나타냅니다. OFFSET 값은 첫 번째 페이지의 경우 0으로, 두 번째 페이지의 경우 페이지 크기로, 세 번째 페이지의 경우 페이지 크기의 두 배로 설정됩니다.

예를 들어 다음 쿼리에서는 페이지 크기가 50인 세 번째 페이지를 가져옵니다.

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
ORDER BY ReleaseTimestamp DESC, AlbumId
LIMIT 50 OFFSET 100;

사용법 참고사항:

  • 페이지 간에 일관된 순서를 보장하려면 ORDER BY 절을 사용하는 것이 좋습니다.
  • 프로덕션 쿼리에서는 상수 대신 쿼리 파라미터를 사용하여 LIMITOFFSET을 지정해 쿼리 캐싱을 더 효율적으로 만듭니다. 자세한 내용은 쿼리 파라미터를 참조하세요.

다음 단계