Benchmarking TimescaleDB vs. InfluxDB For Time-Series Data
Benchmarking TimescaleDB vs. InfluxDB For Time-Series Data
Data model 3
Relational data model
Tagset data model
Data model summary
Query language 6
Query language summary
Reliability 7
Reliability summary
Performance 9
Insert performance
Insert performance summary
Read latency
Read latency performance summary
Stability issues during benchmarking
Ecosystem 16
Ecosystem summary
Operational management 17
High availability
Resource consumption
General tooling
Next steps 19
Overview
Time-series data is emerging in more and more applications, including IoT, DevOps, Finance, Retail,
Logistics, Oil and Gas, Manufacturing, Automotive, Aerospace, SaaS, even machine learning and AI. If
you are investing in a time-series database, that likely means you already have a meaningful amount
of time-series data piling up quickly and need a place to store and analyze it. You may also recognize
that the survival of your business will depend on the database you choose.
In this paper, we compare two leading time-series databases, TimescaleDB and InfluxDB, to
help developers choose the time-series database best suited for their needs. Typically database
comparisons focus on performance benchmarks. Yet performance is just a part of the overall picture;
it doesn’t matter how good benchmarks are if a database can’t be used in production because it has
an incompatible data model or query language, or if it lacks reliability. With that in mind, we begin
by comparing TimescaleDB and InfluxDB across three qualitative dimensions including data model,
query language, and reliability, before diving deeper with performance benchmarks. We then round
out with a comparison across database ecosystem, and operational management.
Data model
Relational data model
TimescaleDB is a relational database, while InfluxDB is more of a custom, NoSQL, non-relational
database. This means TimescaleDB relies on the relational data model, commonly found in
PostgreSQL, MySQL, SQL Server, Oracle, etc. On the other hand, InfluxDB has developed its own
custom data model, which, for the purpose of this comparison, is called the tagset data model.
With the relational model in TimescaleDB, each time-series measurement is recorded in its own row,
with a time field followed by any number of other fields, which can be floats, ints, strings, booleans,
arrays, JSON blobs, geospatial dimensions, date/time/timestamps, currencies, binary data, or even
more complex data types. One can create indexes on any one field (standard indexes) or multiple
fields (composite indexes), or on expressions like functions, or even limit an index to a subset of rows
(partial index). Any of these fields can be used as a foreign key to secondary tables, which can then
store additional metadata.
• Uses a flexible, narrow or wide table depending on how much data/metadata to record per reading
• Has many indexes to speed up queries or few indexes to reduce disk usage
• Denormalized metadata within the measurement row, or normalized metadata that lives in a
separate table, can be updated at any time
• A rigid schema validates input types or a schemaless JSON blob to increase iteration speed
• Check constraints validate inputs, for example, checking for uniqueness or non-null values
Disadvantages
• To get started, one needs to generally choose a schema and explicitly decide whether or not
have indexes
Advantages
• If data naturally fits the tagset model, then it is easy to get started since as you don’t have to
worry about creating schemas or indexes
Disadvantages
• The model is quite rigid and limited, with no ability to create additional indexes, indexes on
continuous fields (e.g., numerics), update metadata after the fact, enforce data validation, etc.
• The model may feel “schemaless”, but there is actually an underlying schema that is auto-
created from the input data, which may differ from the desired schema
At a high-level, here’s how the two language syntaxes compare, using the computation of an
exponential moving average as an example:
Reliability
At its start, InfluxDB sought to completely write an entire database in Go. With its 0.9 release,
they completely rewrote the backend storage engine (the earlier versions of Influx were going in
the direction of a pluggable backend with LevelDB, RocksDB, or others). There are benefits from
this approach, yet these design decisions have significant implications that affect reliability. First,
InfluxDB has to implement the full suite of fault-tolerance mechanisms, including replication, high
availability, and backup/restore. Second, InfluxDB is responsible for its on-disk reliability, e.g., to
make sure all its data structures are both durable and resist data corruption across failures (and even
failures during the recovery of failures).
Due to its architectural decisions, TimescaleDB instead relies on the 25+ years of hard, careful
engineering work that the entire PostgreSQL community has done to build a rock-solid database that
can support truly mission-critical applications.
Additionally, given TimescaleDB’s design, it’s able to leverage the full complement of tools that the
PostgreSQL ecosystem offers and and all of these are available in open-source: streaming replication
for high availability and read-only replicas, pg_dump and pg_recovery for full database snapshots,
pg_basebackup and log shipping / streaming for incremental backups and arbitrary point-in-time
recovery, WAL-E for continuous archiving to cloud storage, and robust COPY FROM and COPY TO tools
for quickly importing/exporting data with a variety of formats.
InfluxDB, on the hand, had to build all these tools from scratch and it doesn’t offer many of these
capabilities. It initially offered replication and high availability in its open source, but subsequently
pulled this capability out of open source and into its enterprise product. Its backup tools have the
ability to perform a full snapshot and recover to this point-in-time, and only recently added some
support for a manual form of incremental backups. Its ability to easily and safely export large
volumes of data is also quite limited.
TimescaleDB does not change the lowest levels of PostgreSQL storage, nor interfere with the proper
function of its write-ahead log. (The WAL ensures that as soon a write is accepted, it gets written to
an on-disk log to ensure safety and durability, even before the data is written to its final location and
all its indexes are safely updated.) These data structures are critical for ensuring consistency and
atomicity; they prevent data from becoming lost or corrupted, and ensure safe recovery. Alternatively,
InfluxDB had to design and implement all this functionality itself from scratch. This is a notoriously
hard problem in databases that typically takes many years or even decades to get correct.
Reliability summary
These challenges and problems are not unique to InfluxDB, and every developer of a reliable, stateful
service must grapple with them. Every database goes through a period when it sometimes loses data
because it’s really hard to get all the corner cases right. PostgreSQL went through this period in the
1990s, while InfluxDB still needs to figure out the kinks and has many years of dedicated engineering
effort in store to catch up.
Insert performance
For insert performance, we used the following setup for each database:
Note: These metrics are measured in terms of rows per second (in the case of InfluxDB, defined as
a collection of metrics recorded at the same time). If you are collecting multiple metrics per row,
then the total number of metrics per second can be much higher. For example, in our [4,000 devices
x 10 metrics] test, you would multiply [rows per second] by [10], resulting in 1.44M metrics/sec for
TimescaleDB and 0.56M metrics/sec for InfluxDB.
Read latency
For benchmarking read latency, we used the following setup for each database:
(The read latency setup varies from the previous one focused on inserts because this was conducted
earlier in time.)
The results are below, using the same workloads we used for inserts. Latencies in this chart are all shown
as milliseconds, with an additional column showing the relative performance of TimescaleDB compared
to InfluxDB (highlighted in orange when TimescaleDB is faster, in blue when InfluxDB is faster).
Starting at 100K cardinality, we also experienced problems with some of our read queries on
InfluxDB. Our InfluxDB HTTP connection would error out with a cryptic ‘End of File’ message. When
we investigated the InfluxDB server we found out that InfluxDB had consumed all available memory
to run the query and subsequently crashed with an Out of Memory error. Since PostgreSQL helpfully
allows us to limit system memory usage with settings like shared_buffers and work_mem, this
generally was not an issue for TimescaleDB even at higher cardinalities.
The InfluxDB TSI is a home-grown log-structured merge tree based system comprised of various data
structures, including hashmaps and bitsets. This includes: an in-memory log (“LogFile”) that gets
periodically flushed to disk when it exceeds a threshold (5MB) and compacted to an on-disk memory-
mapped index (“IndexFile”); a file (“SeriesFile”) that contains a set of all series keys across the entire
database. (Described here in their documentation.)
The performance of the TSI is limited by the complex interactions of all of these data structures.
The design decisions behind the TSI also leads to several other limitations with performance
implications:
• Their total cardinality limit, according to the InfluxDB documentation, is around 30 million
(although based on the graph above, InfluxDB starts to perform poorly well before that), or far
below what is often required in time-series use cases like IoT.
• InfluxDB indexes tags but not fields, which means that queries that filter on fields can not
perform better than full scans. For example, if one wanted to search for all rows where there was
no free memory (e.g, something like, SELECT * FROM sensor_data WHERE mem_free = 0), one
could not do better than a full linear scan (i.e., O(n) time) to identify the relevant data points.
• The set of columns included in the index is completely fixed and immutable. Changing what
columns in your data are indexed (tagged) and what things are not requires a full rewrite of your
data.
• InfluxDB is only able to index discrete, and not continuous, values due to its reliance on
hashmaps. For example, to search all rows where temperature was greater than 90 degrees (e.g.,
something like SELECT * FROM sensor_data WHERE temperature > 90), one would again have to
fully scan the entire dataset.
• Your cardinality on InfluxDB is affected by your cardinality across all time, even if some fields/
values are no longer present in your dataset. This is because the SeriesFile stores all series keys
across the entire dataset.
First, TimescaleDB partitions your data by time, with one B-tree mapping time-segments to the
appropriate partition (“chunk”). All of this partitioning happens behind the scenes and is hidden
from the user, who is able to access a virtual table (“hypertable”) that spans all of their data across all
partitions.
Next, TimescaleDB allows for the creation of multiple indexes across your dataset (e.g., for
equipment_id, sensor_id, firmware_version, site_id). These indexes are then created on every chunk,
by default in the form of a B-tree. (One can also create indexes using any of the built-in PostgreSQL
index types: Hash, GiST, SP-GiST, GIN, and BRIN.)
• The simpler approach leads to a clearer understanding of how the database performs. As long
as the indexes and data for the dataset we want to query fit inside memory, which is something
that can be tuned, cardinality becomes a non-issue.
• In addition, since the secondary indexes are scoped at the chunk level, the indexes themselves
only get as large as the cardinality of the dataset for that range of time.
• You have control over which columns to index, including the ability to create compound indexes
over multiple columns. You can also add or delete indexes anytime you want, for example if your
query workloads change. Unlike in InfluxDB, changing your indexing structure in TimescaleDB
does not require you to rewrite the entire history of your data.
• You can create indexes on discrete and continuous fields, particularly because B-trees work
well for a comparison using any of the following operators: <, <=, =, >=, >, BETWEEN, IN,
IS NULL, IS NOT NULL. Our example queries from above ( SELECT * FROM sensor_data
WHERE mem_free = 0 and SELECT * FROM sensor_data WHERE temperature > 90) will run in
logarithmic, or O(log n), time.
• The other supported index types can come in handy in other scenarios, e.g., GIST indexes for
“nearest neighbor” searches.
Operational management
Even if a database satisfies all the above needs, it still needs to work, and someone needs to operate
it. Operational management requirements typically boil down to these categories: high availability,
resource consumption (memory, disk, cpu), and general tooling.
High availability
No matter how reliable the database, at some point your node will go down: hardware errors, disk
errors, or some other unrecoverable issue. At that point, you will want to ensure you have a standby
available for failover with no loss of data.
TimescaleDB supports high availability via PostgreSQL streaming replication. At one point, open
source InfluxDB offered high availability via InfluxDB-relay, but it appears to be a dormant project
(last update November 2016). Today InfluxDB HA is only offered by their enterprise version.
Resource consumption
For memory utilization, cardinality again plays a large role. Below are some graphs using the same
workloads as before for measuring insert performance.
• 100 devices x 1 metric x 30 days: InfluxDB (12MB) vs. TimescaleDB (700MB) = 59x
• 100 devices x 10 metrics x 30 days: InfluxDB (113MB) vs. TimescaleDB (1400MB) = 12x
• 4,000 devices x 10 metrics x 3 days: InfluxDB (769MB) vs. TimescaleDB (5900MB) = 8x
Note: Disk size benchmarks were run using ZFS. Numbers do not include WAL size, as that is
configurable by the user.
If minimizing disk storage is a primary requirement for your workload, then this is a big
difference, and you may want to consider InfluxDB. However, as we saw earlier, depending on
your workload InfluxDB may also require much more memory. Given that memory is typically
100x-1000x more expensive than disk, trading off high disk usage for lower memory may be
worthwhile for certain workloads.
TimescaleDB also allows one to elastically scale the number of disks associated with a hypertable
without any data migration, which is another option to offset the higher disk consumption,
particularly in SAN or cloud contexts. Users have scaled a single TimescaleDB node to 10s of TB
using this method. The other cost of InfluxDB’s better on-disk compression is that it required the
developers to rewrite the backend storage engine from scratch, which raises reliability challenges.
Next steps
In this paper, we performed a detailed comparison of TimescaleDB and InfluxDB. One of the worst
mistakes a business can make is investing in a technology that will limit it in the future, let alone be
the wrong fit today. That’s why we encourage you to take a step back and analyze your stack before
you find your database infrastructure crumbling to the ground.
If you are ready to get started with TimescaleDB, follow the instructions below. If you have any
questions along the way, please contact us.
Additional resources
• Benchmark using the Time Series Benchmark Suite (TSBS)
• Follow our blog for database insights, engineering updates, tutorials, and more