Dictionary compression
View as MarkdownStarting in v26.38, dictionary compression will be available (as public preview) for managed clusters. Dictionary compression reduces the memory that arrangements use when a column holds the same values repeatedly. Instead of storing a repeated column value each time it appears, Materialize stores that value once and has each row reference it.
Dictionary compression is off by default. You opt in per cluster with the
EXPERIMENTAL ARRANGEMENT COMPRESSION option.
Within a column, the values that repeat most often are the ones Materialize stores once and references. Everything else is stored as-is, exactly as it would be without compression. Materialize applies compression per column and decides which columns to compress, so a wide row can have one column compressed and the rest untouched.
Enable dictionary compression
Set the EXPERIMENTAL ARRANGEMENT COMPRESSION option on each managed cluster
whose arrangements you want compressed.
The option is configured on the cluster, but the arrangements it applies to live in the cluster’s replicas, in each replica’s memory. A replica picks up the configured value when it is created and holds it for its lifetime.
A replica’s compression setting is fixed when the replica is created, so
changing EXPERIMENTAL ARRANGEMENT COMPRESSION never changes an existing
replica’s arrangements. Materialize instead creates a new set of replicas
carrying the new setting. The existing replicas keep serving until the new ones
have hydrated, then Materialize retires them. As a result, the cluster
temporarily uses roughly twice its usual memory until the switch completes,
regardless of whether you enable or disable dictionary compression.
Nothing else is needed to apply the setting. Plan for the switch the same way you would plan for resizing a cluster. Because hydration is slower with compression enabled, the switch takes longer when turning compression on than when turning it off.
Set the option on a cluster
You can set the option when you create a cluster:
CREATE CLUSTER my_cluster (
SIZE = '100cc',
EXPERIMENTAL ARRANGEMENT COMPRESSION = true
);
Or change it on an existing cluster:
ALTER CLUSTER my_cluster SET (EXPERIMENTAL ARRANGEMENT COMPRESSION = true);
To go back to the default:
ALTER CLUSTER my_cluster RESET (EXPERIMENTAL ARRANGEMENT COMPRESSION);
SHOW CREATE CLUSTER reports the configured value.
The tradeoff
Dictionary compression trades CPU for memory, and it does not reduce memory on every workload. Use this section to judge whether it suits your workload, and contact our team if you have questions.
When it helps
- Columns that hold a small set of often-repeated, longer values. Typical examples are status strings, enum-like labels, country codes, and tenant IDs. Most or all of the memory savings come from columns like these. See How distinct values affect the benefit for how the benefit changes as that set grows.
- Large arrangements. Compression saves more memory when repeated values occur across more rows, so larger arrangements generally offer more opportunity for savings.
Only data held in an arrangement is affected. That means indexes and the
arrangements that a dataflow builds internally for joins and aggregations
(GROUP BY, DISTINCT). Data that is not arranged is untouched. A materialized
view’s stored result is not an arrangement, but the dataflow that maintains it
builds these internal arrangements for any joins and aggregations it computes.
When it does not help
- High-cardinality or near-unique columns. Unique values are not worth storing in a dictionary, so columns dominated by them see little or no memory savings. Materialize does not detect a high-cardinality column and skip it up front. It inspects every column of every row regardless, so a near-unique column pays the full CPU cost for little or no memory benefit. Unique identifiers, timestamps, and free-form text are typical examples.
- Columns of short values. Booleans,
NULLs, and small integers are already stored compactly enough that a dictionary reference cannot beat storing the value itself. They are never compressed. - Small arrangements. An arrangement built from scratch does not install a dictionary until it has seen on the order of 65,000 rows. Small arrangements are effectively unaffected.
The CPU cost
The cost falls mainly on the write path. As updates arrive, Materialize keeps approximate counts of which values repeat most in each column and maintains the dictionary. The most visible symptom is slower arrangement hydration. A replica in a cluster with compression enabled takes longer to build its arrangements after it is created, restarted, or resized.
Reads pay a smaller but ongoing cost. Resolving a compressed value requires an extra indirection, and comparing compressed rows cannot use the fast path that uncompressed rows use.
How distinct values affect the benefit
The memory benefit is largest when a column repeats a small set of values across many rows. It tapers off as the number of distinct values in a column grows past roughly 64, and with enough distinct values the bookkeeping becomes overhead that buys no memory saving at all.
This is not a limit. Nothing breaks when a column holds more distinct values than that. Values that are not worth storing in a dictionary, including values that appear only once, are stored as-is. The result is less memory saved, and nothing else changes.
Observe the effect
There is no metric or system catalog relation specific to dictionary compression. The effect shows up in the existing arrangement-size introspection:
SELECT name, records, size
FROM mz_introspection.mz_dataflow_arrangement_sizes
ORDER BY size DESC;
See mz_introspection.mz_arrangement_sizes for per-operator numbers and
mz_introspection.mz_dataflow_arrangement_sizes for per-dataflow numbers. The
reported size includes the dictionary’s own overhead, so on a workload that does
not compress well you may see the cost of the machinery rather than a saving.
To judge whether compression helped, compare arrangement sizes for the same objects with the option on and off. For example, run the same workload on a second cluster with the option set differently. Because changing the option re-hydrates the cluster, wait until hydration has completed before you measure.