Cloud Bigtable 소개

1. 소개

이 Codelab에서는 자바 HBase 클라이언트와 함께 Cloud Bigtable을 사용하는 방법을 소개합니다.

학습 목표

  • 스키마 설계에서 흔히 발생하는 실수 방지
  • 시퀀스 파일로 데이터 가져오기
  • 데이터 쿼리

완료되면 뉴욕시 버스 데이터를 보여주는 여러 지도가 표시됩니다. 예를 들어 맨해튼의 버스 운행에 대한 다음 히트맵을 만듭니다.

7349d94f7d41f1d1.png

귀하의 Google Cloud Platform 사용 경험을 평가해 주세요.

초급 중급 고급

본 가이드를 어떻게 사용하실 계획인가요?

<ph type="x-smartling-placeholder"></ph> 읽기 전용 읽고 연습 활동을 완료하세요

2. 데이터 세트에 관한 정보

뉴욕시 버스에 대한 데이터 세트를 살펴보겠습니다. 현재 버스 노선은 300개가 넘고, 이들 노선을 따라가는 차량은 5,800대입니다. 데이터 세트는 목적지 이름, 차량 ID, 위도, 경도, 예상 도착 시간, 예정된 도착 시간이 포함된 로그입니다. 데이터 세트는 2017년 6월에 약 10분마다 촬영된 스냅샷으로 구성됩니다.

3. 스키마 설계

Cloud Bigtable에서 최고의 성능을 얻으려면 스키마를 설계할 때 신중해야 합니다. Cloud Bigtable의 데이터는 사전순으로 자동 정렬되므로 스키마를 잘 설계하면 관련 데이터를 쿼리하는 것이 매우 효율적입니다. Cloud Bigtable에서는 row key에 의한 포인트 조회 또는 연속된 행 집합을 반환하는 행 범위 스캔을 사용하는 쿼리를 사용할 수 있습니다. 하지만 스키마를 잘 생각하지 않으면 여러 행 조회를 조합하거나 전체 테이블 스캔을 수행해야 할 수 있습니다. 전체 테이블 스캔은 작업 속도가 매우 느립니다.

쿼리 계획

Google의 데이터에는 다양한 정보가 있지만 이 Codelab에서는 버스의 위치목적지를 사용합니다.

이 정보를 사용하여 다음 쿼리를 수행할 수 있습니다.

  • 특정 시간 동안 단일 버스의 위치를 가져옵니다.
  • 버스 노선 또는 특정 버스에 대한 하루 분량의 데이터를 가져옵니다.
  • 지도에서 직사각형 안에 있는 모든 버스를 찾습니다.
  • 모든 버스의 현재 위치를 가져옵니다 (이 데이터를 실시간으로 수집하는 경우).

이 쿼리 집합을 모두 최적으로 실행할 수는 없습니다. 예를 들어 시간순으로 정렬하는 경우 전체 테이블을 스캔하지 않고는 위치를 기준으로 스캔할 수 없습니다. 가장 자주 실행하는 쿼리를 기반으로 하여 우선순위를 지정해야 합니다.

이 Codelab에서는 다음 쿼리 집합을 최적화하고 실행하는 데 중점을 둡니다.

  • 한 시간 이상 특정 차량의 위치를 가져옵니다.
  • 한 시간 동안 버스 노선의 전체 위치를 가져옵니다.
  • 한 시간 안에 맨해튼에 있는 모든 버스의 위치를 확인합니다.
  • 한 시간 안에 부산에 있는 모든 버스의 최신 위치를 가져옵니다.
  • 한 달 동안 버스 노선의 전체 위치를 가져옵니다.
  • 한 시간 이상 특정 목적지가 있는 전체 버스 노선의 위치를 확인할 수 있습니다.

row key 설계

이 Codelab에서는 정적 데이터 세트로 작업하지만 확장성을 위한 스키마를 설계합니다. 더 많은 버스 데이터를 테이블로 스트리밍하면서 우수한 성능을 유지할 수 있는 스키마를 설계해 보겠습니다.

row key에 대해 제안된 스키마는 다음과 같습니다.

[버스 회사/버스 노선/타임스탬프에서 시간/차량 ID로 내림]. 각 행에는 한 시간 분량의 데이터가 있으며 각 셀에는 타임스탬프가 있는 여러 버전의 데이터가 있습니다.

