이 문서에서는 Spanner Graph 스키마 설계 권장사항에 따라 효율적인 쿼리를 만드는 방법을 설명합니다. 스키마 설계는 반복적인 작업일 수 있기 때문에 먼저 주요 쿼리 패턴을 식별하여 스키마 설계를 안내하는 것이 좋습니다.
Spanner 스키마 설계 권장사항에 대한 자세한 내용은 스키마 설계 권장사항을 참조하세요.
에지 순회 최적화
에지 순회란 특정 노드에서 시작하여 다른 노드에 도달하기 위해 연결된 에지를 따라 이동하는 방식으로 에지를 따라 그래프를 이동하는 프로세스입니다. 에지 순회는 Spanner Graph의 기본적인 작업이므로 애플리케이션 성능 향상을 위해서는 에지 순회의 효율성을 높이는 것이 매우 중요합니다.
에지 순회는 두 방향으로 수행할 수 있습니다. 소스 노드에서 대상 노드로 이동하는 것을 정방향 에지 순회라고 하고, 대상 노드에서 소스 노드로 이동하는 것을 역방향 에지 순회라고 합니다.
인터리브 처리를 통해 정방향 에지 순회 최적화
정방향 에지 순회 성능을 향상시키려면 에지 입력 테이블을 소스 노드 입력 테이블로 인터리브 처리하여 소스 노드에 에지를 공동 배치합니다. 인터리브 처리는 Spanner에서 하위 테이블 행을 스토리지에 있는 해당 상위 행과 함께 물리적으로 공동 배치하는 스토리지 최적화 기법입니다. 인터리브 처리에 대한 자세한 내용은 스키마 개요를 참조하세요.
다음 예시는 이러한 권장사항을 보여줍니다.
CREATE TABLE Person (
id INT64 NOT NULL,
name STRING(MAX),
) PRIMARY KEY (id);
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
외래 키를 사용하여 역방향 에지 순회 최적화
역방향 에지를 효율적으로 순회하기 위해서는 에지와 대상 노드 사이에 외래 키 제약조건을 만듭니다. 이 외래 키는 대상 노드 키를 사용하여 에지에 보조 색인을 자동으로 만듭니다. 보조 색인은 쿼리 실행 중에 자동으로 사용됩니다.
다음 예시는 이러한 권장사항을 보여줍니다.
CREATE TABLE Person (
id INT64 NOT NULL,
name STRING(MAX),
) PRIMARY KEY (id);
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id);
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id),
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
보조 색인을 사용하여 역방향 에지 순회 최적화
엄격한 데이터 무결성 규칙으로 인해 에지에 외래 키를 만들지 않으려는 경우에는 다음 예시에 표시된 것처럼 에지 입력 테이블에서 직접 보조 색인을 만들 수 있습니다.
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
CREATE INDEX Reverse_PersonOwnAccount
ON PersonOwnAccount (account_id);
댕글링 에지 금지
댕글링 에지는 2개 미만의 노드에 연결된 에지입니다. 댕글링 에지는 연결된 에지를 삭제하지 않고 노드를 삭제할 때 또는 노드에 적절하게 연결하지 않은 상태로 에지가 생성되었을 때 발생할 수 있습니다.
댕글링 에지를 허용하지 않으면 다음과 같은 이점이 있습니다.
- 그래프 구조의 무결성이 향상됩니다.
- 엔드포인트가 존재하지 않는 에지를 필터링하기 위한 추가 작업을 방지함으로써 쿼리 성능이 향상됩니다.
참조 제약조건을 사용하여 댕글링 에지 금지
댕글링 에지를 금지하려면 두 엔드포인트 모두에 제약조건을 지정합니다.
- 에지 입력 테이블을 소스 노드 입력 테이블에 인터리브 처리합니다. 이 접근 방법을 사용하면 에지의 소스 노드가 항상 존재하게 됩니다.
- 에지의 대상 노드가 항상 존재하도록 에지에 외래 키 제약조건을 만듭니다.
다음 예시에서는 인터리브 처리와 외래 키를 사용하여 참조 무결성을 적용합니다.
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id) ON DELETE CASCADE,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
ON DELETE CASCADE를 사용해서 노드 삭제 시 에지 자동 삭제
댕글링 에지 금지를 위해 인터리브 처리 또는 외래 키를 사용할 때는 ON DELETE
절을 사용해서 아직 연결되어 있는 에지가 포함된 노드를 삭제할 때의 동작을 제어합니다. 자세한 내용은 인터리브 처리된 테이블의 연결 삭제 및 외래 키로 연결 삭제를 참조하세요.
다음 방법으로 ON DELETE
를 사용할 수 있습니다.
ON DELETE NO ACTION
(또는ON DELETE
절 생략): 에지가 포함된 노드 삭제가 실패합니다.ON DELETE CASCADE
: 노드를 삭제하면 동일한 트랜잭션에 있는 연결된 에지가 자동으로 삭제됩니다.
서로 다른 유형의 노드를 연결하는 에지의 연결 삭제
소스 노드가 삭제될 때 에지를 삭제합니다. 예를 들어
INTERLEAVE IN PARENT Person ON DELETE CASCADE
는 삭제 중인Person
노드에서 외부로 나가는 모든PersonOwnAccount
에지를 삭제합니다. 자세한 내용은 인터리브 처리된 테이블 만들기를 참조하세요.대상 노드가 삭제될 때 에지를 삭제합니다. 예를 들어
CONSTRAINT FK_Account FOREIGN KEY(account_id) REFERENCES Account(id) ON DELETE CASCADE
는 삭제 중인Account
노드로 들어오는 모든PersonOwnAccount
에지를 삭제합니다. 자세한 내용은 외래 키를 참조하세요.
동일 유형의 노드를 연결하는 에지의 연결 삭제
에지의 소스 및 대상 노드가 유형이 동일하고 에지가 소스 노드에 인터리브 처리된 경우 소스 노드 또는 대상 노드 중 하나(두 노드가 아니라)에 대해서만 ON DELETE CASCADE
를 정의할 수 있습니다.
두 경우 모두 댕글링 에지를 자동으로 삭제하려면 에지 입력 테이블을 소스 노드 입력 테이블에 인터리브 처리하는 대신 에지 소스 노드 참조에 외래 키를 만듭니다.
정방향 에지 순회 최적화를 위해서는 인터리브 처리가 권장됩니다.
계속하기 전에 워크로드에 미치는 영향을 확인하세요. 자세한 내용은 AccountTransferAccount
를 에지 입력 테이블로 사용하는 다음 예시를 참조하세요.
--Define two Foreign Keys, each on one end Node of Transfer Edge, both with ON DELETE CASCADE action:
CREATE TABLE AccountTransferAccount (
id INT64 NOT NULL,
to_id INT64 NOT NULL,
amount FLOAT64,
create_time TIMESTAMP NOT NULL,
order_number STRING(MAX),
CONSTRAINT FK_FromAccount FOREIGN KEY (id) REFERENCES Account (id) ON DELETE CASCADE,
CONSTRAINT FK_ToAccount FOREIGN KEY (to_id) REFERENCES Account (id) ON DELETE CASCADE,
) PRIMARY KEY (id, to_id);
보조 색인을 사용하여 노드 또는 에지 속성으로 필터링
보조 색인은 효율적인 쿼리 처리를 위해 필수적입니다. 보조 색인을 사용하면 전체 그래프 구조를 순회할 필요 없이 특정 속성 값을 기준으로 노드 및 에지를 빠르게 조회할 수 있습니다. 모든 노드와 에지를 순회하는 것은 매우 비효율적일 수 있기 때문에 대용량 그래프에서는 보조 색인이 중요합니다.
속성별 노드 필터링 속도 향상
노드 속성별 필터링 속도를 높이려면 속성에 보조 색인을 만듭니다. 예를 들어 다음 쿼리는 계정에서 지정된 닉네임을 찾습니다. 보조 색인이 없으면 필터링 조건과 일치하는 항목을 찾기 위해 모든 Account
노드가 스캔됩니다.
GRAPH FinGraph
MATCH (acct:Account)
WHERE acct.nick_name = "abcd"
RETURN acct.id;
쿼리 속도를 높이기 위해서는 다음 예시에 표시된 것처럼 필터링된 속성에 대해 보조 색인을 만듭니다.
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
is_blocked BOOL,
nick_name STRING(MAX),
) PRIMARY KEY (id);
CREATE INDEX AccountByEmail
ON Account (nick_name);
팁: 희소 속성의 경우 NULL로 필터링되는 색인을 사용합니다. 자세한 내용은 NULL 값 색인 생성 사용 중지를 참조하세요.
에지 속성에 대한 필터링으로 정방향 에지 순회 속도 향상
속성에 필터를 적용할 때 에지를 순회하는 경우 에지 속성에 보조 색인을 만들고 소스 노드에 색인을 인터리브 처리하여 쿼리 속도를 높일 수 있습니다.
예를 들어 다음 쿼리는 일정 시간 후 지정된 인물이 소유한 계정을 찾습니다.
GRAPH FinGraph
MATCH (person:Person)-[owns:Owns]->(acct:Account)
WHERE person.id = 1
AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
RETURN acct.id;
기본적으로 이 쿼리는 지정된 인물의 모든 에지를 읽고 create_time
에 대한 조건을 충족하는 에지를 필터링합니다.
다음 예시는 에지 소스 노드 참조(id
) 및 에지 속성(create_time
)에 보조 색인을 만들어서 쿼리 효율성을 향상시키는 방법을 보여줍니다. 소스 노드 입력 테이블 아래에서 색인을 인터리브 처리하여 색인을 소스 노드와 함께 배치합니다.
CREATE TABLE PersonOwnAccount (
id INT64 NOT NULL,
account_id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
CREATE INDEX PersonOwnAccountByCreateTime
ON PersonOwnAccount (id, create_time)
INTERLEAVE IN Person;
이 접근 방식을 사용하면 쿼리가 create_time
조건을 충족하는 모든 에지를 효율적으로 찾을 수 있습니다.
에지 속성에 대한 필터링으로 역방향 에지 순회 속도 향상
속성에 필터를 적용할 때 역방향 에지를 순회하는 경우 필터링할 대상 노드 및 에지 속성을 사용해서 보조 색인을 만들어 쿼리 속도를 높일 수 있습니다.
다음 예시 쿼리는 에지 속성으로 필터링하여 역방향 에지 순회를 수행합니다.
GRAPH FinGraph
MATCH (acct:Account)<-[owns:Owns]-(person:Person)
WHERE acct.id = 1
AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
RETURN person.id;
보조 색인을 사용하여 이 쿼리 속도를 높이려면 다음 옵션 중 하나를 선택합니다.
다음 예시에 표시된 것처럼 에지 대상 노드 참조(
account_id
) 및 에지 속성(create_time
)에 보조 색인을 만듭니다.CREATE TABLE PersonOwnAccount ( id INT64 NOT NULL, account_id INT64 NOT NULL, create_time TIMESTAMP, ) PRIMARY KEY (id, account_id), INTERLEAVE IN PARENT Person ON DELETE CASCADE; CREATE INDEX PersonOwnAccountByCreateTime ON PersonOwnAccount (account_id, create_time);
이 접근 방법은 역방향 에지가
account_id
및create_time
으로 정렬되어 쿼리 엔진이create_time
에 대한 조건을 충족하는account_id
에 대한 에지를 효율적으로 찾을 수 있기 때문에 더 나은 성능을 제공합니다. 하지만 쿼리가 서로 다른 속성을 기반으로 데이터를 필터링하는 경우 각 속성에 대해 색인을 별도로 만들어야 해서 추가 오버헤드가 발생할 수 있습니다.다음 예시에 표시된 것처럼 에지 대상 노드 참조(
account_id
)에 보조 색인을 만들고 에지 속성(create_time
)을 저장 열에 저장합니다.CREATE TABLE PersonOwnAccount ( id INT64 NOT NULL, account_id INT64 NOT NULL, create_time TIMESTAMP, ) PRIMARY KEY (id, account_id), INTERLEAVE IN PARENT Person ON DELETE CASCADE; CREATE INDEX PersonOwnAccountByCreateTime ON PersonOwnAccount (account_id) STORING (create_time);
이 접근 방법은 여러 속성을 저장할 수 있지만 쿼리가 대상 노드의 모든 에지를 읽고 에지 속성에 필터를 적용해야 합니다.
다음 안내에 따라 이러한 접근 방법을 조합할 수 있습니다.
- 성능이 중요한 쿼리에 사용될 경우 색인 열에 에지 속성을 사용합니다.
- 성능 중요도가 조금 낮은 쿼리에 사용되는 속성의 경우 저장 열에 추가합니다.
라벨 및 속성을 사용하여 노드 및 에지 유형 모델링
노드 및 에지 유형은 일반적으로 라벨을 사용해서 모델링됩니다. 하지만 속성을 사용하여 유형을 모델링할 수도 있습니다. BankAccount
, InvestmentAccount
, RetirementAccount
와 같은 여러 유형의 계정이 존재하는 예시를 고려해보세요. 이 경우에는 각 계정을 개별 입력 테이블에 저장하고 별개의 라벨로 모델링하거나 단일 입력 테이블에 계정을 저장하고 속성을 사용해서 유형을 구분할 수 있습니다.
라벨을 사용해서 유형을 모델링하는 방식으로 모델링 프로세스를 시작하세요. 다음 시나리오에서는 속성을 사용하는 방식을 고려하세요.
스키마 관리 향상
그래프에 여러 다른 노드 및 에지 유형이 포함된 경우에는 각 항목의 입력 테이블을 개별적으로 관리하는 것이 어려울 수 있습니다. 스키마 관리를 쉽게 하려면 유형을 속성으로 모델링합니다.
자주 변경되는 유형 관리를 위해 속성으로 유형 모델링
유형을 라벨로 모델링할 때 유형을 추가하거나 삭제하려면 스키마를 변경해야 합니다. 단기간에 지나치게 많은 스키마 업데이트를 수행하면 Spanner가 큐에 추가된 스키마 업데이트 처리를 throttle할 수 있습니다. 자세한 내용은 스키마 업데이트의 실행 빈도 제한을 참조하세요.
스키마를 자주 변경해야 하는 경우에는 스키마 업데이트 빈도에 대한 제한사항을 해결하기 위해 속성으로 유형을 모델링하는 것이 좋습니다.
쿼리 속도 향상
속성으로 유형을 모델링하면 노드 또는 에지 패턴이 여러 라벨을 참조할 때 쿼리 속도가 향상될 수 있습니다. 다음 예시 쿼리는 계정 유형이 라벨로 모델링되었다고 가정하고 Person
이 소유하는 모든 SavingsAccount
및 InvestmentAccount
인스턴스를 찾습니다.
GRAPH FinGraph
MATCH (:Person {id: 1})-[:Owns]->(acct:SavingsAccount|InvestmentAccount)
RETURN acct.id;
acct
노드 패턴은 2개의 라벨을 참조합니다. 성능이 중요한 쿼리의 경우 속성을 사용하여 Account
를 모델링하는 것이 좋습니다. 이 접근 방법은 다음 쿼리 예시에 표시된 것처럼 더 나은 쿼리 성능을 제공합니다. 두 쿼리를 벤치마킹해서 비교하는 것이 좋습니다.
GRAPH FinGraph
MATCH (:Person {id: 1})-[:Owns]->(acct:Account)
WHERE acct.type IN ("Savings", "Investment")
RETURN acct.id;
쿼리 속도 향상을 위해 노드 요소 키에 유형 저장
노드 유형이 속성으로 모델링되었고 유형이 노드 수명 전반에서 변경되지 않을 때 노드 유형에 대한 필터 적용으로 쿼리 속도를 높이려면 다음 단계를 수행합니다.
- 노드 요소 키의 일부로 속성을 포함합니다.
- 에지 입력 테이블에 노드 유형을 추가합니다.
- 에지 참조 키에 노드 유형을 포함합니다.
다음 예시는 이러한 최적화를 Account
노드 및 AccountTransferAccount
에지에 적용합니다.
CREATE TABLE Account (
type STRING(MAX) NOT NULL,
id INT64 NOT NULL,
create_time TIMESTAMP,
) PRIMARY KEY (type, id);
CREATE TABLE AccountTransferAccount (
type STRING(MAX) NOT NULL,
id INT64 NOT NULL,
to_type STRING(MAX) NOT NULL,
to_id INT64 NOT NULL,
amount FLOAT64,
create_time TIMESTAMP NOT NULL,
order_number STRING(MAX),
) PRIMARY KEY (type, id, to_type, to_id),
INTERLEAVE IN PARENT Account ON DELETE CASCADE;
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
Account
)
EDGE TABLES (
AccountTransferAccount
SOURCE KEY (type, id) REFERENCES Account
DESTINATION KEY (to_type, to_id) REFERENCES Account
);
노드 및 에지에서 TTL 구성
Spanner TTL(수명)은 지정된 기간이 지난 후 자동 만료 및 데이터 삭제를 지원하는 메커니즘입니다. Spanner TTL은 세션 정보, 임시 캐시, 이벤트 로그와 같이 수명 또는 관련성이 제한된 데이터에 자주 사용됩니다. 이러한 경우에 TTL은 데이터베이스 크기와 성능을 유지보수하는 데 도움이 됩니다.
다음 예시에서는 TTL을 사용하여 마감 후 90일이 지나면 계정을 삭제합니다.
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
close_time TIMESTAMP,
) PRIMARY KEY (id),
ROW DELETION POLICY (OLDER_THAN(close_time, INTERVAL 90 DAY));
노드 테이블에 TTL이 있고 내부에 에지 테이블이 인터리브 처리되어 있으면 ON DELETE CASCADE
를 사용하여 인터리브를 정의해야 합니다.
마찬가지로 노드 테이블에 TTL이 있고 외래 키를 통해 에지 테이블에서 참조되는 경우에는 ON DELETE CASCADE
를 사용하여 외래 키를 정의해야 합니다.
다음 예시에서 AccountTransferAccount
는 계정이 활성 상태로 있는 최대 10년 동안 저장됩니다. 계정이 삭제되면 송금 기록도 삭제됩니다.
CREATE TABLE AccountTransferAccount (
id INT64 NOT NULL,
to_id INT64 NOT NULL,
amount FLOAT64,
create_time TIMESTAMP NOT NULL,
order_number STRING(MAX),
) PRIMARY KEY (id, to_id),
INTERLEAVE IN PARENT Account ON DELETE CASCADE,
ROW DELETION POLICY (OLDER_THAN(create_time, INTERVAL 3650 DAY));
노드 및 에지 입력 테이블 병합
동일한 입력 테이블을 사용하여 스키마에서 노드 및 에지를 하나 이상 정의할 수 있습니다.
다음 예시 테이블에서 Account
노드에는 복합 키 (owner_id, account_id)
가 포함됩니다. 여기에서는 키가 id
인 Person
노드가 id
가 owner_id
와 일치할 때 복합 키가 (owner_id, account_id)
인 Account
노드를 소유하는 묵시적 에지 정의를 보여줍니다.
CREATE TABLE Person (
id INT64 NOT NULL,
) PRIMARY KEY (id);
-- Assume each account has exactly one owner.
CREATE TABLE Account (
owner_id INT64 NOT NULL,
account_id INT64 NOT NULL,
) PRIMARY KEY (owner_id, account_id);
여기에서는 다음 스키마 예시에 표시된 것처럼 Account
입력 테이블을 사용해서 Account
노드 및 PersonOwnAccount
에지를 정의할 수 있습니다.
모든 요소 테이블 이름이 고유하도록 보장하기 위해 이 예시에서는 에지 테이블 정의에 Owns
별칭을 지정합니다.
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
Person,
Account
)
EDGE TABLES (
Account AS Owns
SOURCE KEY (owner_id) REFERENCES Person
DESTINATION KEY (owner_id, account_id) REFERENCES Account
);