Серия HBase (7) - подробное описание фильтров HBase

HBase

1. Введение в фильтры HBase

Hbase предоставляет множество фильтров для повышения эффективности обработки данных. Пользователи могут фильтровать данные с помощью встроенных или настраиваемых фильтров. Все фильтры действуют на стороне сервера, то есть predicate pushdown (предикат выталкивания вниз). . Это гарантирует, что отфильтрованные данные не будут доставлены клиенту, тем самым снижая нагрузку на передачу по сети и обработку клиента.

https://github.com/heibaiying

2. Основы фильтрации

2.1 Интерфейс фильтра и абстрактный класс FilterBase

Основные методы фильтров определены в интерфейсе Filter, а абстрактный класс FilterBase реализует интерфейс Filter. Все встроенные фильтры прямо или косвенно наследуются от абстрактного класса FilterBase. Пользователю нужно только передать определенный фильтр черезsetFilterметод переданScanилиputэкземпляр может быть.

setFilter(Filter filter)
 // Scan 中定义的 setFilter
 @Override
  public Scan setFilter(Filter filter) {
    super.setFilter(filter);
    return this;
  }
  // Get 中定义的 setFilter
 @Override
  public Get setFilter(Filter filter) {
    super.setFilter(filter);
    return this;
  }

Все фильтры подкласса FilterBase следующие:

https://github.com/heibaiying

Описание. Приведенный выше рисунок основан на последней версии Hbase-2.1.4 на текущий момент времени (2019.4), и все приведенные ниже инструкции основаны на этой версии.

2.2 Классификация фильтров

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

3. Сравните фильтры

Все фильтры сравнения наследуются отCompareFilter. Для создания фильтра сравнения требуются два параметра, а именнооператор сравненияа такжеЭкземпляр компаратора.

 public CompareFilter(final CompareOp compareOp,final ByteArrayComparable comparator) {
    this.compareOp = compareOp;
    this.comparator = comparator;
  }

3.1 Операторы сравнения

  • LESS (<)
  • LESS_OR_EQUAL (<=)
  • EQUAL (=)
  • NOT_EQUAL (!=)
  • GREATER_OR_EQUAL (>=)
  • GREATER (>)
  • NO_OP (исключить все подходящие значения)

Операторы сравнения определены в классе перечисленияCompareOperatorсередина

@InterfaceAudience.Public
public enum CompareOperator {
  LESS,
  LESS_OR_EQUAL,
  EQUAL,
  NOT_EQUAL,
  GREATER_OR_EQUAL,
  GREATER,
  NO_OP,
}

Примечание. В версии 1.x HBase операторы сравнения определены вCompareFilter.CompareOpВ классе enum, но начиная с версии 2.0 этот класс помечен как @deprecated и будет удален в версии 3.0. Поэтому версии HBase после 2.0 необходимо использоватьCompareOperatorЭтот перечислимый класс.

3.2 Компаратор

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

https://github.com/heibaiying

  • BinaryComparator: использоватьBytes.compareTo(byte [],byte [])Сравнивает указанные массивы байтов лексикографически.
  • BinaryPrefixComparator: Сравнивает лексикографически с указанным массивом байтов, но только до длины этого массива байтов.
  • RegexStringComparator: сравнивает с указанным массивом байтов, используя заданное регулярное выражение. поддерживается толькоEQUALа такжеNOT_EQUALработать.
  • SubStringComparator: Проверяет, встречается ли данная подстрока в указанном массиве байтов, сравнение не зависит от регистра. поддерживается толькоEQUALа такжеNOT_EQUALработать.
  • NullComparator: Определяет, является ли заданное значение пустым.
  • BitComparator: Побитовое сравнение.

BinaryPrefixComparatorа такжеBinaryComparatorРазница не совсем понятна, вот пример для иллюстрации:

в ходе выполненияEQUALПри сравнении, если переданный компараторabcd, но данные для сравненияabcdefgh:

  • При использованииBinaryPrefixComparatorкомпаратор, сравнить сabcdДлина массива байтов должна преобладать, т.е.efghв сравнении участвовать не буду, на данный момент думаю чтоabcdа такжеabcdefghудовлетворенEQUALусловный;
  • При использованииBinaryComparatorКомпаратор, он считается неравным.

