Время отклика (задержка) сканирования / получения Bigtable при низкочастотных вызовах очень велико

У меня есть одна небольшая таблица (размером 100 МБ) в bigtable с 10 экземплярами. Когда я пытаюсь сканировать / получать строку раз в 1 минуту, задержка вызова составляет более 300 мс. Если я нажимаю на более частые вызовы, например, один раз в секунду, задержка составляет 50-60 мс. Я не уверен, как можно улучшить производительность с помощью низкочастотных вызовов. это ожидаемое поведение. или я что-то не так делаю.

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

Какие-либо предложения?

package com.bids;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.util.Bytes;
import org.fusesource.jansi.AnsiConsole;

public class BTConnectTest {
    public static void main(String[] args) throws IOException, InterruptedException {

        Configuration hBaseConfig = HBaseConfiguration.create();
        hBaseConfig.set("google.bigtable.project.id", "xxxxxxx");
        hBaseConfig.set("google.bigtable.cluster.name", "hbase-test1");
        hBaseConfig.set("google.bigtable.zone.name", "us-central1-b");
        hBaseConfig.set("hbase.client.connection.impl", "com.google.cloud.bigtable.hbase1_1.BigtableConnection");

        ExecutorService executor = Executors.newSingleThreadExecutor();

        final Connection bigTableConnection1 = ConnectionFactory.createConnection(hBaseConfig, executor);

        final Connection bigTableConnection2 = ConnectionFactory.createConnection(hBaseConfig, executor);

        Thread t = new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    long before = System.nanoTime();
                    try {
                        makeACall2Bigtable(bigTableConnection2);
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    // bigTableConnection.close();
                    long after = System.nanoTime();

                    long diff = after - before;

                    System.out.println("\t\t\t\t\t\t connection: " + 1 + " diff: " + diff / (1000 * 1000));
                }
            }
        });
        t.start();

        long sum = 0;
        int n = 0;
        while (true) {
            if (n > 60) {
                Thread.sleep(60000);
            }

            long before = System.nanoTime();

            Connection bigTableConnection = bigTableConnection1;
            int label = -1;

            makeACall2Bigtable(bigTableConnection);
            long after = System.nanoTime();

            long diff = after - before;
            n = n + 1;
            sum += diff;
            long avg = sum / (n * 1000 * 1000);
            AnsiConsole a = new AnsiConsole();

            System.out.println("connection: " + 0 + " diff: " + diff / (1000 * 1000) + " avg: " + avg);

        }
        // bigTableConnection.close();

    }

    private static void makeACall2Bigtable(Connection bigTableConnection) throws IOException {

        Table table = bigTableConnection.getTable(TableName.valueOf("customer"));
        Scan scan = new Scan();
        scan.setStartRow(Bytes.toBytes("101"));
        scan.setStopRow(Bytes.toBytes("102"));
        List<String> cols = new ArrayList<String>(3);
        cols.add("name");
        cols.add("age");
        cols.add("weight");
        String keyName = "id";
        final String DEFAULT_COLFAM = "z";
        for (String col : cols) {
            scan.addColumn(Bytes.toBytes(DEFAULT_COLFAM), Bytes.toBytes(col));
        }
        ResultScanner resultScanner = table.getScanner(scan);

        for (Result result : resultScanner) {
            Map<String, String> columnValueMap = new LinkedHashMap<String, String>();
            for (String col : cols) {
                if (result.containsColumn(Bytes.toBytes(DEFAULT_COLFAM), Bytes.toBytes(col))) {
                    columnValueMap.put(col, new String(CellUtil.cloneValue(
                            result.getColumnLatestCell(Bytes.toBytes(DEFAULT_COLFAM), Bytes.toBytes(col)))));
                } else {
                    if (cols.contains(keyName)) {
                        columnValueMap.put(col, null);
                    }

                }
            }

        }
        resultScanner.close();
        table.close();

    }

}

person PraveenK    schedule 17.12.2015    source источник
comment
Важно отметить, что ваши подключения принадлежат другому хостинг-провайдеру, производительность которого непредсказуема. Запросы из центра обработки данных обычно отвечают в течение 4-6 мсек.   -  person Les Vogel - Google DevRel    schedule 18.12.2015


Ответы (2)


  • Первые несколько вызовов выполняются медленнее из-за известных проблем. Это некоторая настройка, которая происходит на стороне сервера для каждого «канала», и у нас есть несколько каналов.
  • Вам не нужен finalFilterList.
  • Вы должны кэшировать байты сканирования, имени таблицы и семейства столбцов. Вы можете использовать их повторно.
  • Если вы получаете одну строку, выполните Get вместо сканирования.
  • Вам нужен исполнитель?
  • Ваше сканирование, вероятно, должно использовать setMaxVersions (1) на всякий случай.
  • Может быть, попробуйте scan.setStartRow (Bytes.toBytes ("101")) и scan.setStopRow (Bytes.toBytes ("102")) вместо префикса строки, чтобы узнать, помогает ли это?
  • Убедитесь, что ваш код запускается в той же зоне, что и ваш кластер.

Надеюсь, это поможет.

person Solomon Duskis    schedule 17.12.2015
comment
@sduskis проблема с производительностью все еще существует, несмотря на некоторые из вышеперечисленных изменений. Это всего лишь тестовый код для моделирования проблемы. Я использую исполнителя, чтобы ограничить количество потоков во время соединения, без него соединение по умолчанию запускается с ~ 300 потоками (возможно, он внутренне использует newCachedThreadPool). Также я не могу кэшировать имя таблицы, потому что это входной параметр для моей службы. Услуга не предназначена для одного стола. - person PraveenK; 18.12.2015

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

Bigtable действительно оптимизирован для большого количества данных с частым доступом. Первый запрос через некоторое время может потребовать повторного считывания данных. Периодические запросы будут поддерживать его в рабочем состоянии.

person Les Vogel - Google DevRel    schedule 18.12.2015
comment
Когда я запускаю один и тот же фрагмент кода из виртуальных машин Google, время отклика намного быстрее - всего 3 мс для высокочастотных вызовов и всего 19 мс для низкочастотных вызовов ... и это здорово. - person PraveenK; 18.12.2015