Expand description
Columns encoded together instead of one at a time.
spec/06-compression.md section 6.4 calls this multi-column compression and says it is the
mechanism that has no equivalent in DuckDB. The idea is that columns are not independent, so
encoding them independently throws away real redundancy. On ClickBench hits the obvious case
is URL and Referer, which are both URLs drawn from the same universe, and the less obvious
one is that URL, Referer, Title and the referer derived columns are all the same alphabet
and could share one symbol table.
§The three strategies
INDEPENDENT is each column encoded on its own by crate::string. It is the baseline the
other two have to beat, and it is what a group falls back to when they do not.
SHARED_TABLE is one FSST symbol table trained on a sample of all the columns, with every
column compressed against it. Storing one 255 symbol table rather than six saves almost nothing
by itself. The effect that matters is that a table trained on the union has more evidence per
symbol, so it compresses each column better than a table trained on that column alone would,
and the columns that gain most are the small ones that never had enough bytes to train on.
SHARED_DICT is one dictionary holding the union of the values, with every column becoming an
array of codes into it. A value that appears in three columns is stored once rather than three
times. The dictionary is itself a string column, so it goes back through the string chooser and
comes out FSST compressed, which means a shared dictionary is also a shared symbol table.
§What decides
Here, measuring all three and keeping the smallest, for the same reason crate::string does
it that way: M1 is measuring what the format can do rather than how fast a writer can decide.
A write path cannot afford this. Section 6.4 says detection is by sampling pairs over a global
sample at table level, recorded in the catalog as hints, with each row group checking only the
hinted pairs. dictionary_groups is that pruning step, and it runs on sketches rather than on
data, so the 5,460 pairs of a 105 column table cost a Jaccard estimate each rather than a pass
over the column.
§What is not here
Correlation encodings, which are the third form in section 6.4: column B stored as a function of
column A, either as a per dictionary entry lookup for a functional dependency or as B minus f(A)
for a numeric one. crate::sketch::dependence is the detection half of that and the encoding
half is its own piece of work.
Global dictionaries, which are section 6.5 and are a dictionary across the whole table rather than across a group of columns in one chunk. The two compose, and the reason they are separate is that a global dictionary needs an incremental builder that can spill, which is open question five and is the thing most likely to make the idea impractical.
Enums§
- Strategy
- How a column group is encoded. The discriminant is the tag byte and is part of the format.
Functions§
- decode_
group - Decodes a group written by
encode_group. - describe
- The shape a group was encoded as, as a line of text.
- dictionary_
groups - Which columns should be considered for a shared dictionary, from one sketch per column.
- encode_
group - Encodes a group of string columns together, choosing whatever comes out smallest.
- strategy_
sizes - The size of every strategy that applies, which is the measurement section 6.4 is asking for.