3.3 Сравнение типов фильтров

Всего фильтров сравнения пять (версия Hbase 1.x и версия 2.x одинаковы), см. следующий рисунок:

https://github.com/heibaiying

  • RowFilter: фильтровать данные по ключам строк;
  • FamilyFilterr: фильтровать данные на основе семейства столбцов;
  • QualifierFilterr: фильтровать данные на основе квалификатора столбца (имя столбца);
  • ValueFilterr: фильтровать данные по значению ячейки;
  • DependentColumnFilter: укажите ссылочный столбец для фильтрации других столбцов.Принцип фильтрации заключается в фильтрации на основе временной метки ссылочного столбца.

Аналогично используются первые четыре фильтра, их можно построить, передав оператор сравнения и экземпляр оператора, а затем передатьsetFilterметод переданscan:

 Filter filter  = new RowFilter(CompareOperator.LESS_OR_EQUAL,
                                new BinaryComparator(Bytes.toBytes("xxx")));
  scan.setFilter(filter);    

DependentColumnFilterИспользование немного сложнее и объясняется отдельно здесь.

3.4 DependentColumnFilter

можно поставитьDependentColumnFilterпонимается какКомбинация valueFilter и фильтра отметки времени.DependentColumnFilterЕсть три конструктора с параметрами, здесь выбираем тот, у которого наиболее полные параметры для описания:

DependentColumnFilter(final byte [] family, final byte[] qualifier,
                               final boolean dropDependentColumn, final CompareOperator op,
                               final ByteArrayComparable valueComparator)
  • family: семейство столбцов
  • qualifier: квалификатор столбца (имя столбца)
  • dropDependentColumn: Определяет, включен ли ссылочный столбец в возвращаемый результат.Если значение равно true, это означает, что ссылочный столбец возвращается, а если значение false, это означает, что он отбрасывается.
  • op: оператор сравнения
  • valueComparator: Компараторы

Вот пример для иллюстрации:

DependentColumnFilter dependentColumnFilter = new DependentColumnFilter( 
    Bytes.toBytes("student"),
    Bytes.toBytes("name"),
    false,
    CompareOperator.EQUAL, 
    new BinaryPrefixComparator(Bytes.toBytes("xiaolan")));
  • первый, кто нашелstudent:nameмедиана сxiaolanВсе данные, начинающиеся с get参考数据集, этот шаг эквивалентен фильтру valueFilter;

  • Затем используйте временные метки всех данных в эталонном наборе данных для извлечения других столбцов и получения данных других столбцов с той же временной меткой, что и结果数据集, этот шаг эквивалентен фильтру отметок времени;

  • Наконец, еслиdropDependentColumnверно, возвращается参考数据集+结果数据集, если false, отбросить эталонный набор данных и просто вернуть结果数据集.

Четыре, специальный фильтр

Специализированные фильтры обычно наследуются непосредственно отFilterBase, для более узких правил фильтрации.

4.1 Фильтр значений одного столбца (SingleColumnValueFilter)

Определяет, фильтруется ли строка данных на основе значения столбца (ссылочного столбца). Его примеры имеют следующие методы:

  • setFilterIfMissing(boolean filterIfMissing): значение по умолчанию — false, то есть, если строка данных не содержит опорный столбец, он все равно включается в окончательный результат, если установлено значение true, он не включается;
  • setLatestVersionOnly(boolean latestVersionOnly): значение по умолчанию — true, то есть извлекаются только данные последней версии ссылочного столбца; если установлено значение false, извлекаются все данные версии.
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(
                "student".getBytes(), 
                "name".getBytes(), 
                CompareOperator.EQUAL, 
                new SubstringComparator("xiaolan"));
singleColumnValueFilter.setFilterIfMissing(true);
scan.setFilter(singleColumnValueFilter);

4.2 Фильтр SingleColumnValueExclude

SingleColumnValueExcludeFilterунаследовано от вышеперечисленногоSingleColumnValueFilter, поведение фильтрации противоположное.

4.3 Фильтр префикса ключа строки (PrefixFilter)

Определяет, фильтруется ли строка данных на основе значения RowKey.

PrefixFilter prefixFilter = new PrefixFilter(Bytes.toBytes("xxx"));
scan.setFilter(prefixFilter);

4.4 Фильтр префикса столбца (ColumnPrefixFilter)

