Compressing data
is built on which is, by nature, a row-based database. Because time-series data is accessed in order of time, when you enable compression, converts many wide rows of data into a single row of data, called an array form. This means that each field of that new, wide row stores an ordered set of data comprising the entire column. For example, if you had a table with data that looked a bit like this:
You can convert this to a single row in array form, like this:
Even before you compress any data, this format immediately saves storage by reducing the per-row overhead.
typically adds a small number of bytes of overhead per row. So even without any compression, the schema in this example
is now smaller on disk than the previous format.
This format arranges the data so that similar data, such as timestamps, device IDs, or temperature readings, is stored
contiguously. This means that you can then use type-specific compression algorithms to compress the data further, and
each array is separately compressed. For more information about the compression methods used, see the compression
methods section.
When the data is in array format, you can perform queries that require a subset of the columns very quickly. For
example, if you have a query like this one, that asks for the average temperature over the past day:
Querying compressed data
In the previous example, the database has no way of knowing which rows need to be fetched and decompressed to resolve a query. For example, the database can’t easily determine which rows contain data from the past day, as the timestamp itself is in a compressed column. You don’t want to have to decompress all the data in a chunk, or even an entire , to determine which rows are required. automatically includes more information in the row and includes additional groupings to improve query performance. When you compress a , either manually or through a compression policy, it can help to specify anORDER BY column.
ORDER BY columns specify how the rows that are part of a compressed batch are ordered. For most time-series workloads,
this is by timestamp, so if you don’t specify an ORDER BY column, defaults to using the time column. You
can also specify additional dimensions, such as location.
For each ORDER BY column, automatically creates additional columns that store the minimum and maximum
value of that column. This way, the query planner can look at the range of timestamps in the compressed column, without
having to do any decompression, and determine whether the row could possibly match the query.
When you compress your , you can also choose to specify a SEGMENT BY column. This allows you to segment
compressed rows by a specific column, so that each compressed row corresponds to a data about a single item such as, for
example, a specific device ID. This further allows the query planner to determine if the row could possibly match the
query without having to decompress the column first. For example:
With the data segmented in this way, a query for device A between a time interval becomes quite fast. The query planner
can use an index to find those rows for device A that contain at least some timestamps corresponding to the specified
interval, and even a sequential scan is quite fast since evaluating device IDs or timestamps does not require
decompression. This means the query executor only decompresses the timestamp and temperature columns corresponding to
those selected rows.