Введение в Cloud Bigtable

1. Введение

В этом практическом занятии вы познакомитесь с использованием Cloud Bigtable с Java-клиентом HBase .

Вы узнаете, как

  • Избегайте распространенных ошибок при проектировании схем.
  • Импорт данных из файла последовательности
  • Запросите ваши данные

В итоге у вас получится несколько карт, отображающих данные об автобусных маршрутах Нью-Йорка. Например, вы создадите тепловую карту автобусных поездок по Манхэттену:

7349d94f7d41f1d1.png

Как бы вы оценили свой опыт использования платформы Google Cloud Platform?

Новичок Средний Профессионал

Как вы будете использовать этот учебный материал?

Прочитайте только от начала до конца. Прочитайте текст и выполните упражнения.

2. О наборе данных

Вы будете работать с набором данных о автобусах Нью-Йорка. Существует более 300 автобусных маршрутов и 5800 транспортных средств, следующих по этим маршрутам. Наш набор данных представляет собой журнал, включающий название пункта назначения, идентификатор транспортного средства, широту, долготу, ожидаемое время прибытия и запланированное время прибытия. Набор данных состоит из снимков, сделанных примерно каждые 10 минут в июне 2017 года.

3. Разработка схемы

Для достижения максимальной производительности Cloud Bigtable необходимо тщательно продумать схему . Данные в Cloud Bigtable автоматически сортируются лексикографически, поэтому, если схема разработана грамотно, запросы к связанным данным будут очень эффективными. Cloud Bigtable позволяет выполнять запросы с использованием точечного поиска по ключу строки или сканирования диапазонов строк , возвращающих непрерывный набор строк. Однако, если схема не продумана должным образом, вы можете столкнуться с необходимостью объединения нескольких запросов к строкам или, что еще хуже, с необходимостью полного сканирования таблицы, что является крайне медленной операцией.

Составьте план запросов.

Наши данные содержат разнообразную информацию, но для этой практической работы вам понадобятся местоположение и пункт назначения автобуса.

Имея эту информацию, вы можете выполнить следующие запросы:

  • Получите местоположение одного автобуса в течение заданного часа.
  • Получите данные за один день по автобусному маршруту или конкретному автобусу.
  • Найдите все автобусы, расположенные в прямоугольнике на карте.
  • Получите текущее местоположение всех автобусов (если вы обрабатываете эти данные в режиме реального времени).

Этот набор запросов невозможно оптимально выполнить одновременно. Например, если вы сортируете данные по времени, вы не можете выполнить сканирование по местоположению без полного сканирования таблицы. Вам необходимо расставить приоритеты, исходя из того, какие запросы вы выполняете чаще всего.

В рамках этого практического занятия вы сосредоточитесь на оптимизации и выполнении следующего набора запросов:

  • Получите информацию о местоположении конкретного транспортного средства в течение часа.
  • Получите информацию о местоположении всего автобусного маршрута в течение часа.
  • Узнайте местоположение всех автобусов на Манхэттене за час.
  • Получите самые актуальные координаты всех автобусов на Манхэттене в течение часа.
  • Получите информацию о местоположении всех автобусных маршрутов за месяц.
  • Получите информацию о местоположении всего автобусного маршрута с определенным пунктом назначения в течение часа.

Разработайте ключ к строкам.

В этом практическом задании вы будете работать со статическим набором данных, но разработаете схему, обеспечивающую масштабируемость. Вы создадите схему, которая позволит передавать больше данных о шине в таблицу и при этом сохранит высокую производительность.

Вот предлагаемая схема для ключа строки:

[Автобусная компания/Автобусный маршрут/Время, округленное до часа/Идентификатор транспортного средства]. Каждая строка содержит данные за один час, а каждая ячейка содержит несколько версий данных с указанием времени.

Для этого практического задания вы будете использовать одно семейство столбцов, чтобы упростить задачу. Вот пример того, как выглядят данные. Данные отсортированы по ключу строки.

Ключ строки

см.:VehicleLocation.Latitude

cf:VehicleLocation.Longitude

...

MTA/M86-SBS/1496275200000/NYCT_5824

40.781212 @20:52:54.00 40.776163 @20:43:19.00 40.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.00 40.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.00 40.779961 @20:43:15.00 40.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 уже установлены инструменты, которые вы будете использовать в этом практическом занятии: инструмент командной строки gcloud , интерфейс командной строки cbt и Maven .

Активируйте API Cloud Bigtable, выполнив эту команду.

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, а затем создайте таблицу и семейство столбцов, выполнив следующие команды:

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 выполнив следующие шаги:

Включите API Cloud Dataflow, выполнив эту команду.

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 . Кроме того, вы можете просмотреть нагрузку на ваш экземпляр Cloud Bigtable с помощью его интерфейса мониторинга . Весь процесс импорта должен занять 5 минут.

6. Получите код

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

Для переключения на Java 11 выполните следующие команды:

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

7. Выполните поиск.

Первый запрос, который вы выполните, — это простой поиск по строке. Вам понадобятся данные об автобусе, курсирующем по маршруту M86-SBS 1 июня 2017 года с 00:00 до 01:00. В это время на маршруте находится транспортное средство с идентификатором NYCT_5824 .

Имея эту информацию и зная схему данных (автобусная компания/автобусный маршрут/временная метка, округленная до часа/идентификатор транспортного средства), можно сделать вывод, что ключом строки является:

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

Вы можете скопировать и вставить широту и долготу в приложение MapMaker , чтобы визуализировать результаты. После добавления нескольких слоев вам будет предложено создать бесплатную учетную запись. Вы можете создать учетную запись или просто удалить существующие слои. В этом практическом задании для каждого шага есть визуализация, если вы просто хотите следовать инструкциям. Вот результат для первого запроса:

f1a1fac6051c6210.png

8. Выполните сканирование.

Теперь давайте посмотрим все данные по автобусному маршруту за этот час. Код сканирования очень похож на код получения данных. Вы задаете сканеру начальную позицию, а затем указываете, что хотите получить только строки для автобусного маршрута M86-SBS за час, обозначенный меткой времени 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);

Выполните следующую команду, чтобы получить результаты.

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

c18a4ac6522d08a2.png

Приложение Map Maker может отображать несколько списков одновременно, поэтому вы можете увидеть, какой из автобусов соответствует транспортному средству из первого выполненного вами запроса.

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

Если скопировать результаты в MapMaker, можно просмотреть тепловую карту автобусного маршрута. Оранжевые точки обозначают остановки, а ярко-красные — начало и конец маршрута.

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. Завершить

Уберитесь, чтобы избежать обвинений.

Чтобы избежать списания средств с вашего аккаунта Google Cloud Platform за ресурсы, использованные в этом практическом занятии, вам следует удалить свой экземпляр.

gcloud bigtable instances delete $INSTANCE_ID

Что мы рассмотрели

  • проектирование схемы
  • Настройка экземпляра, таблицы и семейства
  • Импорт файлов последовательностей с помощью DataFrame.
  • Запросы с использованием поиска, сканирования, сканирования с фильтром и многодиапазонного сканирования.

Следующие шаги