1. Введение в фильтры HBase
Hbase предоставляет множество фильтров для повышения эффективности обработки данных. Пользователи могут фильтровать данные с помощью встроенных или настраиваемых фильтров. Все фильтры действуют на стороне сервера, то есть predicate pushdown (предикат выталкивания вниз). . Это гарантирует, что отфильтрованные данные не будут доставлены клиенту, тем самым снижая нагрузку на передачу по сети и обработку клиента.
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 следующие:
Описание. Приведенный выше рисунок основан на последней версии 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
Абстрактные классы обычно используются в следующих категориях:
-
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 одинаковы), см. следующий рисунок:
- 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.:Руководство для начинающих по большим данным