이 Codelab에서는 작업을 단순화하기 위해 하나의 column family를 사용합니다. 다음은 데이터가 표시되는 방식의 예입니다. 데이터는 row key를 기준으로 정렬됩니다.

row key

cf:차량 위치.위도

cf:VehicleLocation.Longitude를 사용하여 차량 위치를 선택하세요.

...

MTA/M86-SBS/1496275200000/NYCT_5824

40.781212 @20:52:54.0040.776163 @20:43:19.0040.778714 @20:33:46.00

-73.961942 @20:52:54.00-73.946949 @20:43:19.00-73.953731 @20:33:46.00

...

MTA/M86-SBS/1496275200000/NYCT_5840

40.780664 @20:13:51.0040.788416 @20:03:40.00

-73.958357 @20:13:51.00 -73.976748 @20:03:40.00

...

MTA/M86-SBS/1496275200000/NYCT_5867

40.780281 @20:51:45.0040.779961 @20:43:15.0040.788416 @20:33:44.00

-73.946890 @20:51:45.00-73.959465 @20:43:15.00-73.976748 @20:33:44.00

...

...

...

...

...

4. 인스턴스, 테이블, 계열 만들기

다음으로 Cloud Bigtable 테이블을 만듭니다.

먼저 새 프로젝트를 만듭니다. 'Cloud Shell 활성화'를 클릭하여 열 수 있는 기본 제공 Cloud Shell 사용 버튼을 클릭합니다.

a74d156ca7862b28.png

Codelab 명령어를 더 쉽게 복사하고 붙여넣을 수 있도록 다음 환경 변수를 설정합니다.

INSTANCE_ID="bus-instance"
CLUSTER_ID="bus-cluster"
TABLE_ID="bus-data"
CLUSTER_NUM_NODES=3
CLUSTER_ZONE="us-central1-c"

Cloud Shell에는 이 Codelab에서 사용할 도구, gcloud 명령줄 도구, cbt 명령줄 인터페이스, Maven이 이미 설치되어 있습니다.

다음 명령어를 실행하여 Cloud Bigtable API를 사용 설정합니다.

gcloud services enable bigtable.googleapis.com bigtableadmin.googleapis.com

다음 명령어를 실행하여 인스턴스를 만듭니다.

gcloud bigtable instances create $INSTANCE_ID \
    --cluster=$CLUSTER_ID \
    --cluster-zone=$CLUSTER_ZONE \
    --cluster-num-nodes=$CLUSTER_NUM_NODES \
    --display-name=$INSTANCE_ID

인스턴스를 만든 후 cbt 구성 파일을 채운 후 다음 명령어를 실행하여 테이블과 column family를 만듭니다.

echo project = $GOOGLE_CLOUD_PROJECT > ~/.cbtrc
echo instance = $INSTANCE_ID >> ~/.cbtrc

cbt createtable $TABLE_ID
cbt createfamily $TABLE_ID cf

5. 데이터 가져오기

다음 단계에 따라 gs://cloud-bigtable-public-datasets/bus-data에서 이 Codelab의 시퀀스 파일 세트를 가져옵니다.

다음 명령어를 실행하여 Cloud Dataflow API를 사용 설정합니다.

gcloud services enable dataflow.googleapis.com

다음 명령어를 실행하여 테이블을 가져옵니다.

NUM_WORKERS=$(expr 3 \* $CLUSTER_NUM_NODES)
gcloud beta dataflow jobs run import-bus-data-$(date +%s) \
--gcs-location gs://dataflow-templates/latest/GCS_SequenceFile_to_Cloud_Bigtable \
--num-workers=$NUM_WORKERS --max-workers=$NUM_WORKERS \
--parameters bigtableProject=$GOOGLE_CLOUD_PROJECT,bigtableInstanceId=$INSTANCE_ID,bigtableTableId=$TABLE_ID,sourcePattern=gs://cloud-bigtable-public-datasets/bus-data/*

가져오기 모니터링

Cloud Dataflow UI에서 작업을 모니터링할 수 있습니다. 또한 모니터링 UI를 통해 Cloud Bigtable 인스턴스의 부하를 확인할 수 있습니다. 전체 가져오기에는 5분 정도 걸립니다.