Определяет, фильтруется ли строка данных на основе квалификатора столбца (имя столбца).

ColumnPrefixFilter columnPrefixFilter = new ColumnPrefixFilter(Bytes.toBytes("xxx"));
 scan.setFilter(columnPrefixFilter);

4.5 Фильтр страниц (PageFilter)

Вы можете использовать этот фильтр для разбиения результатов на страницы по строкам.При создании экземпляра PageFilter вам необходимо передать количество строк на странице.

public PageFilter(final long pageSize) {
    Preconditions.checkArgument(pageSize >= 0, "must be positive %s", pageSize);
    this.pageSize = pageSize;
  }

Следующий код отражает основную логику клиента для реализации пейджингового запроса, вот его объяснение:

Клиент выполняет пейджинговый запрос, который необходимо передатьstartRow(начать RowKey), знать началоstartRowПосле этого вы можете вернуть соответствующие данные строки pageSize. Единственная проблема здесь в том, что для первого запроса, очевидно,startRowЭто первая строка данных в таблице, но второй и третий запросы после нее нам неизвестны.startRow, вы можете знать только RowKey последних данных последнего запроса (просто называетсяlastRow).

мы не можемlastRowкак новый запросstartRowВходящий, потому что интервал запроса сканирования равен [startRow, endRow), то есть интервал открывается раньше и закрывается, поэтомуstartRowВ новом запросе также будет возвращено, эти данные повторяются.

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

Поскольку RowKey Hbase отсортирован лексикографически. В этом случае вы можетеlastRowдобавить после0, в видеstartRowВходящий, потому что по лексикографическим правилам значение плюс0Новое значение после него должно быть следующим значением этого значения в лексикографическом порядке, а для HBase следующий RowKey также должен быть больше или равен этому новому значению в лексикографическом порядке.

Так что, наконец, пройти вlastRow+0, если существует RowKey, равный этому значению, начать сканирование с этого значения, в противном случае начать сканирование со следующего RowKey в лексикографическом порядке.

25 буквенно-цифровых символов, лексикографически отсортированных следующим образом:

'0' < '1' < '2' < ... < '9' < 'a' < 'b' < ... < 'z'

Пейджинговый запрос в основном реализует логику:

byte[] POSTFIX = new byte[] { 0x00 };
Filter filter = new PageFilter(15);

int totalRows = 0;
byte[] lastRow = null;
while (true) {
    Scan scan = new Scan();
    scan.setFilter(filter);
    if (lastRow != null) {
        // 如果不是首行 则 lastRow + 0
        byte[] startRow = Bytes.add(lastRow, POSTFIX);
        System.out.println("start row: " +
                           Bytes.toStringBinary(startRow));
        scan.withStartRow(startRow);
    }
    ResultScanner scanner = table.getScanner(scan);
    int localRows = 0;
    Result result;
    while ((result = scanner.next()) != null) {
        System.out.println(localRows++ + ": " + result);
        totalRows++;
        lastRow = result.getRow();
    }
    scanner.close();
    //最后一页,查询结束  
    if (localRows == 0) break;
}
System.out.println("total rows: " + totalRows);

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

4.6 Фильтр временных меток

List<Long> list = new ArrayList<>();
list.add(1554975573000L);
TimestampsFilter timestampsFilter = new TimestampsFilter(list);
scan.setFilter(timestampsFilter);

4.7 ФирстКейОнлиФильтр

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

FirstKeyOnlyFilter firstKeyOnlyFilter = new FirstKeyOnlyFilter();
scan.set(firstKeyOnlyFilter);

5. Упаковочный фильтр

Обертывание фильтров — это реализация некоторых расширенных функций путем обертывания других фильтров.

5.1 Фильтр SkipFilter

SkipFilterОберните фильтр, когда обернутый фильтр встречает экземпляр KeyValue, который необходимо отфильтровать, он расширяется, чтобы отфильтровать всю строку данных. Вот пример использования:

// 定义 ValueFilter 过滤器
Filter filter1 = new ValueFilter(CompareOperator.NOT_EQUAL,
      new BinaryComparator(Bytes.toBytes("xxx")));
// 使用 SkipFilter 进行包装
Filter filter2 = new SkipFilter(filter1);

