Building columnar compression in a row-oriented database
blog.timescale.com
blog.timescale.com
http://db.csail.mit.edu/pubs/abadi-column-stores.pdf
The key takeaway is that columnar compression only accounts for a small minority of the speed up that you get for scan-oriented workloads; the real big win comes when you implement a block-oriented query processor and pipelined execution. Of course you can’t do this by building inside the Postgres codebase, which is why every good column store is built more or less from scratch.
Anyone considering a “time series database” should first set up a modern commercial column store, partition their tables on the time column, and time their workload. For any scan-oriented workload, it will crush a row store like Timescale.
Or you can set up a clickhouse instance. It's a seriously promising and underrated product.
There is also PostgreSQL Foreign Data Wrapper for ClickHouse which allows you to run all SQL PostgreSQL support and often with great performance
It will not be as fast as a tailored engine? Sure. But the advantage of stay inside of PG is very great (not only ecosystem, but have sql, OLTP + OLAP, etc).
Is like how a document store could be better than add JSON with PG...
In practique? I think this hybrid approach is very good.
P.D: And what about the opposite? Which columnar store can I use for OLTP?
We have TimescaleDB in production with hundreds of millions of time series data sat alongside "regular" data - having to deploy a single database instead of Postgres and a specialised time series database is a real boon.
For us, TimescaleDB queries run way faster than stock Postgres. They might well run faster in something specialised, but querying/aggregating hundreds of millions of rows in 10s or 100s of milliseconds is plenty fast enough for many use cases.
There are plenty of scenarios that benefit from having automatic time-based partitioning and querying within the same operational PG datastore instead of running a separate analytical system. The improved performance only helps this case.
But our users, and the users of most so-called "time series databases", do not typically generate the classic scan-oriented workloads of OLAP systems that motivated C-Store and classic columnar data warehouses.
I talk about this in the post: sure, there are deep and narrow queries, but there are also a /lot/ of shallow and wide queries. Most real-time dashboarding, which is a huge use-case for IT/devops monitoring and IoT sensor monitoring, is about "what happened very recently".
But you do occasionally want to look back, so you want to keep old data sitting around, just in a cost-effective manner. And there is a lot of data: time-series and monitoring applications are typically write (not read) heavy, and ingest high volumes of data continuously.
Enter compression and data tiering, both of which appeared in today's v1.5 release.
As such, the primary motivation behind our work on compression was actually saving storage overhead, not improving query performance. Although we'll never pass up on performance improvements when we get them! =)
(And we are familiar with the citation -- I've known Daniel and Sam for many years, and one of the engineers who led this work at Timescale was actually Daniel's former PhD advisee Josh Lockerman.)
select * from time_series where ingest_time > ?
You will only scan the most recent block, and it will be extremely fast. I have no doubt that there are individual queries where a time-series database will “win”, but I earnestly question whether there’s a real-world use case, with a mix of queries, where a special purpose time-series db is preferable to a good column store with time-based partitioning.
Do you have a Patreon, PayPal account, or any other means to receive money as a donation, gift or token of appreciation?
And yes as mfreed already mentioned we love hearing user stories :)
[1] https://pdfs.semanticscholar.org/0cb4/d21ca5198819a85adae8ea...
* What is a plan for PG12 PLUGGABLE STORAGE[1]? this is for PG12?
* Can you compare with Zedstore[2]?
One of the interesting things of our technique is that it doesn't require low-level changes to Postgres, and actually then works with any version of PG that TimescaleDB supports (currently PG10, 11...PG12 coming soon).
That said, we're excited by the work Postgres has been doing with pluggable storage, particularly how PG13 will further open up possibilities such as Zedstore, and look to see how we can then marry some of these ideas. In terms of feature-by-feature comparison, haven't yet dug enough into the details.
Aside, one interesting aspects of our approach is discussed in the article: Having a "hybrid" row/column, rather than purely columnar, can actually be beneficial for many time-series workloads that constantly query very recent data (e.g., for dashboarding) as well as to improve ingest rates (although some column stores do build a temporary in-memory row-based cache before batch writing a column).
If you are interested in primarily showing the recent data -- which you see in many monitoring examples in IT/devops or IoT -- you often want the raw data or an aggregate per time period.
As an aside, TimescaleDB introduced continuous aggregations in v1.3. Here's a nice example of using it with Grafana for dashboarding: https://blog.timescale.com/blog/how-to-quickly-build-dashboa...
With TimescaleDB, would I use the recorded_at or inserted_at column for the hypertable?
Does this change if data for an individual sensor can sometimes arrive out of order? If the sensor malfunctions and the data contains timestamps in the far past or the far future does this cause issues with TimescaleDB?
What we've done in postgres so far is have the tables with data generally structured around the recorded_at column because most analysis wants to look at the data "in order" . to generate reports, graphs, etc. Each data row also contains a "payload_id" relating it to a "payloads" table which helps group data by when it actually hit the system. Data processing has generally been built around the payloads and then query any additional data in recorded_at order on the main data tables if we need to look back or forward in time.
Out of order data should be handled fine by TimescaleDB -- if you do have data that is far in the future or in the past, you may get stray chunks to hold those, but it's not going to create all the intermediate chunks or anything that might be undesirable. You can later correct those fields by deleting and reinserting the record with a corrected timestamp.
I've looked into timescaledb for this, and it doesn't support them.
However, the intersection of bitemporal indexes and columnar time-series queries seems important and yet I haven't seen anything that looks like it might offer both, possibly asides from kdb+ and SAP HANA.
Disclosure: I work on https://github.com/juxt/crux (which is optimised for bitemporal graph joins and doesn't currently employ columnar indexes)
Question: how does the new compression compare to TokuDB, and is it tuneable for performance/size tradeoff?
https://www.percona.com/doc/percona-server/5.7/tokudb/using_...
[1] https://www.oracle.com/technetwork/database/exadata/ehcc-twp...
Quick scan of the Oracle paper couldn't find specifics, other than something like this:
"Warehouse Compression provides two levels of compression: LOW and HIGH. Warehouse Compression HIGH typically provides a 10x reduction in storage, while Warehouse Compression LOW typically provides a 6x reduction"
That would at least suggest that they aren't doing anything type-specific like we are.
This also leads to significant query performance settings if you common filter by device_id, for example. Which are super common in time-series workloads for IT monitoring / devops / IOT / etc.
How well could be apply the same ideas for in-memory processing?
If I understand correctly, you have something alike:
- Store each column on a array of N=1000 - Store the group of columns in pages, with metadata of ranges of keys to locate rows in the adequate page
One question: why do you use Gorilla compression for floating-point values? It works well for integer values, but is pretty useless for floating-point values [3].
[1] https://github.com/VictoriaMetrics/VictoriaMetrics
[2] https://medium.com/@valyala/measuring-vertical-scalability-f...
[3] https://medium.com/faun/victoriametrics-achieving-better-com...
Tricks such as renormalizing floats in base-10 (which I believe VictoriaMetrics implements) while great when they work, potentially truncates lower-order digits.
Lossiness is not a decision we're willing to make for our users unilaterally. And, as Gorilla is one the default compression algorithms we employ, it needs to be one that is correct in all cases.
Incidentally, we experimented with the binary equivalent of the renormalization algorithm, and it performed on-par with Gorilla in our tests.
Any plan to bring back something like continuous views in timescaledb? (or to integrate pipelinedb work)
Tutorial: https://docs.timescale.com/latest/using-timescaledb/continuo...
Example with Grafana dashboarding: https://blog.timescale.com/blog/how-to-quickly-build-dashboa...
Technical details of correctly handling backfilled data: https://blog.timescale.com/blog/continuous-aggregates-faster...
- Floats are hard to compress without quantization or lossy compression. The Gorilla algorithm is not helping so much.
You can do your own benchmarks with your data with TurboPFor : https://github.com/powturbo/TurboPFor
or download icapp including more powerfull algorithms.
And also this approach allows us to leverage alot of the battle-tested stability built into current storage layout with TOASTed pages, without starting from scratch.