여기서는 Spanner로 수행하는 모든 삽입 및 업데이트 작업 시 커밋 타임스탬프를 기록하는 방법에 대해 설명합니다. 이 기능을 사용하려면 TIMESTAMP
열에서 allow_commit_timestamp
옵션을 설정한 다음, 각 트랜잭션 과정에서 타임스탬프를 씁니다.
개요
TrueTime 기술에 기반을 둔 커밋 타임스탬프는 데이터베이스에서 트랜잭션이 커밋되는 시간입니다. allow_commit_timestamp
열 옵션을 사용하여 커밋 타임스탬프를 열에 원자적으로 저장할 수 있습니다.
테이블에 저장된 커밋 타임스탬프를 사용하여 변형의 정확한 순서를 결정하고 변경 로그와 같은 기능을 빌드할 수 있습니다.
데이터베이스에 커밋 타임스탬프를 삽입하려면 다음 단계를 완료하세요.
스키마 정의에서 열 옵션
allow_commit_timestamp
를true
로 설정하여TIMESTAMP
유형의 열 만들기를 수행합니다. 예를 들면 다음과 같습니다.CREATE TABLE Performances ( ... LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true) ... ) PRIMARY KEY (...);
DML을 사용하여 삽입 또는 업데이트를 수행하는 경우
PENDING_COMMIT_TIMESTAMP
함수를 사용하여 커밋 타임스탬프를 씁니다.변형이 포함된 삽입 또는 업데이트를 수행하는 경우 커밋 타임스탬프 열에 삽입 또는 업데이트 시
spanner.commit_timestamp()
자리표시자 문자열을 사용합니다. 클라이언트 라이브러리에서 제공하는 커밋 타임스탬프 상수를 사용할 수도 있습니다. 예를 들어 Java 클라이언트의 상수는Value.COMMIT_TIMESTAMP
입니다.
Spanner가 이러한 자리표시자를 열 값으로 사용해서 트랜잭션을 커밋하면 실제 커밋 타임스탬프가 지정된 열에 기록됩니다(예: LastUpdateTime
열). 그런 다음 이 열 값을 사용해서 업데이트 기록을 테이블에 만들 수 있습니다.
커밋 타임스탬프 값은 고유한 값이 아닐 수도 있습니다. 중복되지 않는 필드 세트에 기록하는 트랜잭션은 동일한 타임스탬프를 가질 수 있습니다. 중복된 필드 세트에 기록하는 트랜잭션은 고유한 타임스탬프를 가집니다.
Spanner 커밋 타임스탬프는 마이크로초 단위로 기록되며 TIMESTAMP
열에 저장될 때는 나노초로 변환됩니다.
커밋 타임스탬프 열 생성 및 삭제
allow_commit_timestamp
열 옵션을 사용하여 커밋 타임스탬프에 대한 지원을 추가하고 삭제할 수 있습니다.
- 새 테이블 만들기를 수행할 때 열이 커밋 타임스탬프를 지원하도록 지정합니다.
- 기존 테이블을 수정하는 경우에는 다음을 수행합니다.
- 커밋 타임스탬프를 지원하는 새 열을 추가합니다.
- 커밋 타임스탬프를 지원하도록 기존
TIMESTAMP
열을 수정합니다. - 커밋 타임스탬프 지원을 삭제하도록 기존
TIMESTAMP
열을 수정합니다.
키와 색인
커밋 타임스탬프 열을 기본 키 열이나 키 열이 아닌 열로 사용할 수 있습니다. 기본 키는 ASC
또는 DESC
로 정의될 수 있습니다.
ASC
(기본값) - 오름차순 키는 특정 시간부터 쿼리에 답변하는 데 적합합니다.DESC
- 내림차순 키는 최신 행을 테이블의 상단에 유지합니다. 따라서 최신 레코드에 빨리 액세스할 수 있습니다.
allow_commit_timestamp
옵션은 상위 테이블과 하위 테이블의 모든 기본 키에서 일관되어야 합니다. 이 옵션이 모든 기본 키에서 일관되지 않으면 Spanner가 오류를 반환합니다. 이 옵션이 일관되지 않아도 되는 유일한 시간은 스키마를 생성하거나 업데이트할 때입니다.
다음 시나리오에서 커밋 타임스탬프를 사용하면 데이터 성능을 저하시키는 핫스팟이 생성됩니다.
테이블 기본 키의 첫 번째 부분으로 타임스탬프 열을 커밋합니다.
CREATE TABLE Users ( LastAccess TIMESTAMP NOT NULL, UserId INT64 NOT NULL, ... ) PRIMARY KEY (LastAccess, UserId);
보조 색인 기본 키의 첫 번째 부분은 다음과 같습니다.
CREATE INDEX UsersByLastAccess ON Users(LastAccess)
또는
CREATE INDEX UsersByLastAccessAndName ON Users(LastAccess, FirstName)
핫스팟은 낮은 쓰기 속도에서도 데이터 성능을 저하시킵니다. 색인이 생성되지 않은 키 열이 아닌 열에 커밋 타임스탬프를 사용 설정해도 성능 오버헤드가 발생하지 않습니다.
커밋 타임스탬프 열 만들기
다음은 커밋 타임스탬프를 지원하는 열로 테이블을 만드는 DDL입니다.
CREATE TABLE Performances (
SingerId INT64 NOT NULL,
VenueId INT64 NOT NULL,
EventDate Date,
Revenue INT64,
LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (SingerId, VenueId, EventDate),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE
옵션을 추가하면 타임스탬프 열이 다음과 같이 바뀝니다.
- 삽입 및 업데이트 시
spanner.commit_timestamp()
자리표시자 문자열(또는 클라이언트 라이브러리에서 제공하는 상수)을 사용할 수 있습니다. - 열은 과거의 값만 포함할 수 있습니다. 자세한 내용은 타임스탬프에 고유한 값 지정을 참조하세요.
allow_commit_timestamp
옵션은 대소문자를 구분합니다.
기존 테이블에 커밋 타임스탬프 열 추가하기
기존 테이블에 커밋 타임스탬프 열을 추가하려면 ALTER TABLE
문을 사용합니다. 예를 들어 LastUpdateTime
열을 Performances
테이블에 추가하려면 다음 문을 사용합니다.
ALTER TABLE Performances ADD COLUMN LastUpdateTime TIMESTAMP
NOT NULL OPTIONS (allow_commit_timestamp=true)
타임스탬프 열을 커밋 타임스탬프 열로 변환하기
기존 타임스탬프 열을 커밋 타임스탬프 열로 변환할 수 있으나 이렇게 하려면 Spanner가 기존 타임스탬프 값이 과거의 값임을 확인해야 합니다. 예를 들면 다음과 같습니다.
ALTER TABLE Performances ALTER COLUMN LastUpdateTime
SET OPTIONS (allow_commit_timestamp=true)
SET OPTIONS
를 포함하는 ALTER TABLE
문에서 특정 열의 데이터 형식이나 NULL
주석을 변경할 수 없습니다. 자세한 내용은 데이터 정의 언어를 참조하세요.
커밋 타임스탬프 옵션 제거하기
열에서 커밋 타임스탬프 지원을 제거하려면 ALTER TABLE
문에서 allow_commit_timestamp=null
옵션을 사용합니다. 커밋 타임스탬프 동작은 제거되지만 해당 열은 여전히 타임스탬프입니다. 이 옵션을 변경해도 해당 열의 다른 특성(예: 유형 또는 null 허용 여부(NOT NULL
))은 바뀌지 않습니다.
ALTER TABLE Performances ALTER COLUMN LastUpdateTime
SET OPTIONS (allow_commit_timestamp=null)
DML 문을 사용하여 커밋 타임스탬프 쓰기
PENDING_COMMIT_TIMESTAMP
함수를 사용하여 DML 문에서 커밋 타임스탬프를 씁니다. Spanner는 트랜잭션이 커밋될 때 커밋 타임스탬프를 선택합니다.
다음 DML 문은 Performances
테이블의 LastUpdateTime
열을 커밋 타임스탬프로 업데이트합니다.
UPDATE Performances SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()
WHERE SingerId=1 AND VenueId=2 AND EventDate="2015-10-21"
다음 코드 예시에서는 PENDING_COMMIT_TIMESTAMP
함수를 사용하여 LastUpdateTime
열에 커밋 타임스탬프를 씁니다.
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
Ruby
커밋 타임스탬프는 allow_commit_timestamp=true
옵션 주석이 달린 열에만 기록될 수 있습니다.
여러 테이블의 행에 변형이 있는 경우 각 테이블의 커밋 타임스탬프 열에 spanner.commit_timestamp()
(또는 클라이언트 라이브러리 상수)를 지정해야 합니다.
커밋 타임스탬프 열 쿼리
다음의 예는 테이블의 커밋 타임스탬프 열을 쿼리합니다.
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
커밋 타임스탬프 열에 고유한 값 지정
spanner.commit_timestamp()
(또는 클라이언트 라이브러리 상수)를 열 값으로 전달하는 대신에 커밋 타임스탬프 열에 고유한 값을 지정할 수 있습니다. 이 값은 과거의 타임스탬프여야 합니다. 이 같은 제한은 타임스탬프 기록 작업을 저렴하고 신속한 작업으로 만들어 줍니다. 미래의 타임스탬프가 지정되면 서버가 FailedPrecondition
오류를 반환합니다.
변경 로그 만들기
테이블에 발생하는 모든 변형에 대한 변경 로그를 만들어서 감사에 사용하려는 경우를 가정해 보겠습니다. 변경 내역을 워드 프로세싱 문서로 저장하는 테이블을 예로 들 수 있습니다. 커밋 타임스탬프를 사용하면 변경 로그를 만들기가 더 쉽습니다. 타임스탬프는 변경 로그 항목의 순서를 강제할 수 있기 때문입니다. 다음의 예시와 같은 스키마를 사용하여 특정 문서에 대한 변경 내역을 저장하는 변경 로그를 빌드할 수 있습니다.
CREATE TABLE Documents (
UserId INT64 NOT NULL,
DocumentId INT64 NOT NULL,
Contents STRING(MAX) NOT NULL,
) PRIMARY KEY (UserId, DocumentId);
CREATE TABLE DocumentHistory (
UserId INT64 NOT NULL,
DocumentId INT64 NOT NULL,
Ts TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
Delta STRING(MAX),
) PRIMARY KEY (UserId, DocumentId, Ts),
INTERLEAVE IN PARENT Documents ON DELETE NO ACTION;
변경 로그를 만들려면, DocumentHistory
에 행을 삽입하거나 업데이트하는 동일한 트랜잭션에서 Document
에 새 행을 삽입합니다. DocumentHistory
에 새 행을 삽입할 때, 자리표시자 spanner.commit_timestamp()
(또는 클라이언트 라이브러리 상수)를 사용하여 Spanner에게 Ts
열에 커밋 타임스탬프를 기록하라고 명령합니다. DocumentsHistory
테이블과 Documents
테이블을 인터리브 처리하면 데이터 위치 파악과 더 효율적인 삽입 및 업데이트가 가능해집니다. 그러나 상위 행과 하위 행을 함께 삭제해야 한다는 제약도 추가됩니다. Documents
의 행 다음에 있는 DocumentHistory
의 행이 삭제되도록 하려면 이들 테이블을 인터리브 처리하지 마세요.
커밋 타임스탬프로 최근 데이터 쿼리 최적화
커밋 타임스탬프를 사용하면 특정 시간 이후에 작성된 데이터를 검색할 때 쿼리 I/O를 줄일 수 있는 Spanner 최적화가 사용 설정됩니다.
이 최적화를 활성화하려면 쿼리의 WHERE
절에 다음 속성을 사용하여 테이블의 커밋 타임스탬프 열과 사용자가 제공하는 특정 시간 간의 비교를 포함해야 합니다.
특정 시간을 상수 표현식(리터럴, 매개변수 또는 자체 인수가 상수로 평가되는 함수)으로 제공합니다.
>
또는>=
연산자를 사용하여 커밋 타임스탬프가 지정된 시간보다 최신인지 여부를 비교합니다.필요한 경우
AND
를 사용하여WHERE
절에 제약사항을 더 추가합니다.OR
을 사용하여 절을 확장하면 이 최적화에서 쿼리가 유효하지 않습니다.
예를 들어 커밋 타임스탬프 열이 포함된 다음 Performances
테이블을 살펴보겠습니다.
CREATE TABLE Performances (
SingerId INT64 NOT NULL,
VenueId INT64 NOT NULL,
EventDate DATE,
Revenue INT64,
LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (SingerId, VenueId, EventDate);
이 쿼리는 테이블의 커밋 타임스탬프 열과 상수 표현식(이 경우 리터럴) 간에 크거나 같음 비교가 있기 때문에 앞서 설명한 커밋 타임스탬프 최적화의 이점을 얻습니다.
SELECT * FROM Performances WHERE LastUpdateTime >= "2022-05-01";
다음 쿼리도 커밋 타임스탬프와 쿼리 실행 중에 모든 인수가 상수로 평가되는 함수 간에 큼 비교가 있기 때문에 최적화 대상이 됩니다.
SELECT * FROM Performances
WHERE LastUpdateTime > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY);
다음 단계
- 커밋 타임스탬프를 사용하여 Go로 변경 로그 만들기