Wprowadzenie do Cloud Bigtable

1. Wprowadzenie

W tym module dowiesz się, jak używać Cloud Bigtable z klientem Java HBase.

Dowiesz się, jak:

  • Unikanie typowych błędów podczas projektowania schematu
  • Importowanie danych w pliku sekwencyjnym
  • Wysyłanie zapytania dotyczącego danych

Gdy skończysz, będziesz mieć kilka map z danymi o autobusach w Nowym Jorku. Na przykład utworzysz tę mapę termiczną przejazdów autobusowych na Manhattanie:

7349d94f7d41f1d1.png

Jak oceniasz korzystanie z Google Cloud Platform?

Początkujący Średnio zaawansowany Zaawansowany

Jak zamierzasz korzystać z tego samouczka?

Tylko przeczytaj Przeczytaj i wykonaj ćwiczenia

2. Informacje o zbiorze danych

Będziesz analizować zbiór danych o autobusach w Nowym Jorku. W mieście jest ponad 300 tras autobusowych i 5800 pojazdów,które nimi kursują. Nasz zbiór danych to dziennik, który zawiera nazwę miejsca docelowego, identyfikator pojazdu, szerokość i długość geograficzną, oczekiwany czas przyjazdu oraz zaplanowany czas przyjazdu. Zbiór danych składa się z migawek wykonanych w czerwcu 2017 roku co około 10 minut.

3. Projektowanie schematu

Aby uzyskać najlepszą wydajność Cloud Bigtable, musisz starannie zaprojektować schemat. Dane w Cloud Bigtable są automatycznie sortowane leksykograficznie, więc jeśli dobrze zaprojektujesz schemat, wyszukiwanie powiązanych danych będzie bardzo wydajne. Cloud Bigtable umożliwia wykonywanie zapytań za pomocą wyszukiwania punktowego według klucza wiersza lub skanowania zakresu wierszy, które zwraca ciągły zbiór wierszy. Jeśli jednak schemat nie jest dobrze przemyślany, może się okazać, że musisz łączyć ze sobą wiele wyszukiwań wierszy lub, co gorsza, wykonywać pełne skanowanie tabeli, które jest bardzo powolne.

Planowanie zapytań

Nasze dane zawierają różne informacje, ale w tym samouczku użyjesz lokalizacjimiejsca docelowego autobusu.

Na podstawie tych informacji możesz wykonać te zapytania:

  • Uzyskaj lokalizację pojedynczego autobusu w określonej godzinie.
  • Uzyskaj dane z całego dnia dotyczące linii autobusowej lub konkretnego autobusu.
  • Znajdź wszystkie autobusy w prostokącie na mapie.
  • uzyskać bieżące lokalizacje wszystkich autobusów (jeśli dane są przesyłane w czasie rzeczywistym);

Nie można optymalnie wykonać wszystkich tych zapytań jednocześnie. Jeśli na przykład sortujesz według czasu, nie możesz przeprowadzić skanowania na podstawie lokalizacji bez pełnego skanowania tabeli. Musisz ustalić priorytety na podstawie zapytań, które wykonujesz najczęściej.

W tym ćwiczeniu skupisz się na optymalizacji i wykonywaniu tego zestawu zapytań:

  • Uzyskiwanie lokalizacji konkretnego pojazdu w ciągu godziny.
  • Uzyskaj lokalizacje całej linii autobusowej w ciągu godziny.
  • Uzyskaj lokalizacje wszystkich autobusów na Manhattanie w ciągu godziny.
  • Uzyskaj najnowsze lokalizacje wszystkich autobusów na Manhattanie w ciągu godziny.
  • uzyskać informacje o lokalizacji całej linii autobusowej w ciągu miesiąca;
  • Uzyskaj lokalizacje wszystkich autobusów na danej linii, które w ciągu godziny zmierzają do określonego miejsca docelowego.

Zaprojektuj klucz wiersza

W tym ćwiczeniu będziesz pracować ze statycznym zbiorem danych, ale zaprojektujesz schemat z myślą o skalowalności. Zaprojektujesz schemat, który umożliwi Ci przesyłanie do tabeli większej ilości danych o autobusach przy zachowaniu dobrej wydajności.

Oto proponowany schemat klucza wiersza:

[Przewoźnik autobusowy/Linia autobusowa/Sygnatura czasowa zaokrąglona w dół do godziny/Identyfikator pojazdu]. Każdy wiersz zawiera dane z godziny, a każda komórka zawiera wiele wersji danych z sygnaturami czasowymi.

W tym ćwiczeniu użyjesz jednej grupy kolumn, aby uprościć zadanie. Oto przykładowy widok danych. Dane są sortowane według klucza wiersza.

Klucz wiersza

cf:VehicleLocation.Latitude

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. Tworzenie instancji, tabeli i grupy kolumn

Następnie utworzysz tabelę Cloud Bigtable.

Najpierw utwórz nowy projekt. Użyj wbudowanego Cloud Shell, który możesz otworzyć, klikając przycisk „Aktywuj Cloud Shell” w prawym górnym rogu.

a74d156ca7862b28.png

Aby ułatwić kopiowanie i wklejanie poleceń z samouczka, ustaw te zmienne środowiskowe:

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

Cloud Shell zawiera narzędzia, których będziesz używać w tym ćwiczeniu programistycznym, czyli narzędzie wiersza poleceń gcloud, interfejs wiersza poleceń cbt i Maven.

Włącz interfejsy Cloud Bigtable API, uruchamiając to polecenie.

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

Utwórz instancję, uruchamiając to polecenie:

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

