소개
이 페이지에서는 Spanner 쿼리 실행 계획에 사용되는 연산자를 자세히 설명합니다. Google Cloud 콘솔을 사용하여 특정 쿼리의 실행 계획을 검색하는 방법은 Spanner의 쿼리 실행 방법 이해를 참조하세요.
이 페이지의 쿼리 및 실행 계획은 다음 데이터베이스 스키마를 기반으로 합니다.
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX),
BirthDate DATE
) PRIMARY KEY(SingerId);
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX),
MarketingBudget INT64
) PRIMARY KEY(SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);
CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget);
CREATE TABLE Songs (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
TrackId INT64 NOT NULL,
SongName STRING(MAX),
Duration INT64,
SongGenre STRING(25)
) PRIMARY KEY(SingerId, AlbumId, TrackId),
INTERLEAVE IN PARENT Albums ON DELETE CASCADE;
CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC), INTERLEAVE IN Albums;
CREATE INDEX SongsBySongName ON Songs(SongName);
CREATE TABLE Concerts (
VenueId INT64 NOT NULL,
SingerId INT64 NOT NULL,
ConcertDate DATE NOT NULL,
BeginTime TIMESTAMP,
EndTime TIMESTAMP,
TicketPrices ARRAY<INT64>
) PRIMARY KEY(VenueId, SingerId, ConcertDate);
다음과 같은 데이터 조작 언어(DML) 문을 사용하여 이러한 테이블에 데이터를 추가할 수 있습니다.
INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate)
VALUES (1, "Marc", "Richards", "1970-09-03"),
(2, "Catalina", "Smith", "1990-08-17"),
(3, "Alice", "Trentor", "1991-10-02"),
(4, "Lea", "Martin", "1991-11-09"),
(5, "David", "Lomond", "1977-01-29");
INSERT INTO Albums (SingerId, AlbumId, AlbumTitle)
VALUES (1, 1, "Total Junk"),
(1, 2, "Go, Go, Go"),
(2, 1, "Green"),
(2, 2, "Forever Hold Your Peace"),
(2, 3, "Terrified"),
(3, 1, "Nothing To Do With Me"),
(4, 1, "Play");
INSERT INTO Songs (SingerId, AlbumId, TrackId, SongName, Duration, SongGenre)
VALUES (2, 1, 1, "Let's Get Back Together", 182, "COUNTRY"),
(2, 1, 2, "Starting Again", 156, "ROCK"),
(2, 1, 3, "I Knew You Were Magic", 294, "BLUES"),
(2, 1, 4, "42", 185, "CLASSICAL"),
(2, 1, 5, "Blue", 238, "BLUES"),
(2, 1, 6, "Nothing Is The Same", 303, "BLUES"),
(2, 1, 7, "The Second Time", 255, "ROCK"),
(2, 3, 1, "Fight Story", 194, "ROCK"),
(3, 1, 1, "Not About The Guitar", 278, "BLUES");
Leaf 연산자
Leaf 연산자는 하위 요소가 없는 연산자입니다. Leaf 연산자 유형은 다음과 같습니다.
배열 중첩 해제
배열 중첩 해제 연산자는 입력 배열을 요소 행으로 평면화합니다. 각 결과 행에는 배열의 실제 값과 0부터 시작하는 배열 내 위치(선택사항)가 들어 있는 최대 2개의 열이 포함됩니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT a, b FROM UNNEST([1,2,3]) a WITH OFFSET b;
이 쿼리는 a
열에 있는 [1,2,3]
배열을 평면화하고 b
열에 배열 위치를 표시합니다.
결과는 다음과 같습니다.
a | b |
---|---|
1 | 0 |
2 | 1 |
3 | 2 |
다음은 실행 계획입니다.
관계 생성
관계 생성 연산자는 0개 이상의 행을 반환합니다.
단위 관계
단위 관계는 1개의 행을 반환합니다. 이것은 관계 생성 연산자의 특별한 경우입니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT 1 + 2 AS Result;
결과는 다음과 같습니다.
결과 |
---|
3 |
다음은 실행 계획입니다.
빈 관계
빈 관계는 행을 반환하지 않습니다. 이것은 관계 생성 연산자의 특별한 경우입니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT *
FROM albums
LIMIT 0
결과는 다음과 같습니다.
결과 없음
다음은 실행 계획입니다.
스캔
스캔 연산자는 행 소스를 스캔하여 행을 반환합니다. 다음은 스캔 연산자의 유형입니다.
- 테이블 스캔: 테이블에서 스캔이 실행됩니다.
- 색인 스캔: 색인에서 스캔이 실행됩니다.
- 배치 스캔: 다른 관계형 연산자에서 생성된 중간 테이블 (예: 분산 교차 적용에서 생성된 테이블)에서 스캔이 실행됩니다.
Spanner는 가능하면 스캔의 일부로 키에 조건자를 적용합니다. 조건자가 적용되면 스캔 시 전체 테이블 또는 색인을 읽을 필요가 없으므로 스캔이 보다 효율적으로 실행됩니다. 조건자는 실행 계획에 KeyPredicate: column=value
형식으로 나타납니다.
최악의 경우에는 쿼리가 테이블에 있는 행을 모두 조회해야 할 수 있습니다. 이러한 상황은 전체 스캔으로 이어지고, 실행 계획에 full scan:
true
로 표시됩니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT s.lastname
FROM singers@{FORCE_INDEX=SingersByFirstLastName} as s
WHERE s.firstname = 'Catalina';
결과는 다음과 같습니다.
LastName |
---|
Smith |
다음은 실행 계획입니다.
실행 계획에서 최상위 수준의 분산 통합 연산자는 원격 서버로 하위 계획을 보냅니다. 각 하위 계획에는 결과 직렬화 연산자와 색인 스캔 연산자가 있습니다. 조건자 Key Predicate: FirstName = 'Catalina'
는 스캔을 색인 SingersByFirstLastname
에서 FirstName
이 Catalina
인 행으로 제한합니다. 색인 스캔 출력은 결과 직렬화 연산자로 반환됩니다.
단항 연산자
단항 연산자는 하나의 관계형 하위 요소가 있는 연산자입니다.
다음 연산자들이 단항 연산자입니다.
집계
집계 연산자는 GROUP BY
SQL 문과 집계 함수 (예: COUNT
)를 실행합니다. 집계 연산자의 입력은 키 열에 정렬된 그룹 (또는 GROUP BY
가 없는 경우 단일 그룹)으로 논리적으로 분할됩니다. 각 그룹에 대해 0개 이상의 집계가 계산됩니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT s.singerid,
Avg(s.duration) AS average,
Count(*) AS count
FROM songs AS s
GROUP BY singerid;
이 쿼리는 SingerId
를 기준으로 그룹화를 수행한 후 AVG
집계와 COUNT
집계를 실행합니다.
결과는 다음과 같습니다.
SingerId | average | count |
---|---|---|
3 | 278 | 1 |
2 | 225.875 | 8 |
다음은 실행 계획입니다.
집계 연산자는 스트림 기반 또는 해시 기반일 수 있습니다. 이전 실행 계획은 스트림 기반 집계를 보여줍니다. 스트림 기반 집계는 사전 정렬된 입력에서 읽고 (GROUP BY
가 있는 경우) 차단 없이 그룹을 컴퓨팅합니다. 해시 기반 집계는 여러 입력 행의 증분 집계를 동시에 유지하기 위해 해시 테이블을 작성합니다. 스트림 기반 집계는 해시 기반 집계보다 메모리를 적게 사용하고 빠르지만 입력을 정렬해야 합니다(키 열 또는 보조 색인 사용).
분산 시나리오의 경우 집계 연산자를 로컬-전역 쌍으로 분리할 수 있습니다. 각 원격 서버는 입력 행에서 로컬 집계를 수행한 후 결과를 루트 서버로 반환합니다. 루트 서버는 전역 집계를 수행합니다.
변형 적용
변형 적용 연산자는 데이터 조작 문 (DML)의 변형을 테이블에 적용합니다. 이는 DML 문의 쿼리 계획에서 최상위 연산자입니다.
예를 들어 다음 쿼리를 사용합니다.
DELETE FROM singers
WHERE firstname = 'Alice';
결과는 다음과 같습니다.
4 rows deleted This statement deleted 4 rows and did not return any rows.
다음은 실행 계획입니다.
배치 만들기
배치 만들기 연산자는 입력 행을 하나의 시퀀스로 일괄 처리합니다. 배치 만들기 연산은 일반적으로 분산 교차 적용 연산의 일부로 수행됩니다. 입력 행은 일괄 처리 중에 다시 정렬될 수 있습니다. 일괄 연산자를 실행할 때마다 일괄 처리되는 입력 행 수는 달라집니다.
실행 계획에서 배치 만들기 연산자의 예시를 보려면 분산 교차 적용 연산자를 참조하세요.
컴퓨팅
컴퓨팅 연산자는 입력 행을 읽고 스칼라 표현식을 사용하여 계산된 추가 열을 한 개 이상 추가하여 출력을 생성합니다. 실행 계획에서 컴퓨팅 연산자의 예시를 보려면 전체 통합 연산자를 참조하세요.
컴퓨팅 구조체
컴퓨팅 구조체 연산자는 각 입력 열의 필드가 있는 구조체 변수를 만듭니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT FirstName,
ARRAY(SELECT AS STRUCT song.SongName, song.SongGenre
FROM Songs AS song
WHERE song.SingerId = singer.SingerId)
FROM singers AS singer
WHERE singer.SingerId = 3;
결과는 다음과 같습니다.
FirstName | 미지정 |
---|---|
Alice | [["Not About The Guitar","BLUES"]] |
다음은 실행 계획입니다.
실행 계획에서 배열 서브 쿼리 연산자는 분산 통합 연산자로부터 입력을 받으며 분산 통합 연산자는 컴퓨팅 구조체 연산자로부터 입력을 받습니다. 컴퓨팅 구조체 연산자는 Songs
테이블의 SongName
및 SongGenre
열에서 구조체를 만듭니다.
필터
필터 연산자는 입력에서 모든 행을 읽고 각 행에서 스칼라 조건자를 적용한 후 이 조건자를 충족하는 행만 반환합니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT s.lastname
FROM (SELECT s.lastname
FROM singers AS s
LIMIT 3) s
WHERE s.lastname LIKE 'Rich%';
결과는 다음과 같습니다.
LastName |
---|
Richards |
다음은 실행 계획입니다.
성이 Rich
로 시작하는 가수에 대한 조건자가 필터로 구현됩니다. 이 필터의 입력은 색인 스캔의 출력이고, 필터 출력은 LastName
이 Rich
로 시작하는 행입니다.
성능을 위해 필터가 스캔 위에 직접 위치할 때마다 필터는 데이터가 판독되는 방식에 영향을 줍니다. 예를 들어 키 k
가 있는 테이블을 생각해보겠습니다.
테이블의 스캔 바로 위에 조건자 k = 5
가 있는 필터는 전체 입력을 읽지 않고 k = 5
와 일치하는 행을 찾습니다. 이렇게 하면 쿼리가 보다 효율적으로 실행됩니다. 이전 예에서 필터 연산자는 WHERE s.LastName LIKE 'Rich%'
조건자를 충족하는 행만 읽습니다.
필터 스캔
필터 스캔 연산자는 항상 테이블 또는 색인 스캔의 맨 위에 있습니다. 이 연산자는 스캔을 사용하여 데이터베이스에서 읽은 행의 수를 줄이므로 일반적으로 필터를 사용하는 경우보다 스캔이 더 빠릅니다. Spanner는 특정 조건에서 필터 스캔을 적용합니다.
- 검색 가능한 조건: 검색 가능한 조건은 Spanner가 테이블에서 액세스할 특정 행을 결정할 수 있는 경우에 적용됩니다. 일반적으로는 필터가 기본 키의 접두어에 있는 경우가 이에 해당합니다. 예를 들어 기본 키가
Col1
과Col2
로 구성된 경우Col1
또는Col1
과Col2
의 명시적 값을 포함하는WHERE
절을 검색할 수 있습니다. 이 경우 Spanner는 키 범위 내에서만 데이터를 읽습니다. - 잔여 조건: 읽는 데이터의 양을 제한하기 위해 Spanner가 스캔을 평가할 수 있는 그 밖의 모든 조건입니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT lastname
FROM singers
WHERE singerid = 1
결과는 다음과 같습니다.
LastName |
---|
Richards |
다음은 실행 계획입니다.
한도
제한 연산자는 반환되는 행 수를 제한합니다. 선택사항인 OFFSET
매개변수는 반환할 시작 행을 지정합니다. 분산 시나리오의 경우 제한 연산자를 로컬-전역 쌍으로 분리할 수 있습니다. 각 원격 서버는 출력 행에 로컬 제한을 적용한 후 결과를 루트 서버에 반환합니다. 루트 서버는 원격 서버에서 보낸 행을 집계한 후 전역 제한을 적용합니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT s.songname
FROM songs AS s
LIMIT 3;
결과는 다음과 같습니다.
SongName |
---|
Not About The Guitar |
The Second Time |
Starting Again |
다음은 실행 계획입니다.
로컬 제한은 각 원격 서버에 대한 제한입니다. 루트 서버는 원격 서버의 행을 집계한 후 전역 제한을 적용합니다.
임의 ID 할당
임의 ID 할당 연산자는 입력 행을 읽고 각 행에 임의의 숫자를 추가하여 출력을 생성합니다. Filter
또는 Sort
연산자와 함께 작동하여 샘플링 방법을 수행합니다. 지원되는 샘플링 방법으로는 Bernoulli와 Reservoir가 있습니다.
예를 들어 다음 쿼리에서는 샘플링 레이트가 10%인 Bernoulli 샘플링을 사용합니다.
SELECT s.songname
FROM songs AS s TABLESAMPLE bernoulli (10 PERCENT);
결과는 다음과 같습니다.
SongName |
---|
Starting Again |
Nothing Is The Same |
결과는 샘플이므로 쿼리가 동일해도 쿼리를 실행할 때마다 결과가 달라질 수 있습니다.
다음은 실행 계획입니다.
이 실행 계획에서 Random Id Assign
샘플 연산자는 분산 통합 연산자로부터 입력을 받으며 분산 통합 연산자는 색인 스캔 연산자로부터 입력을 받습니다. 연산자는 임의 ID가 있는 행을 반환하고 Filter
연산자는 임의 ID에 스칼라 조건자를 적용하고 행의 약 10%를 반환합니다.
다음 예에서는 행 2개의 샘플링 레이트로 Reservoir 샘플링을 사용합니다.
SELECT s.songname
FROM songs AS s TABLESAMPLE reservoir (2 rows);
결과는 다음과 같습니다.
SongName |
---|
I Knew You Were Magic |
The Second Time |
결과는 샘플이므로 쿼리가 동일해도 쿼리를 실행할 때마다 결과가 달라질 수 있습니다.
다음은 실행 계획입니다.
이 실행 계획에서 Random Id Assign
샘플 연산자는 분산 통합 연산자로부터 입력을 받으며 분산 통합 연산자는 색인 스캔 연산자로부터 입력을 받습니다. 연산자는 임의 ID가 있는 행을 반환하고 Sort
연산자는 임의 ID에 정렬 순서를 적용하고 2개의 행으로 LIMIT
을 적용합니다.
로컬 분할 조합
로컬 분할 통합 연산자는 로컬 서버에 저장된 테이블 분할을 찾고 각 분할에서 서브쿼리를 실행한 후 모든 결과를 결합하는 통합을 만듭니다.
로컬 분할 통합은 배치 테이블을 스캔하는 실행 계획에 나타납니다. 게재위치를 사용하면 테이블의 분할 수를 늘릴 수 있으므로 실제 저장소 위치를 기반으로 일괄적으로 분할을 스캔하는 것이 더 효율적입니다.
예를 들어 Singers
테이블에서 배치 키를 사용하여 가수 데이터를 파티셔닝한다고 가정해 보겠습니다.
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
SingerName STRING(MAX) NOT NULL,
...
Location STRING(MAX) NOT NULL PLACEMENT KEY
) PRIMARY KEY (SingerId);
다음 쿼리를 생각해 보세요.
SELECT BirthDate FROM Singers;
다음은 실행 계획입니다.
분산된 합집합은 동일한 서버에 물리적으로 함께 저장된 각 분할 배치로 서브 쿼리를 전송합니다. 각 서버에서 로컬 분할 통합은 Singers
데이터를 저장하는 분할을 찾고, 각 분할에서 서브 쿼리를 실행하고, 결합된 결과를 반환합니다. 이렇게 하면 분산된 연합과 로컬 분할 연합이 함께 작동하여 Singers
테이블을 효율적으로 스캔합니다.
로컬 분할 통합이 없으면 분할 배치당이 아닌 분할당 하나의 RPC를 전송하므로 배치당 분할이 두 개 이상인 경우 RPC 왕복이 중복됩니다.
결과 직렬화
결과 직렬화 연산자는 클라이언트에 반환하기 위해 쿼리의 최종 결과에 있는 각 행을 직렬화하는 컴퓨팅 구조체 연산자의 특수한 경우입니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT array
(
select as struct so.songname,
so.songgenre
FROM songs AS so
WHERE so.singerid = s.singerid)
FROM singers AS s;
이 쿼리는 SingerId
를 기준으로 SongName
과 SongGenre
로 구성된 배열을 요청합니다.
결과는 다음과 같습니다.
미지정 |
---|
[] |
[[Let's Get Back Together, COUNTRY], [Starting Again, ROCK]] |
[[Not About The Guitar, BLUES]] |
[] |
[] |
다음은 실행 계획입니다.
결과 직렬화 연산자는 Singers
테이블의 각 행에 대해 가수의 곡에 대한 SongName
과 SongGenre
쌍의 배열을 포함하는 결과를 만듭니다.
정렬
정렬 연산자는 입력 행을 읽고 열을 기준으로 이를 정렬한 후 정렬된 결과를 반환합니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT s.songgenre
FROM songs AS s
ORDER BY songgenre;
결과는 다음과 같습니다.
SongGenre |
---|
블루스 |
블루스 |
블루스 |
블루스 |
클래식 |
컨트리 |
록 |
록 |
록 |
다음은 실행 계획입니다.
이 실행 계획에서 정렬 연산자는 분산 통합 연산자로부터 입력 행을 받아 입력 행을 정렬하고 정렬된 행을 결과 직렬화 연산자로 반환합니다.
반환되는 행 수를 제한하기 위해 정렬 연산자는 선택적으로 LIMIT
및 OFFSET
매개변수를 사용할 수 있습니다. 분산 시나리오의 경우 LIMIT
또는 OFFSET
연산자가 있는 정렬 연산자는 로컬-전역 쌍으로 분리됩니다. 각 원격 서버는 입력 행에 대한 정렬 순서와 로컬 제한 또는 오프셋을 적용한 후 결과를 루트 서버에 반환합니다. 루트 서버는 원격 서버가 보낸 행을 집계하고 정렬한 후 전역 제한/오프셋을 적용합니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT s.songgenre
FROM songs AS s
ORDER BY songgenre
LIMIT 3;
결과는 다음과 같습니다.
SongGenre |
---|
블루스 |
블루스 |
블루스 |
다음은 실행 계획입니다.
실행 계획에는 원격 서버에 대한 로컬 제한과 루트 서버에 대한 전역 제한이 표시됩니다.
TVF
테이블 값 함수 연산자는 입력 행을 읽고 지정된 함수를 적용하여 출력을 생성합니다. 함수는 매핑을 구현하고 입력과 동일한 수의 행을 반환할 수 있습니다. 더 많은 행을 반환하는 생성자이거나 더 적은 행을 반환하는 필터일 수도 있습니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT genre,
songname
FROM ml.predict(model genreclassifier, TABLE songs)
결과는 다음과 같습니다.
장르 | SongName |
---|---|
컨트리 | Not About The Guitar |
록 | The Second Time |
팝 | Starting Again |
팝 | Nothing Is The Same |
컨트리 | Let's Get Back Together |
팝 | I Knew You Were Magic |
일렉트릭 | Blue |
록 | 42 |
록 | Fight Story |
다음은 실행 계획입니다.
통합 입력
통합 입력 연산자는 결과를 전체 통합 연산자로 반환합니다. 실행 계획에서 통합 입력 연산자의 예시를 보려면 전체 통합 연산자를 참조하세요.
바이너리 연산자
바이너리 연산자는 2개의 관계형 하위 요소가 있는 연산자입니다. 다음 연산자는 바이너리 연산자입니다.
교차 적용
교차 적용 연산자는 다른 테이블의 쿼리로 검색된 각 행의 테이블 쿼리를 실행하고 모든 테이블 쿼리 실행 통합을 반환합니다. 교차 적용 및 외부 적용 연산자는 해시 조인처럼 집합 기반 처리를 실행하는 연산자와는 달리 행 기반 처리를 실행합니다. 교차 적용 연산자에는 입력과 맵 등 2가지 입력이 있습니다. 교차 적용 연산자는 입력 측의 각 행을 맵 측에 적용합니다. 교차 적용 결과에는 입력 및 맵 측 모두의 열이 있습니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT si.firstname,
(SELECT so.songname
FROM songs AS so
WHERE so.singerid = si.singerid
LIMIT 1)
FROM singers AS si;
쿼리는 각 가수의 성과 가수의 곡 중 한 곡 이름만 요청합니다.
결과는 다음과 같습니다.
FirstName | 미지정 |
---|---|
Alice | Not About The Guitar |
Catalina | Let's Get Back Together |
David | NULL |
Lea | NULL |
Marc | NULL |
첫 번째 열에는 Singers
테이블의 내용이 채워지고 두 번째 열에는 Songs
테이블의 내용이 채워집니다. Singers
테이블에 SingerId
1개가 있지만 Songs
테이블에 일치하는 SingerId
가 없으면 두 번째 열에 NULL
이 포함됩니다.
다음은 실행 계획입니다.
최상위 노드는 분산 통합 연산자입니다. 분산 통합 연산자는 원격 서버에 하위 계획을 분배합니다. 이 하위 계획에는 가수의 성과 발표곡 중 한 곡의 이름을 컴퓨팅하고 출력의 각 행을 직렬화하는 결과 직렬화 연산자가 포함되어 있습니다.
결과 직렬화 연산자는 교차 적용 연산자에서 입력을 받습니다.
교차 적용 연산자의 입력 측은 Singers
테이블에 대한 테이블 스캔입니다.
교차 적용 연산의 맵 측에는 다음이 포함됩니다(위에서 아래로).
Songs.SongName
을 반환하는 집계 연산자- 가수 1명당 반환되는 곡 수를 제한하는 제한 연산자
SongsBySingerAlbumSongNameDesc
색인에 대한 색인 스캔
교차 적용 연산자는 입력 측의 각 행을 동일한 SingerId
를 가진 맵 측의 행에 매핑합니다. 교차 적용 연산자 출력은 입력 행의 FirstName
값과 맵 행의 SongName
값입니다.
SingerId
에 일치하는 맵 행이 없으면 SongName
값은 NULL
입니다. 그런 다음 실행 계획 맨 위에 있는 분산 통합 연산자가 원격 서버의 모든 출력 행을 결합하여 쿼리 결과로 반환합니다.
해시 조인
해시 조인 연산자는 해시에 기반해 SQL 조인을 구현한 것입니다. 해시 조인은 집합 기반 프로세싱을 실행합니다. 해시 조인 연산자는 빌드로 표시된 입력에서 행을 읽고 이를 조인 조건에 따라 해시 테이블에 삽입합니다. 그런 다음 해시 조인 연산자는 프로브로 표시된 입력에서 행을 읽습니다. 프로브 입력에서 읽는 각 행에 대해 해시 조인 연산자는 해시 테이블에서 일치하는 행을 찾습니다. 해시 조인 연산자는 일치하는 행을 결과로 반환합니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT a.albumtitle,
s.songname
FROM albums AS a join@{join_method=hash_join} songs AS s
ON a.singerid = s.singerid
AND a.albumid = s.albumid;
결과는 다음과 같습니다.
AlbumTitle | SongName |
---|---|
Nothing To Do With Me | Not About The Guitar |
Green | The Second Time |
Green | Starting Again |
Green | Nothing Is The Same |
Green | Let's Get Back Together |
Green | I Knew You Were Magic |
Green | Blue |
Green | 42 |
Terrified | Fight Story |
다음은 실행 계획입니다.
실행 계획에서 빌드는 Albums
테이블에 대한 스캔을 분산하는 분산 통합입니다. 프로브는 SongsBySingerAlbumSongNameDesc
색인에 대한 스캔을 분산하는 분산 통합 연산자입니다.
해시 조인 연산자는 빌드 측의 모든 행을 읽습니다. 각 빌드 행은 조건 a.SingerId =
s.SingerId AND a.AlbumId = s.AlbumId
의 열을 기반으로 해시 테이블에 배치됩니다. 그런 다음 해시 조인 연산자는 프로브 측의 모든 행을 읽습니다. 각 스캔 행에 대해 해시 조인 연산자는 해시 테이블에서 일치하는 항목을 찾습니다. 일치 항목은 해시 조인 연산자에 의해 반환됩니다.
해시 테이블에서의 일치 결과는 반환되기 전에 나머지 조건으로 필터링될 수도 있습니다. (나머지 조건이 나타나는 부분의 예시는 비동등 조인에 있습니다.) 해시 조인 실행 계획은 메모리 관리 및 조인 변형으로 인해 복잡할 수 있습니다. 메인 해시 조인 알고리즘은 내부, 반, 반대, 외부 조인 변형을 처리하도록 조정됩니다.
병합 조인
병합 조인 연산자는 병합을 기반으로 구현한 SQL 조인입니다. 조인의 양측 모두 조인 조건에 사용된 열로 정렬된 행을 생성합니다. 병합 조인은 두 입력 스트림을 동시에 사용하고 조인 조건이 충족되었을 때 행을 출력합니다. 입력이 처음에 필요한 방식으로 정렬되지 않았으면 최적화 도구가 계획에 명시적인 Sort
연산자를 추가합니다.
병합 조인은 최적화 도구에서 자동으로 선택되지 않습니다. 이 연산자를 사용하려면 다음 예시에 표시된 것처럼 쿼리 힌트에서 조인 메서드를 MERGE_JOIN
에 설정합니다.
SELECT a.albumtitle,
s.songname
FROM albums AS a join@{join_method=merge_join} songs AS s
ON a.singerid = s.singerid
AND a.albumid = s.albumid;
결과는 다음과 같습니다.
AlbumTitle | SongName |
---|---|
Green | The Second Time |
Green | Starting Again |
Green | Nothing Is The Same |
Green | Let's Get Back Together |
Green | I Knew You Were Magic |
Green | Blue |
Green | 42 |
Terrified | Fight Story |
Nothing To Do With Me | Not About The Guitar |
다음은 실행 계획입니다.
이 실행 계획에서 병합 조인은 데이터가 있는 위치에서 조인이 실행되도록 분산됩니다. 또한 이 예시에서는 두 테이블 스캔이 조인 조건인 SingerId
, AlbumId
를 기준으로 이미 정렬되어 있으므로 추가 정렬 연산자 없이 이 예시에서 병합 조인을 사용할 수 있습니다. 이 계획에서는 Albums
테이블의 왼쪽 스캔은 SingerId
, AlbumId
가 오른쪽 SongsBySingerAlbumSongNameDesc
색인 스캔인 SingerId_1
, AlbumId_1
쌍보다 상대적으로 작을 때 진행됩니다.
마찬가지로 오른쪽은 왼쪽보다 작을 때마다 오른쪽이 진행됩니다. 이 병합 진행은 결과 일치가 반환될 수 있도록 등가를 계속 검색합니다.
다음 쿼리를 사용하는 또 다른 병합 조인 예시를 살펴보세요.
SELECT a.albumtitle,
s.songname
FROM albums AS a join@{join_method=merge_join} songs AS s
ON a.albumid = s.albumid;
결과는 다음과 같습니다.
AlbumTitle | SongName |
---|---|
Total Junk | The Second Time |
Total Junk | Starting Again |
Total Junk | Nothing Is The Same |
Total Junk | Let's Get Back Together |
Total Junk | I Knew You Were Magic |
Total Junk | Blue |
Total Junk | 42 |
Total Junk | Not About The Guitar |
Green | The Second Time |
Green | Starting Again |
Green | Nothing Is The Same |
Green | Let's Get Back Together |
Green | I Knew You Were Magic |
Green | Blue |
Green | 42 |
Green | Not About The Guitar |
Nothing To Do With Me | The Second Time |
Nothing To Do With Me | Starting Again |
Nothing To Do With Me | Nothing Is The Same |
Nothing To Do With Me | Let's Get Back Together |
Nothing To Do With Me | I Knew You Were Magic |
Nothing To Do With Me | Blue |
Nothing To Do With Me | 42 |
Nothing To Do With Me | Not About The Guitar |
Play | The Second Time |
Play | Starting Again |
Play | Nothing Is The Same |
재생 | Let's Get Back Together |
재생 | I Knew You Were Magic |
Play | Blue |
Play | 42 |
Play | Not About The Guitar |
Terrified | Fight Story |
다음은 실행 계획입니다.
앞의 실행 계획에서 추가 Sort
연산자는 실행할 병합 조인에 필요한 속성을 얻기 위해 쿼리 최적화 도구로 도입되었습니다. 이 예시 쿼리에서 JOIN
조건은 AlbumId
에만 있으며, 이것은 데이터 저장 방식이 아니므로, 정렬을 추가해야 합니다. 쿼리 엔진은 전역 대신 로컬로 정렬할 수 있게 해주는 분산 병합 알고리즘을 지원하여, CPU 비용을 분산 및 병렬화합니다.
결과 일치는 반환되기 전에 나머지 조건으로 필터링될 수도 있습니다. (나머지 조건이 나타나는 부분의 예시는 비동등 조인에 있습니다.) 추가 정렬 요구사항으로 인해 병합 조인 실행 계획이 복잡해질 수 있습니다. 메인 병합 조인 알고리즘은 내부, 반, 반대, 외부 조인 변형을 처리하도록 조정됩니다.
푸시 브로드캐스트 해시 조인
푸시 브로드캐스트 해시 조인 연산자는 분산 해시 조인을 기반으로 구현된 SQL 조인입니다. 푸시 브로드캐스트 해시 조인 연산자는 데이터 배치를 생성하기 위해 입력 측에서 행을 읽습니다. 그런 후 이 배치가 맵 측면 데이터를 포함하는 모든 서버에 브로드캐스트됩니다. 데이터 배치가 수신된 대상 서버에서 해시 조인은 배치를 빌드 측 데이터로 사용하여 빌드되고, 그런 후 로컬 데이터가 해시 조인의 프로브 측으로 스캔됩니다.
푸시 브로드캐스트 해시 조인은 최적화 도구에서 자동으로 선택되지 않습니다. 이 연산자를 사용하려면 다음 예시에 표시된 것처럼 쿼리 힌트에서 조인 메서드를 PUSH_BROADCAST_HASH_JOIN
에 설정합니다.
SELECT a.albumtitle,
s.songname
FROM albums AS a join@{join_method=push_broadcast_hash_join} songs AS s
ON a.singerid = s.singerid
AND a.albumid = s.albumid;
결과는 다음과 같습니다.
AlbumTitle | SongName |
---|---|
Green | The Second Time |
Green | Starting Again |
Green | Nothing Is The Same |
Green | Lets Get Back Together |
Green | I Knew You Were Magic |
Green | Blue |
Green | 42 |
Terrified | Fight Story |
Nothing To Do With Me | Not About The Guitar |
다음은 실행 계획입니다.
푸시 브로드캐스트 해시 조인에 대한 입력은 AlbumsByAlbumTitle
색인입니다.
이러한 입력은 데이터의 배치로 직렬화됩니다. 그런 후 이 배치가 SongsBySingerAlbumSongNameDesc
색인의 모든 로컬 분할로 전송됩니다. 여기서 배치는 역직렬화되고 해시 테이블로 빌드됩니다. 그런 후 해시 테이블은 로컬 색인 데이터를 프로브로 사용하여 결과 일치 항목을 반환합니다.
결과 일치는 반환되기 전에 나머지 조건으로 필터링될 수도 있습니다. (나머지 조건이 나타나는 부분의 예시는 비동등 조인에 있습니다.)
외부 적용
외부 적용 연산자는 필요할 경우 NULL로 채워진 행을 작성하여 맵 측의 각 실행이 행을 1개 이상 반환하도록 한다는 점만 제외하고는 교차 적용 연산자와 유사합니다. (즉, 왼쪽 외부 조인 의미를 제공합니다.)
재귀적 연합
재귀적 합집합 연산자는 base
케이스를 나타내는 입력과 recursive
케이스를 나타내는 입력의 합집합을 실행합니다. 이 키워드는 수치화된 경로 탐색이 있는 그래프 쿼리에서 사용됩니다. 기본 입력은 먼저 정확히 한 번 처리됩니다. 재귀 입력은 재귀가 종료될 때까지 처리됩니다. 재귀는 지정된 경우 상한에 도달하거나 재귀에서 새 결과를 생성하지 못할 때 종료됩니다. 다음 예에서는 Collaborations
테이블이 스키마에 추가되고 MusicGraph
라는 속성 그래프가 생성됩니다.
CREATE TABLE Collaborations (
SingerId INT64 NOT NULL,
FeaturingSingerId INT64 NOT NULL,
AlbumTitle STRING(MAX) NOT NULL,
) PRIMARY KEY(SingerId, FeaturingSingerId, AlbumTitle);
CREATE OR REPLACE PROPERTY GRAPH MusicGraph
NODE TABLES(
Singers
KEY(SingerId)
LABEL Singers PROPERTIES(
BirthDate,
FirstName,
LastName,
SingerId,
SingerInfo)
)
EDGE TABLES(
Collaborations AS CollabWith
KEY(SingerId, FeaturingSingerId, AlbumTitle)
SOURCE KEY(SingerId) REFERENCES Singers(SingerId)
DESTINATION KEY(FeaturingSingerId) REFERENCES Singers(SingerId)
LABEL CollabWith PROPERTIES(
AlbumTitle,
FeaturingSingerId,
SingerId),
);
다음 그래프 쿼리는 특정 가수와 공동작업했거나 해당 공동작업자와 공동작업한 가수를 찾습니다.
GRAPH MusicGraph
MATCH (singer:Singers {singerId:42})-[c:CollabWith]->{1,2}(featured:Singers)
RETURN singer.SingerId AS singer, featured.SingerId AS featured
재귀적 합집합 연산자는 Singers
테이블을 필터링하여 지정된 SingerId
가 있는 가수를 찾습니다. 이는 재귀적 합집합의 기본 입력입니다. 재귀 연합의 재귀 입력은 Collaborations
테이블을 이전 조인 반복의 결과와 반복적으로 조인하는 다른 쿼리의 분산 교차 적용 또는 기타 조인 연산자로 구성됩니다. 기본 입력의 행은 0번째 반복을 형성합니다. 반복할 때마다 반복의 출력은 재귀 스풀 검사에 의해 저장됩니다. 재귀 스풀 스캔의 행이 spoolscan.featuredSingerId =
Collaborations.SingerId
의 Collaborations
테이블과 조인됩니다. 재귀는 두 번의 반복이 완료되면 종료됩니다. 쿼리에서 지정된 상한값이기 때문입니다.
N-항 연산자
N-항 연산자는 관계형 하위 요소가 2개 이상 있는 연산자입니다. 다음 연산자가 N-항 연산자입니다.
전체 통합
전체 통합 연산자는 중복을 제거하지 않고 하위 항목의 모든 행 집합을 결합합니다. 전체 통합 연산자는 여러 서버에 분산된 통합 입력 연산자로부터 입력을 받습니다. 전체 통합 연산자에는 동일한 스키마를 갖는 입력이 필요합니다. 즉, 각 열에 대해 동일한 데이터 유형 집합이 필요합니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT 1 a,
2 b
UNION ALL
SELECT 3 a,
4 b
UNION ALL
SELECT 5 a,
6 b;
하위 요소의 행 유형은 정수 2개로 구성됩니다.
결과는 다음과 같습니다.
a | b |
---|---|
1 | 2 |
3 | 4 |
5 | 6 |
다음은 실행 계획입니다.
전체 통합 연산자는 입력 행을 결합하며, 이 예시에서는 결과를 결과 직렬화 연산자로 보냅니다.
하위 요소가 열 이름에 대해 다른 변수를 사용하더라도 각 열에 동일한 데이터 유형 집합이 사용되므로, 다음과 같은 쿼리가 성공합니다.
SELECT 1 a,
2 b
UNION ALL
SELECT 3 c,
4 e;
하위 요소가 열에 다른 데이터 유형을 사용하므로 다음과 같은 쿼리는 실패합니다.
SELECT 1 a,
2 b
UNION ALL
SELECT 3 a,
'This is a string' b;
스칼라 서브 쿼리
스칼라 서브 쿼리는 스칼라 표현식의 일부인 SQL 하위 표현식입니다. Spanner는 가능한 경우 항상 스칼라 서브 쿼리 제거를 시도합니다. 그러나 특정 시나리오에서는 계획에 스칼라 서브 쿼리가 명시적으로 포함될 수 있습니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT firstname,
IF(firstname = 'Alice', (SELECT Count(*)
FROM songs
WHERE duration > 300), 0)
FROM singers;
다음은 SQL 하위 표현식입니다.
SELECT Count(*)
FROM songs
WHERE duration > 300;
다음은 전체 쿼리 결과입니다.
FirstName | |
---|---|
Alice | 1 |
Catalina | 0 |
David | 0 |
Lea | 0 |
Marc | 0 |
다음은 실행 계획입니다.
이 실행 계획은 집계 연산자 위에 Scalar Subquery로 표시된 스칼라 서브 쿼리를 포함합니다.
Spanner는 때로 스칼라 서브 쿼리를 조인 또는 교차 적용과 같은 다른 연산자로 변환하여 성능을 향상시킬 수 있습니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT *
FROM songs
WHERE duration = (SELECT Max(duration)
FROM songs);
다음은 SQL 하위 표현식입니다.
SELECT MAX(Duration)
FROM Songs;
다음은 전체 쿼리 결과입니다.
SingerId | AlbumId | TrackId | SongName | 소요 시간 | SongGenre |
---|---|---|---|---|---|
2 | 1 | 6 | Nothing Is The Same | 303 | 블루스 |
다음은 실행 계획입니다.
Spanner가 스칼라 서브 쿼리를 교차 적용으로 변환했으므로, 실행 계획에는 스칼라 서브 쿼리가 포함되지 않습니다.
배열 서브 쿼리
배열 서브 쿼리는 스칼라 서브 쿼리와 비슷하지만 서브 쿼리가 입력 행을 2개 이상 사용할 수 있다는 점이 다릅니다. 소비된 행은 소비된 입력 행당 하나의 요소를 포함하는 단일 스칼라 출력 배열로 변환됩니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT a.albumid,
array
(
select concertdate
FROM concerts
WHERE concerts.singerid = a.singerid)
FROM albums AS a;
다음은 서브 쿼리입니다.
SELECT concertdate
FROM concerts
WHERE concerts.singerid = a.singerid;
각 AlbumId
에 대한 서브 쿼리 결과가 해당 AlbumId
에 대응한 ConcertDate
행 배열로 변환됩니다. 실행 계획에는 분산 통합 연산자 위에 Array Subquery로 표시된 배열 서브 쿼리가 포함됩니다.
분산 연산자
이 페이지의 앞에서 설명한 연산자는 단일 머신 경계 내에서 실행됩니다. 분산 연산자는 여러 서버에서 실행됩니다.
다음 연산자가 분산 연산자입니다.
분산 통합 연산자는 분산 교차 적용 및 분산 외부 적용을 파생시킨 기본 연산자입니다.
분산 연산자는 1개 이상의 로컬 분산 통합 변형 위에 분산 통합 변형을 사용하여 실행 계획에 나타납니다. 분산 통합 변형은 하위 계획의 원격 배포를 수행합니다. 이 실행 계획에 표시된 것처럼 로컬 분산 통합 변형은 쿼리에 대해 수행된 각 스캔 위에 있습니다.
로컬 분산 통합 변형은 동적으로 변하는 분할 경계에 대해 재시작이 발생하는 경우 안정적인 쿼리 실행을 보장합니다.
가능한 경우 분산 통합 변형에는 분할 잘라내기를 유발하는 분할 조건자가 있습니다. 즉, 원격 서버는 조건자를 충족하는 분할에만 하위 계획을 실행합니다. 이렇게 하면 지연 시간과 전반적인 쿼리 성능이 향상됩니다.
분산 통합
분산 통합 연산자는 테이블 1개 이상을 개념적으로 여러 분할로 분할하고 각 분할에서 독립적으로 서브 쿼리를 원격 평가한 후 모든 결과를 통합합니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT s.songname,
s.songgenre
FROM songs AS s
WHERE s.singerid = 2
AND s.songgenre = 'ROCK';
결과는 다음과 같습니다.
SongName | SongGenre |
---|---|
Starting Again | 록 |
The Second Time | 록 |
Fight Story | 록 |
다음은 실행 계획입니다.
분산 통합 연산자는 분할 전체에서 테이블 스캔을 수행하는 원격 서버로 하위 계획을 보냅니다. 이때 분할은 쿼리의 조건자 WHERE
s.SingerId = 2 AND s.SongGenre = 'ROCK'
을 만족해야 합니다. 결과 직렬화 연산자는 테이블 스캔에서 반환된 행으로부터 SongName
및 SongGenre
값을 계산합니다. 그런 다음 분산 통합 연산자는 원격 서버에서 결합된 결과를 SQL 쿼리 결과로 반환합니다.
분산 병합 통합
분산 병합 통합 연산자는 여러 원격 서버에 쿼리를 분산합니다. 그런 다음 쿼리 결과를 결합하여 분산 병합 정렬이라고 하는 정렬된 결과를 생성합니다.
분산 병합 통합은 다음 단계를 실행합니다.
루트 서버는 쿼리된 데이터의 분할을 호스팅하는 각 원격 서버로 서브 쿼리를 보냅니다. 서브 쿼리에는 결과가 특정 순서로 정렬되는 지침이 포함됩니다.
각 원격 서버는 분할에서 서브 쿼리를 실행한 후 요청된 순서대로 결과를 다시 보냅니다.
루트 서버는 정렬된 서브 쿼리를 병합하여 완전히 정렬된 결과를 생성합니다.
분산 병합 통합은 Spanner 버전 3 이상에 기본적으로 사용 설정되어 있습니다.
분산 교차 적용
분산 교차 적용(DCA) 연산자는 교차 적용 연산자를 여러 서버에서 실행하여 확장합니다. DCA 입력 측은 행의 배치를 그룹화합니다(한 번에 입력 행 1개에서만 작업을 수행하는 일반 교차 적용 연산자와 다름). DCA 맵 측은 원격 서버에서 실행되는 교차 적용 연산자 조합입니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT albumtitle
FROM songs
JOIN albums
ON albums.albumid = songs.albumid;
결과는 다음과 같은 형식입니다.
AlbumTitle |
---|
Green |
Nothing To Do With Me |
Play |
Total Junk |
Green |
다음은 실행 계획입니다.
DCA 입력에는 AlbumId
의 행을 일괄 처리하는 SongsBySingerAlbumSongNameDesc
색인에 대한 색인 스캔이 포함됩니다. 이 교차 적용 연산자의 맵 측은 AlbumsByAlbumTitle
색인에서 AlbumId
키와 일치하는 입력 행의 AlbumId
조건자를 조건으로 하는 색인 AlbumsByAlbumTitle
의 색인 스캔입니다. 이 매핑으로 일괄 처리된 입력 행의 SingerId
값에 대한 SongName
이 반환됩니다.
이 예시의 DCA 프로세스를 요약하면 DCA 입력은 Albums
테이블에서 일괄 처리된 행이고 DCA의 출력은 색인 스캔 맵에 이 행을 적용한 것입니다.
분산 외부 적용
분산 교차 적용 연산자가 교차 적용 연산자를 확장하는 것과 마찬가지로 분산 외부 적용 연산자는 여러 서버에서 실행되어 외부 적용 연산자를 확장합니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT lastname,
concertdate
FROM singers LEFT OUTER join@{JOIN_TYPE=APPLY_JOIN} concerts
ON singers.singerid=concerts.singerid;
결과는 다음과 같은 형식입니다.
LastName | ConcertDate |
---|---|
Trentor | 2014-02-18 |
Smith | 2011-09-03 |
Smith | 2010-06-06 |
Lomond | 2005-04-30 |
Martin | 2015-11-04 |
Richards |
다음은 실행 계획입니다.
변형 적용
변형 적용 연산자는 데이터 조작 문 (DML)의 변형을 테이블에 적용합니다. 이는 DML 문의 쿼리 계획에서 최상위 연산자입니다.
예를 들어 다음 쿼리를 사용합니다.
DELETE FROM singers
WHERE firstname = 'Alice';
결과는 다음과 같습니다.
4 rows deleted This statement deleted 4 rows and did not return any rows.
다음은 실행 계획입니다.
추가 정보
이 섹션에서는 독립 실행형 연산자는 아니지만 이전에 나열된 연산자를 1개 이상 지원하는 태스크를 실행하는 항목에 대해 설명합니다. 여기에 설명된 항목은 기술적으로 연산자이지만 쿼리 계획에서 별도의 연산자로 구분되지 않습니다.
구조체 생성자
구조체 생성자는 구조체 또는 필드 컬렉션을 만듭니다. 일반적으로 컴퓨팅 연산의 결과인 행에 대한 구조체를 만듭니다. 구조체 생성자는 독립 실행형 연산자가 아닙니다. 대신, 컴퓨팅 구조체 연산자 또는 결과 직렬화 연산자에 나타납니다.
컴퓨팅 구조체 연산의 경우, 구조체 생성자는 계산된 행에 대한 열이 구조체에 대한 단일 변수 참조를 사용할 수 있도록 구조체를 생성합니다.
결과 직렬화 연산의 경우, 구조체 생성자는 결과를 직렬화하는 구조체를 만듭니다.
예를 들어 다음 쿼리를 사용합니다.
SELECT IF(TRUE, struct(1 AS A, 1 AS B), struct(2 AS A , 2 AS B)).A;
결과는 다음과 같습니다.
A |
---|
1 |
다음은 실행 계획입니다.
실행 계획에서 구조체 생성자는 결과 직렬화 연산자 안에 나타납니다.