6. 코드 가져오기

git clone https://github.com/googlecodelabs/cbt-intro-java.git
cd cbt-intro-java

다음 명령어를 실행하여 자바 11로 변경합니다.

sudo update-java-alternatives -s java-1.11.0-openjdk-amd64 && export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/

7. 조회 수행

수행할 첫 번째 쿼리는 간단한 행 조회입니다. 2017년 6월 1일 오전 12시부터 오전 1시까지 M86-SBS 노선 버스 데이터를 확인할 수 있습니다. 이때 ID가 NYCT_5824인 차량이 버스 노선에 있습니다.

이 정보와 스키마 설계 (버스 회사/버스 노선/타임스탬프로 시간/차량 ID로 내림됨)를 알면 행 키가 다음과 같음을 추론할 수 있습니다.

MTA/M86-SBS/1496275200000/NYCT_5824

BusQueries.java

private static final byte[] COLUMN_FAMILY_NAME = Bytes.toBytes("cf");
private static final byte[] LAT_COLUMN_NAME = Bytes.toBytes("VehicleLocation.Latitude");
private static final byte[] LONG_COLUMN_NAME = Bytes.toBytes("VehicleLocation.Longitude");

String rowKey = "MTA/M86-SBS/1496275200000/NYCT_5824";
Result getResult =
    table.get(
        new Get(Bytes.toBytes(rowKey))
            .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
            .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME));

결과에는 해당 시간 이내의 가장 최근 버스 위치가 포함됩니다. 그러나 모든 위치를 보려면 get 요청에서 최대 버전 수를 설정하세요.

BusQueries.java

Result getResult =
    table.get(
        new Get(Bytes.toBytes(rowKey))
            .setMaxVersions(Integer.MAX_VALUE)
            .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
            .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME));

Cloud Shell에서 다음 명령어를 실행하여 해당 버스의 시간 경과에 따른 위도 및 경도 목록을 가져옵니다.

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=lookupVehicleInGivenHour

위도와 경도를 복사하여 맵메이커 앱에 붙여넣으면 결과를 시각화할 수 있습니다. 몇 개의 레이어를 완료하면 무료 계정을 만들라는 메시지가 표시됩니다. 계정을 만들거나 기존 레이어를 삭제할 수 있습니다. 이 Codelab에는 각 단계의 시각화가 포함되어 있으므로 따라해 보면 됩니다. 이 첫 번째 쿼리의 결과는 다음과 같습니다.

f1a1fac6051c6210.png

8. 검사 실행

이제 해당 시간의 버스 노선에 대한 모든 데이터를 확인해 보겠습니다. 스캔 코드는 get 코드와 매우 유사합니다. 스캐너에 시작 위치를 지정한 다음 타임스탬프 1496275200000으로 표시된 시간 내에 M86-SBS 버스 노선에 대한 행만 원한다고 나타냅니다.

BusQueries.java

Scan scan;
scan = new Scan();
scan.setMaxVersions(Integer.MAX_VALUE)
    .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
    .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME)
    .withStartRow(Bytes.toBytes("MTA/M86-SBS/1496275200000"))
    .setRowPrefixFilter(Bytes.toBytes("MTA/M86-SBS/1496275200000"));
ResultScanner scanner = table.getScanner(scan);

다음 명령어를 실행하여 결과를 가져옵니다.

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=scanBusLineInGivenHour

c18a4ac6522d08a2.png

맵 메이커 앱은 한 번에 여러 목록을 표시할 수 있으므로 실행한 첫 번째 쿼리를 통해 어떤 버스가 차량인지 확인할 수 있습니다.

234c1b51e3b201e.png

이 쿼리를 수정해 보면 M86-SBS 버스 노선의 한 달 전체 데이터를 볼 수 있어 매우 쉽습니다. 결과를 얻으려면 시작 행과 접두사 필터에서 타임스탬프를 삭제하세요.

BusQueries.java

scan.withStartRow(Bytes.toBytes("MTA/M86-SBS/"))
    .setRowPrefixFilter(Bytes.toBytes("MTA/M86-SBS/"));

// Optionally, reduce the results to receive one version per column
// since there are so many data points.
scan.setMaxVersions(1);