Po utworzeniu instancji wypełnij plik konfiguracyjny cbt, a następnie utwórz tabelę i grupę kolumn, uruchamiając te polecenia:

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

cbt createtable $TABLE_ID
cbt createfamily $TABLE_ID cf

5. Importuj dane

Zaimportuj zestaw plików sekwencji do tego laboratorium z gs://cloud-bigtable-public-datasets/bus-data, wykonując te czynności:

Włącz interfejs Cloud Dataflow API, uruchamiając to polecenie.

gcloud services enable dataflow.googleapis.com

Aby zaimportować tabelę, uruchom te polecenia.

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/*

Monitorowanie importu

Zadanie możesz monitorować w interfejsie Cloud Dataflow. Obciążenie instancji Cloud Bigtable możesz też sprawdzić w interfejsie monitorowania. Cały import powinien zająć 5 minut.

6. Pobierz kod

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

Aby przejść na Javę 11, uruchom te polecenia:

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

7. Przeprowadź wyszukiwanie

Pierwsze zapytanie, które wykonasz, to proste wyszukiwanie wiersza. Otrzymasz dane dotyczące autobusu na linii M86-SBS z 1 czerwca 2017 r. z godzin 0:00–1:00. W tym czasie na linii autobusowej znajduje się pojazd o identyfikatorze NYCT_5824.

Na podstawie tych informacji i znajomości schematu (firma autobusowa/linia autobusowa/sygnatura czasowa zaokrąglona w dół do godziny/identyfikator pojazdu) możesz wywnioskować, że klucz wiersza to:

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));

Wynik powinien zawierać najnowszą lokalizację autobusu w ciągu tej godziny. Chcesz jednak zobaczyć wszystkie lokalizacje, więc w żądaniu GET ustaw maksymalną liczbę wersji.

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));

Aby uzyskać listę szerokości i długości geograficznych tego autobusu w ciągu godziny, uruchom w Cloud Shell to polecenie:

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

Możesz skopiować i wkleić szerokość i długość geograficzną w aplikacji MapMaker, aby wizualizować wyniki. Po kilku warstwach pojawi się prośba o utworzenie bezpłatnego konta. Możesz utworzyć konto lub po prostu usunąć istniejące warstwy. Ten przewodnik zawiera wizualizację każdego kroku, jeśli chcesz po prostu śledzić postępy. Oto wynik pierwszego zapytania:

f1a1fac6051c6210.png

8. Przeprowadzanie skanowania

Teraz wyświetlmy wszystkie dane dotyczące linii autobusowej w tym przedziale czasowym. Kod skanowania wygląda podobnie do kodu pobierania. Podajesz skanerowi pozycję początkową, a następnie wskazujesz, że chcesz wyświetlić tylko wiersze dotyczące linii autobusowej M86-SBS w godzinie oznaczonej sygnaturą czasową 1496275200000.

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);

Aby uzyskać wyniki, uruchom to polecenie.

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

c18a4ac6522d08a2.png

Aplikacja Map Maker może wyświetlać kilka list jednocześnie, dzięki czemu możesz sprawdzić, które autobusy są pojazdami z pierwszego zapytania.

234c1b51e3b201e.png

Ciekawą modyfikacją tego zapytania jest wyświetlenie danych za cały miesiąc dla linii autobusowej M86-SBS. Jest to bardzo proste. Aby uzyskać wynik, usuń sygnaturę czasową z wiersza początkowego i filtra prefiksu.

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);

Aby uzyskać wyniki, uruchom to polecenie. (Pojawi się długa lista wyników).

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

Jeśli skopiujesz wyniki do MapMakera, możesz wyświetlić mapę termiczną trasy autobusu. Pomarańczowe plamy oznaczają przystanki, a jasnoczerwone plamy to początek i koniec trasy.

346f52e61b3d8902.png

9. Wprowadzenie filtrów

Następnie odfiltrujesz autobusy jadące na wschód i na zachód oraz utworzysz dla nich oddzielne mapy cieplne.

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);

Aby uzyskać wyniki dotyczące autobusów jadących na wschód, uruchom to polecenie:

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

Aby uzyskać informacje o autobusach jadących na zachód, zmień ciąg znaków w parametrze valueFilter:

BusQueries.java

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

Aby uzyskać wyniki dotyczące autobusów jadących na zachód, uruchom to polecenie:

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

Autobusy jadące na wschód

76f6f62096a6847a.png

Autobusy jadące na zachód

2b5771ee9046399f.png

Porównując te 2 mapy aktywności, możesz zobaczyć różnice w trasach, a także w tempie. Jedna z interpretacji danych jest taka, że na trasie w kierunku zachodnim autobusy są częściej zatrzymywane, zwłaszcza przy wjeździe do Central Parku. A w przypadku autobusów jadących na wschód nie widać wielu wąskich gardeł.

10. Przeprowadzanie skanowania w wielu zakresach

W przypadku ostatniego zapytania zajmiesz się sytuacją, w której interesuje Cię wiele linii autobusowych w danym obszarze:

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);

Aby uzyskać wyniki, uruchom to polecenie.

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

7349d94f7d41f1d1.png

11. Zakończ

Zwolnij miejsce, aby uniknąć opłat

Aby uniknąć obciążenia konta Google Cloud Platform opłatami za zasoby zużyte w tym laboratorium, usuń instancję.

gcloud bigtable instances delete $INSTANCE_ID

Omówione zagadnienia

  • Projektowanie schematu
  • Konfigurowanie instancji, tabeli i rodziny
  • Importowanie plików sekwencji za pomocą Dataflow
  • Wykonywanie zapytań za pomocą wyszukiwania, skanowania, skanowania z filtrem i skanowania wielu zakresów

Dalsze kroki