이 페이지에서는 읽기 전용 및 읽기-쓰기 트랜잭션의 컨텍스트 외부에서 Spanner의 읽기를 수행하는 방법을 설명합니다. 다음 중 하나에 해당하면 트랜잭션 페이지를 대신 읽어야 합니다.
하나 이상의 읽기 값에 따라 쓰기를 실행해야 하는 경우, 읽기-쓰기 트랜잭션의 일부로 읽기를 실행해야 합니다. 자세한 내용은 읽기-쓰기 트랜잭션을 참조하세요.
일관된 데이터 보기가 필요한 여러 읽기 호출을 수행하는 경우, 읽기 전용 트랜잭션의 일부로 읽기를 실행해야 합니다. 자세한 내용은 읽기 전용 트랜잭션을 참조하세요.
읽기 유형
Spanner는 두 가지 읽기 유형을 제공하므로 데이터를 읽을 때 데이터의 현재 상태를 확인할 수 있습니다.
- 강력 읽기는 현재 타임스탬프에서의 읽기이며 이 읽기를 시작할 때까지 커밋한 모든 데이터를 볼 수 있습니다. Spanner는 기본적으로 강력 읽기를 사용하여 읽기 요청을 처리합니다.
- 비활성 읽기는 과거의 타임스탬프에서의 읽기입니다. 애플리케이션이 지연 시간에 민감하지만 비활성 데이터를 허용하는 경우, 비활성 읽기는 성능상의 이점이 있습니다.
원하는 읽기 유형을 선택하려면 읽기 요청에서 타임스탬프 경계를 설정합니다. 타임스탬프 경계를 선택할 때 다음 권장사항을 따르세요.
가능하다면 강력 읽기를 선택합니다. 이 유형은 읽기 전용 트랜잭션을 포함하는 Spanner 읽기의 기본 타임스탬프 경계입니다. 강력 읽기에서는 읽기를 수신하는 복제본에 관계없이 작업을 시작하기 전에 커밋한 모든 트랜잭션의 영향을 관찰할 수 있습니다. 이러한 이유로 강력 읽기를 사용하면 애플리케이션 코드가 보다 간단해지고 애플리케이션의 신뢰성을 높일 수 있습니다. TrueTime 및 외부 일관성에서 Spanner의 일관성 속성을 자세히 알아보세요.
상황에 따라 지연 시간 때문에 강력 읽기를 실행할 수 없다면 비활성 읽기(제한된 비활성 또는 완전 비활성)를 사용하여 최신 상태의 읽기가 필요하지 않은 경우에 성능을 향상시킵니다. 복제 페이지의 설명대로 우수한 성능을 위해 사용할 수 있는 적정한 비활성 값은 15초입니다.
데이터베이스 역할로 데이터 읽기
세분화된 액세스 제어 사용자는 SQL 문과 쿼리를 실행하고 데이터베이스에서 행 작업을 수행할 수 있는 데이터베이스 역할을 선택해야 합니다. 선택한 역할은 역할을 변경할 때까지 세션 동안 유지됩니다.
데이터베이스 역할로 읽기를 수행하는 방법은 세분화된 액세스 제어로 데이터베이스에 액세스를 참조하세요.
단일 읽기 메서드
Spanner에서는 다음의 경우 데이터베이스에서 단일 읽기 메서드(즉, 트랜잭션 컨텍스트 외부에서 읽기)를 지원합니다.
- SQL 쿼리 문으로 또는 Spanner의 읽기 API를 사용하여 읽기 실행
- 테이블의 단일 행 또는 여러 행에서 강력 읽기 수행
- 테이블의 단일 행 또는 여러 행에서 비활성 읽기 수행
- 보조 색인의 단일 행 또는 여러 행에서 읽기
멀티 리전 인스턴스 구성 또는 선택적인 읽기 전용 리전이 있는 커스텀 리전 구성 내에서 특정 복제본 또는 리전으로 단일 읽기를 라우팅하려면 방향성 읽기를 참조하세요.
다음 섹션에서는 Spanner 클라이언트 라이브러리를 통해 읽기 메서드를 사용하는 방법을 설명합니다.
쿼리 실행
다음은 데이터베이스에 SQL 쿼리 구문을 실행하는 방법을 보여줍니다.
GoogleSQL
C++
ExecuteQuery()
를 사용하여 데이터베이스에 SQL 쿼리 문을 실행합니다.
C#
ExecuteReaderAsync()
를 사용하여 데이터베이스를 쿼리합니다.
Go
Client.Single().Query
를 사용하여 데이터베이스를 쿼리합니다.
Java
ReadContext.executeQuery
를 사용하여 데이터베이스를 쿼리합니다.
Node.js
Database.run
를 사용하여 데이터베이스를 쿼리합니다.
PHP
Database::execute
를 사용하여 데이터베이스를 쿼리합니다.
Python
Database.execute_sql
를 사용하여 데이터베이스를 쿼리합니다.
Ruby
Client#execute
를 사용하여 데이터베이스를 쿼리합니다.
SQL 문을 작성할 때 SQL 쿼리 문법 및 함수 및 연산자 참조 자료를 찾아보세요.
강력 읽기 수행
다음은 데이터베이스에서 강력 읽기로 0개 이상의 행을 읽는 방법을 보여줍니다.
GoogleSQL
C++
데이터를 읽는 코드는 SQL 쿼리를 실행하여 Spanner를 쿼리하는 앞선 샘플과 같습니다.
C#
데이터를 읽는 코드는 SQL 쿼리를 실행하여 Spanner를 쿼리하는 앞선 샘플과 같습니다.
Go
Client.Single().Read
를 사용하여 데이터베이스의 행을 읽습니다.
이 예시에서는 AllKeys
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
Java
ReadContext.read
를 사용하여 데이터베이스의 행을 읽습니다.
이 예시에서는 KeySet
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
Node.js
Table.read
를 사용하여 데이터베이스의 행을 읽습니다.
이 예시에서는 keySet
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
PHP
Database::read
를 사용하여 데이터베이스의 행을 읽습니다.
이 예시에서는 keySet
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
Python
Database.read
를 사용하여 데이터베이스의 행을 읽습니다.
이 예시에서는 KeySet
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
Ruby
Client#read
를 사용하여 데이터베이스의 행을 읽습니다.
비활성 읽기 수행
다음 샘플 코드는 완전 비활성 타임스탬프 경계를 사용하여 데이터베이스에서 비활성 읽기로 0개 이상의 행을 읽는 방법을 보여줍니다. 제한된 비활성 타임스탬프 경계를 사용하여 비활성 읽기를 수행하는 방법에 대한 자세한 내용은 샘플 코드 뒤에 나오는 참고 사항을 참조하세요. 사용할 수 있는 타임스탬프 경계의 여러 유형에 대한 자세한 내용은 타임스탬프 경계를 참조하세요.
GoogleSQL
C++
MakeReadOnlyTransaction()
및 Transaction::ReadOnlyOptions()
과 함께 ExecuteQuery()
를 사용하여 비활성 읽기를 수행합니다.
C#
지정된 TimestampBound.OfExactStaleness()
값이 있는 connection
에서 BeginReadOnlyTransactionAsync
메서드를 사용하여 데이터베이스를 쿼리합니다.
Go
Client.ReadOnlyTransaction().WithTimestampBound()
를 사용하고 ExactStaleness
값을 지정하여 완전 비활성 타임스탬프 경계를 통해 데이터베이스에서 행 읽기를 수행합니다.
이 예시에서는 AllKeys
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
Java
완전 비활성 타임스탬프 경계를 사용하여 데이터베이스에서 행 읽기를 수행하려면 지정된 TimestampBound.ofExactStaleness()
를 포함하는 ReadContext
의 read
메서드를 사용합니다.
이 예시에서는 KeySet
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
Node.js
완전 비활성 타임스탬프 경계를 사용하여 데이터베이스에서 행 읽기를 수행하려면 exactStaleness
옵션을 포함하는 Table.read
를 사용합니다.
이 예시에서는 keySet
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
PHP
완전 비활성 타임스탬프 경계를 사용하여 데이터베이스에서 행 읽기를 수행하려면 지정된 exactStaleness
값이 있는 Database::read
를 사용합니다.
이 예시에서는 keySet
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
Python
완전 비활성 타임스탬프 경계를 사용하여 데이터베이스에서 행 읽기를 수행하려면 지정된 exact_staleness
값을 포함하는 Database
snapshot
의 read
메서드를 사용합니다.
이 예시에서는 KeySet
를 사용하여 읽을 키 모음 또는 키 범위를 정의합니다.
Ruby
완전 비활성 타임스탬프 경계를 사용하여 데이터베이스에서 행 읽기를 수행하려면 지정된 staleness
값(초 단위)이 있는 스냅샷 Client
의 read
메서드를 사용합니다.
색인을 사용하여 읽기 수행
다음은 색인을 사용하여 데이터베이스에서 0개 이상의 행을 읽는 방법을 보여줍니다.
GoogleSQL
C++
Read()
함수를 사용하여 색인을 사용한 읽기를 수행합니다.
C#
색인을 사용하여 데이터를 읽으려면 명시적으로 색인을 지정하는 쿼리를 실행합니다.
Go
색인을 사용하여 데이터베이스에서 행을 읽으려면 Client.Single().ReadUsingIndex
를 사용합니다.
Java
색인을 사용하여 데이터베이스에서 행을 읽으려면 ReadContext.readUsingIndex
를 사용합니다.
Node.js
색인을 사용하여 데이터베이스에서 행을 읽으려면 Table.read
를 사용하여 쿼리에서 색인을 지정합니다.
PHP
색인을 사용하여 데이터베이스에서 행을 읽으려면 Database::read
를 사용하여 색인을 지정합니다.
Python
색인을 사용하여 데이터베이스에서 행을 읽으려면 Database.read
를 사용하여 색인을 지정합니다.
Ruby
색인을 사용하여 데이터베이스에서 행을 읽으려면 Client#read
를 사용하여 색인을 지정합니다.
동시에 데이터 읽기
Spanner에서 대규모 데이터가 포함된 대량 읽기 작업이나 쿼리 작업을 수행할 때 PartitionQuery
API를 사용하면 결과를 빨리 얻을 수 있습니다. API는 파티션을 병렬로 가져오기 위해 머신 여러 개를 사용하여 쿼리를 파티션이라고 하는 배치로 분할합니다. PartitionQuery
API는 전체 데이터베이스 내보내기 또는 스캔과 같은 대량 작업 전용으로 사용되기 때문에 이 API를 사용하면 지연 시간이 길어집니다.
Spanner 클라이언트 라이브러리를 사용하여 모든 읽기 API 작업을 동시에 수행할 수 있습니다. 하지만 쿼리에서 루트 분할이 가능한 경우에만 SQL 쿼리의 파티션을 나눌 수 있습니다. 쿼리를 루트로 분할할 수 있으려면 쿼리 계획이 다음 조건 중 하나를 충족해야 합니다.
쿼리 실행 계획의 첫 번째 연산자는 분산 통합이며 쿼리 실행 계획에는 분산 통합(로컬 배포 통합 제외) 하나만 포함됩니다. 쿼리 계획에는 분산 교차 적용과 같은 다른 분산 연산자가 포함될 수 없습니다.
쿼리 계획에는 분산 연산자가 없습니다.
PartitionQuery
API는 쿼리를 일괄 모드에서 실행합니다. Spanner는 일괄 모드에서 실행할 때 쿼리를 루트로 분할할 수 있게 해주는 쿼리 실행 계획을 선택할 수 있습니다. 따라서 PartitionQuery
API 및 Spanner 스튜디오는 같은 쿼리에 서로 다른 쿼리 실행 계획을 사용할 수 있습니다. Spanner 스튜디오의 PartitionQuery
API에서 사용하는 쿼리 실행 계획을 가져오지 못할 수 있습니다.
이와 같이 파티션을 나눈 쿼리의 경우 Spanner Data Boost를 사용 설정할 수 있습니다. Data Boost를 사용하면 프로비저닝된 Spanner 인스턴스의 기존 워크로드에 거의 영향을 주지 않고 대규모 분석 쿼리를 실행할 수 있습니다. 이 페이지의 C++, Go, Java, Node.js, Python 코드 예시에서는 Data Boost를 사용 설정하는 방법을 보여줍니다.
Data Boost에 대한 자세한 내용은 Data Boost 개요를 참고하세요.
GoogleSQL
C++
이 예시에서는 Singers
테이블에서 SQL 쿼리의 파티션을 가져오고 다음 단계를 통해 각 파티션에서 쿼리를 실행합니다.
- Spanner 일괄 트랜잭션 만들기
- 파티션을 여러 작업자에게 분산할 수 있도록 쿼리의 파티션 생성
- 각 파티션의 쿼리 결과 검색
C#
이 예시에서는 Singers
테이블에서 SQL 쿼리의 파티션을 가져오고 다음 단계를 통해 각 파티션에서 쿼리를 실행합니다.
- Spanner 일괄 트랜잭션 만들기
- 파티션을 여러 작업자에게 분산할 수 있도록 쿼리의 파티션 생성
- 각 파티션의 쿼리 결과 검색
Go
이 예시에서는 Singers
테이블에서 SQL 쿼리의 파티션을 가져오고 다음 단계를 통해 각 파티션에서 쿼리를 실행합니다.
- Spanner 클라이언트 및 트랜잭션 만들기
- 파티션을 여러 작업자에게 분산할 수 있도록 쿼리의 파티션 생성
- 각 파티션의 쿼리 결과 검색
Java
이 예시에서는 Singers
테이블에서 SQL 쿼리의 파티션을 가져오고 다음 단계를 통해 각 파티션에서 쿼리를 실행합니다.
- Spanner 일괄 클라이언트 및 트랜잭션 만들기
- 파티션을 여러 작업자에게 분산할 수 있도록 쿼리의 파티션 생성
- 각 파티션의 쿼리 결과 검색
Node.js
이 예시에서는 Singers
테이블에서 SQL 쿼리의 파티션을 가져오고 다음 단계를 통해 각 파티션에서 쿼리를 실행합니다.
- Spanner 클라이언트 및 배치 만들기
- 파티션을 여러 작업자에게 분산할 수 있도록 쿼리의 파티션 생성
- 각 파티션의 쿼리 결과 검색
PHP
이 예시에서는 Singers
테이블에서 SQL 쿼리의 파티션을 가져오고 다음 단계를 통해 각 파티션에서 쿼리를 실행합니다.
- Spanner 클라이언트 및 배치 만들기
- 파티션을 여러 작업자에게 분산할 수 있도록 쿼리의 파티션 생성
- 각 파티션의 쿼리 결과 검색
Python
이 예시에서는 Singers
테이블에서 SQL 쿼리의 파티션을 가져오고 다음 단계를 통해 각 파티션에서 쿼리를 실행합니다.
- Spanner 클라이언트 및 일괄 트랜잭션 만들기
- 파티션을 여러 작업자에게 분산할 수 있도록 쿼리의 파티션 생성
- 각 파티션의 쿼리 결과 검색
Ruby
이 예시에서는 Singers
테이블에서 SQL 쿼리의 파티션을 가져오고 다음 단계를 통해 각 파티션에서 쿼리를 실행합니다.
- Spanner 일괄 클라이언트 만들기
- 파티션을 여러 작업자에게 분산할 수 있도록 쿼리의 파티션 만들기
- 각 파티션의 쿼리 결과 검색