다음 명령어를 실행하여 결과를 가져옵니다. (결과 목록이 매우 많음)

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=scanEntireBusLine

결과를 지도 작성기에 복사하면 버스 노선의 히트맵을 볼 수 있습니다. 주황색 blob은 정류장을 나타내고 밝은 빨간색 blob은 경로의 시작과 끝을 나타냅니다.

346f52e61b3d8902.png

9. 필터 도입

다음으로 동쪽으로 향하는 버스와 서쪽으로 향하는 버스를 필터링하고 각각에 대해 별도의 히트맵을 만듭니다.

BusQueries.java

Scan scan;
ResultScanner scanner;
scan = new Scan();
SingleColumnValueFilter valueFilter =
    new SingleColumnValueFilter(
        COLUMN_FAMILY_NAME,
        Bytes.toBytes("DestinationName"),
        CompareOp.EQUAL,
        Bytes.toBytes("Select Bus Service Yorkville East End AV"));

scan.setMaxVersions(1)
    .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
    .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME);
scan.withStartRow(Bytes.toBytes("MTA/M86-SBS/"))
    .setRowPrefixFilter(Bytes.toBytes("MTA/M86-SBS/"));
scan.setFilter(valueFilter);
scanner = table.getScanner(scan);

다음 명령어를 실행하여 동쪽으로 가는 버스의 결과를 가져옵니다.

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=filterBusesGoingEast

서쪽으로 가는 버스를 가져오려면 valueFilter의 문자열을 변경합니다.

BusQueries.java

SingleColumnValueFilter valueFilter =
    new SingleColumnValueFilter(
        COLUMN_FAMILY_NAME,
        Bytes.toBytes("DestinationName"),
        CompareOp.EQUAL,
        Bytes.toBytes("Select Bus Service Westside West End AV"));

다음 명령어를 실행하여 서쪽으로 가는 버스의 결과를 가져옵니다.

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=filterBusesGoingWest

동쪽으로 향하는 버스

76f6f62096a6847a.png

서쪽으로 가는 버스

2b5771ee9046399f.png

두 히트맵을 비교하면 경로의 차이는 물론 속도의 차이를 확인할 수 있습니다. 데이터에 대한 한 가지 해석은 서쪽으로 향하는 경로에서 특히 센트럴 파크에 진입할 때 버스가 더 많이 정차한다는 것입니다. 그리고 동쪽으로 향하는 버스에서는 실제로 초크 포인트가 많지 않습니다.

10. 다중 범위 스캔 실행

마지막 쿼리에서는 한 지역에서 많은 버스 노선이 중요한 경우의 사례를 살펴보겠습니다.

BusQueries.java

private static final String[] MANHATTAN_BUS_LINES = {"M1","M2","M3",...

Scan scan;
ResultScanner scanner;
List<RowRange> ranges = new ArrayList<>();
for (String busLine : MANHATTAN_BUS_LINES) {
  ranges.add(
      new RowRange(
          Bytes.toBytes("MTA/" + busLine + "/1496275200000"), true,
          Bytes.toBytes("MTA/" + busLine + "/1496275200001"), false));
}
Filter filter = new MultiRowRangeFilter(ranges);
scan = new Scan();
scan.setFilter(filter);
scan.setMaxVersions(Integer.MAX_VALUE)
    .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
    .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME);
scan.withStartRow(Bytes.toBytes("MTA/M")).setRowPrefixFilter(Bytes.toBytes("MTA/M"));
scanner = table.getScanner(scan);

다음 명령어를 실행하여 결과를 가져옵니다.

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=scanManhattanBusesInGivenHour

7349d94f7d41f1d1.png

11. 완료

청구되지 않도록 정리하기

이 Codelab에서 사용한 리소스 비용이 Google Cloud Platform 계정에 청구되지 않도록 하려면 인스턴스를 삭제해야 합니다.

gcloud bigtable instances delete $INSTANCE_ID

학습한 내용

  • 스키마 설계
  • 인스턴스, 테이블, 계열 설정
  • Dataflow로 시퀀스 파일 가져오기
  • 조회, 스캔, 필터를 사용한 스캔, 다범위 스캔으로 쿼리

다음 단계