Why it matters
Filter design is the difference between efficient and inefficient HBase queries. A query that fetches a hundred rows out of a billion should transfer only those hundred over the network. Without filters, it transfers all billion. That is a 10-million-times difference in network cost.
Filters also reduce client memory pressure. Instead of holding a huge scan result in client memory to filter, only matching rows arrive. This lets clients run on smaller boxes.
The architecture
Filters are Java classes that implement the Filter interface. Each filter has hook methods that the RegionServer calls at various points during scan: filterRowKey (decide whether to skip the row before reading any cells), filterCell (decide whether to skip a cell), filterAllRemaining (decide whether to abort the scan).
Common built-in filters: RowFilter matches row keys against a comparison operator; PrefixFilter is a fast-path for row-key prefix matching; SingleColumnValueFilter matches based on the value of one specific column; PageFilter limits the number of rows returned; FilterList combines multiple filters with AND or OR semantics.
How it works end to end
Server-side execution flow: for each row the scanner considers, filterRowKey is called first with just the row key. If it returns SKIP, no cells for that row are read. Otherwise, each cell is passed to filterCell, which can include, skip, or terminate.
FilterList combines filters. MUST_PASS_ALL means every child filter must accept the row (AND semantics). MUST_PASS_ONE means any child accepting is sufficient (OR). Complex logic can be built with nested FilterLists.
Filter cost is real. Poorly-written custom filters that touch every cell in a scan can consume more CPU than the client would have. Prefer built-in filters and simple compositions over custom code.