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

Как бы вы оценили свой опыт использования платформы 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» в правом верхнем углу.

Для упрощения копирования и вставки команд из 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 , чтобы визуализировать результаты. После добавления нескольких слоев вам будет предложено создать бесплатную учетную запись. Вы можете создать учетную запись или просто удалить существующие слои. В этом практическом задании для каждого шага есть визуализация, если вы просто хотите следовать инструкциям. Вот результат для первого запроса:

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

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

Интересная модификация этого запроса — просмотр данных за весь месяц для автобусной линии 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, можно просмотреть тепловую карту автобусного маршрута. Оранжевые точки обозначают остановки, а ярко-красные — начало и конец маршрута.

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
Автобусы, направляющиеся на восток

Автобусы, направляющиеся на запад

Сравнивая две тепловые карты, можно увидеть различия в маршрутах, а также заметить разницу в темпе движения. Одна из интерпретаций данных заключается в том, что на маршруте, ведущем на запад, автобусы останавливаются чаще, особенно при въезде в Центральный парк. А на маршруте, ведущем на восток, узких мест практически нет.
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

11. Завершить
Уберитесь, чтобы избежать обвинений.
Чтобы избежать списания средств с вашего аккаунта Google Cloud Platform за ресурсы, использованные в этом практическом занятии, вам следует удалить свой экземпляр.
gcloud bigtable instances delete $INSTANCE_ID
Что мы рассмотрели
- проектирование схемы
- Настройка экземпляра, таблицы и семейства
- Импорт файлов последовательностей с помощью DataFrame.
- Запросы с использованием поиска, сканирования, сканирования с фильтром и многодиапазонного сканирования.
Следующие шаги
- Подробнее о Cloud Bigtable можно узнать в документации .
- Попробуйте другие функции Google Cloud Platform самостоятельно. Ознакомьтесь с нашими обучающими материалами .
- Узнайте, как отслеживать данные временных рядов с помощью интеграции с OpenTSDB.