5.2 Фильтр WhileMatchFilter

WhileMatchFilterОбернуть фильтр, когда обернутый фильтр встречает экземпляр KeyValue, который необходимо отфильтровать,WhileMatchFilterЗатем текущее сканирование завершается, и возвращаются результаты сканирования. Вот пример его использования:

Filter filter1 = new RowFilter(CompareOperator.NOT_EQUAL,
                               new BinaryComparator(Bytes.toBytes("rowKey4")));

Scan scan = new Scan();
scan.setFilter(filter1);
ResultScanner scanner1 = table.getScanner(scan);
for (Result result : scanner1) {
    for (Cell cell : result.listCells()) {
        System.out.println(cell);
    }
}
scanner1.close();

System.out.println("--------------------");

// 使用 WhileMatchFilter 进行包装
Filter filter2 = new WhileMatchFilter(filter1);

scan.setFilter(filter2);
ResultScanner scanner2 = table.getScanner(scan);
for (Result result : scanner1) {
    for (Cell cell : result.listCells()) {
        System.out.println(cell);
    }
}
scanner2.close();
rowKey0/student:name/1555035006994/Put/vlen=8/seqid=0
rowKey1/student:name/1555035007019/Put/vlen=8/seqid=0
rowKey2/student:name/1555035007025/Put/vlen=8/seqid=0
rowKey3/student:name/1555035007037/Put/vlen=8/seqid=0
rowKey5/student:name/1555035007051/Put/vlen=8/seqid=0
rowKey6/student:name/1555035007057/Put/vlen=8/seqid=0
rowKey7/student:name/1555035007062/Put/vlen=8/seqid=0
rowKey8/student:name/1555035007068/Put/vlen=8/seqid=0
rowKey9/student:name/1555035007073/Put/vlen=8/seqid=0
--------------------
rowKey0/student:name/1555035006994/Put/vlen=8/seqid=0
rowKey1/student:name/1555035007019/Put/vlen=8/seqid=0
rowKey2/student:name/1555035007025/Put/vlen=8/seqid=0
rowKey3/student:name/1555035007037/Put/vlen=8/seqid=0

Видно, что после упаковки вернули толькоrowKey4предыдущие данные.

Шесть, список фильтров

Вышеприведенное объясняет функцию одного фильтра. Когда несколько фильтров должны работать вместе над запросом, вам нужно использоватьFilterList.FilterListподдержку через конструктор илиaddFilterМетод проходит через несколько фильтров.

// 构造器传入
public FilterList(final Operator operator, final List<Filter> filters)
public FilterList(final List<Filter> filters)
public FilterList(final Filter... filters)

// 方法传入
 public void addFilter(List<Filter> filters)
 public void addFilter(Filter filter)

Результат объединения нескольких фильтров определяется выражениемoperatorопределение параметра, дополнительные параметры которого определены вOperatorв классе перечисления. ТолькоMUST_PASS_ALLа такжеMUST_PASS_ONEДва необязательных значения:

  • MUST_PASS_ALL: Эквивалентно И, все фильтры должны пройти за проходом;
  • MUST_PASS_ONE: Эквивалент ИЛИ, считается пройденным, только если проходит один фильтр.
@InterfaceAudience.Public
  public enum Operator {
    /** !AND */
    MUST_PASS_ALL,
    /** !OR */
    MUST_PASS_ONE
  }

Пример использования следующий:

List<Filter> filters = new ArrayList<Filter>();

Filter filter1 = new RowFilter(CompareOperator.GREATER_OR_EQUAL,
                               new BinaryComparator(Bytes.toBytes("XXX")));
filters.add(filter1);

Filter filter2 = new RowFilter(CompareOperator.LESS_OR_EQUAL,
                               new BinaryComparator(Bytes.toBytes("YYY")));
filters.add(filter2);

Filter filter3 = new QualifierFilter(CompareOperator.EQUAL,
                                     new RegexStringComparator("ZZZ"));
filters.add(filter3);

FilterList filterList = new FilterList(filters);

Scan scan = new Scan();
scan.setFilter(filterList);

использованная литература

HBase: The Definitive Guide _> Chapter 4. Client API: Advanced Features

Дополнительные статьи серии о больших данных см. в проекте с открытым исходным кодом GitHub.:Руководство для начинающих по большим данным