What Is a Time-Series Database?
A time-series database is built for data that arrives in timestamp order and is rarely changed afterwards: metrics, sensor readings, logs, financial ticks. It organizes storage around the time column rather than indexing it as an afterthought, which is what makes time-range filters, time-bucketed aggregation, compression, and automatic retention fast and cheap instead of something you hand-roll.
You have data with timestamps. Server metrics, sensor readings, application logs, financial ticks. Every row has a created_at or recorded_at column, and your queries almost always include a WHERE timestamp > ... clause.
PostgreSQL can store all of it. So can MySQL. Any relational database handles timestamps just fine. But at some point, you start noticing that your aggregation queries are slow, your table is growing faster than you expected, and you're writing the same date_trunc + GROUP BY pattern over and over. That's when the question comes up: do I need a specialized database for this?
Sometimes the answer is no. Sometimes it's absolutely yes. Here's how to tell the difference.
Contents
- What Makes Time-Series Data Different
- What a Time-Series Database Does Differently
- When PostgreSQL Is Enough
- When You Need a Dedicated TSDB
- Your Options
- Managed Time-Series Databases with Layerbase Cloud
- Try It: QuestDB with SAMPLE BY
- FAQ
What Makes Time-Series Data Different
Time-series data has a distinct shape that separates it from typical application data. Understanding these patterns helps explain why specialized databases exist.
Append-mostly writes. You almost never update an old sensor reading or go back and change a metric from yesterday. Data arrives, gets written, and stays as-is. This is fundamentally different from a users table where you're constantly updating emails, passwords, and preferences. Time-series workloads are write-heavy and update-light.
Queries always filter by time range. "Show me the last hour of CPU usage." "What was the average temperature between 2pm and 6pm?" "Give me all error rates from the past 7 days." The time column is in virtually every query. General-purpose databases can index a timestamp column, but they don't organize storage around it.
Data ages out. Per-second metrics from two years ago are rarely useful at their original resolution. You might want hourly averages from last year, but you don't need 31 million individual data points. Time-series data has a natural lifecycle: high resolution when fresh, summarized as it ages, eventually deleted.
Write volumes can be enormous. A single server reporting CPU, memory, disk, and network metrics every 10 seconds generates 34,560 data points per day. A fleet of 1,000 servers generates 34 million. Add application-level metrics (request latency, error rates, queue depths) and you're looking at hundreds of millions of rows per day. That's not an edge case. That's a normal monitoring setup.
What a Time-Series Database Does Differently
A time-series database (TSDB) is built around the patterns above. It's not a general-purpose database with a timestamp index bolted on. The storage engine, query optimizer, and data lifecycle tools are all designed for time-ordered, append-mostly data.
Time-based partitioning. Data is physically organized by time intervals (hourly, daily, weekly). When you query "the last 24 hours," the database reads only the partitions that contain that range. It doesn't scan your entire 500GB table to find yesterday's rows.
Compression for repetitive patterns. Timestamps in time-series data are highly regular. If a sensor reports every 15 seconds, the database can store the start time and interval instead of every individual timestamp. Numeric values from the same source tend to be close together, which compresses well with delta or XOR encoding. Real-world compression ratios of 10:1 or better are common.
Built-in downsampling and retention. Instead of writing cron jobs to aggregate old data and delete raw rows, a TSDB handles it natively. Keep raw data for 7 days. Roll it up to 1-minute averages for 90 days. Delete everything older than a year. Set it once and forget about it.
Optimized write path. Time-series writes are sequential (data arrives roughly in order) and don't need the transaction overhead of a general-purpose database. TSDBs exploit this with write-ahead logs, batched ingestion, and lock-free append operations that handle millions of writes per second.
Time-bucketed aggregation as a first-class operation. "Average value per 15-minute window" is not an afterthought. It's a core operation with dedicated syntax. In QuestDB, it's SAMPLE BY 15m. In InfluxDB, it's GROUP BY time(15m). Compare that to PostgreSQL's date_trunc('minute', ts) + (EXTRACT(minute FROM ts)::int / 15) * interval '15 minutes'.
When PostgreSQL Is Enough
PostgreSQL is a remarkably capable database, and plenty of time-series workloads run fine on it. You probably don't need a TSDB if:
- Write volume is moderate. Hundreds or low thousands of writes per second. PostgreSQL handles this without breaking a sweat, especially with proper indexing and partitioning.
- Your data fits on one server. If your total dataset is tens of gigabytes and growing slowly, PostgreSQL's native table partitioning (available since version 10) gives you many of the benefits of time-based partitioning.
- You don't need sub-second query latency on large ranges. If "show me the last month of hourly averages" taking 2 seconds is acceptable, PostgreSQL will get it done.
- Your team already knows PostgreSQL. There's real value in not adding another database to your stack. One more database means one more thing to monitor, back up, upgrade, and debug at 2am.
- You only need time-series alongside other data. If your app is primarily a CRUD application that also happens to store some metrics, keeping everything in PostgreSQL avoids the complexity of running two databases and joining across them.
The TimescaleDB extension deserves a mention here. It adds time-series capabilities (automatic partitioning, continuous aggregates, compression, retention policies) to PostgreSQL. If you want TSDB features without leaving PostgreSQL, it's a strong option.
When You Need a Dedicated TSDB
The signs are usually obvious when you hit them:
- High write throughput. Tens of thousands of writes per second and up. You're running a fleet of servers, a network of IoT devices, or a trading platform with tick-by-tick data. PostgreSQL starts struggling with write amplification and index maintenance at these volumes.
- You need automatic retention and downsampling. Manually managing data lifecycle with cron jobs and
DELETEqueries gets brittle fast. If you're writing scripts to aggregate old data into summary tables and purge raw rows, a TSDB does this out of the box. - Time-bucketed aggregation is your primary query pattern. If most of your queries are "average X per Y-minute window, grouped by Z," a TSDB makes these trivial to write and fast to execute. Writing
date_truncandGROUP BYfor the hundredth time is a signal. - Data measured in billions of rows. At this scale, compression and time-partitioned storage aren't nice-to-haves. They're the difference between a database that fits on reasonable hardware and one that doesn't.
- You need real-time dashboards with sub-second latency. Monitoring dashboards that refresh every few seconds and query across millions of recent data points. TSDBs are optimized for exactly this access pattern.
Your Options
Two time-series databases worth looking at, each with a different philosophy:
QuestDB extends SQL with time-series primitives. If your team writes SQL, QuestDB is immediately productive. SAMPLE BY 15m for time-bucketed aggregation, LATEST ON for the most recent value per group. It speaks the PostgreSQL wire protocol, so you connect with any PG client library. No new query language to learn.
InfluxDB takes a different approach entirely. Its own data model (measurements, tags, fields), a write path built around line protocol, and deep integration with the Telegraf metrics collection agent. InfluxDB 3 queries with SQL and InfluxQL; Flux, the functional pipeline language that InfluxDB 2 pushed, is now in maintenance mode and is not supported in version 3. The monitoring ecosystem around InfluxDB is mature and well-tested. (Verified 2026-08-25 against the InfluxDB 3 docs and the future of Flux.)
For a detailed comparison with runnable code, see QuestDB vs InfluxDB. For getting-started guides with full TypeScript projects:
Managed Time-Series Databases with Layerbase Cloud
QuestDB and InfluxDB are both available on Layerbase Cloud if you want a managed time-series database instead of a local development server. Create QuestDB when you want SQL and PostgreSQL-compatible connections. Create InfluxDB when you want the Influx data model, Flux, and the Telegraf ecosystem.
Use the Layerbase CLI (formerly SpinDB) or Layerbase Desktop when you want to experiment locally. Use Layerbase Cloud when you want a hosted endpoint your application or dashboard can reach from anywhere.
Try It: QuestDB with SAMPLE BY
The fastest way to see the difference is to run a query. The Layerbase CLI will have QuestDB running in seconds. (What is the Layerbase CLI?)
npm i -g layerbase
lbase create quest1 -e questdb --startConnect and create a table with some sample data:
lbase connect quest1CREATE TABLE IF NOT EXISTS metrics (
host SYMBOL,
cpu_usage DOUBLE,
mem_usage DOUBLE,
ts TIMESTAMP
) timestamp(ts) PARTITION BY HOUR WAL;
INSERT INTO metrics VALUES ('web-1', 45.2, 62.1, '2026-03-14T10:00:00Z');
INSERT INTO metrics VALUES ('web-2', 72.8, 58.3, '2026-03-14T10:00:05Z');
INSERT INTO metrics VALUES ('web-1', 47.1, 63.4, '2026-03-14T10:15:00Z');
INSERT INTO metrics VALUES ('web-2', 68.5, 59.1, '2026-03-14T10:15:05Z');
INSERT INTO metrics VALUES ('web-1', 51.3, 65.7, '2026-03-14T10:30:00Z');
INSERT INTO metrics VALUES ('web-2', 71.2, 57.8, '2026-03-14T10:30:05Z');
INSERT INTO metrics VALUES ('web-1', 44.9, 61.2, '2026-03-14T10:45:00Z');
INSERT INTO metrics VALUES ('web-2', 69.4, 60.5, '2026-03-14T10:45:05Z');Now run a time-bucketed aggregation:
SELECT
host,
avg(cpu_usage) as avg_cpu,
avg(mem_usage) as avg_mem
FROM metrics
SAMPLE BY 30m
ALIGN TO CALENDAR;host | avg_cpu | avg_mem
-------|---------|--------
web-1 | 46.15 | 62.75
web-2 | 70.65 | 58.70
web-1 | 48.10 | 63.45
web-2 | 70.30 | 59.15That's SAMPLE BY 30m. Two words instead of a date_trunc expression, a GROUP BY clause, and an ORDER BY. Change it to 1h for hourly, 1d for daily, 5m for five-minute buckets. The database handles the rest.
Compare that to the PostgreSQL equivalent:
SELECT
host,
date_trunc('hour', ts)
+ (EXTRACT(minute FROM ts)::int / 30) * interval '30 minutes'
AS bucket,
avg(cpu_usage) AS avg_cpu,
avg(mem_usage) AS avg_mem
FROM metrics
GROUP BY host, bucket
ORDER BY bucket, host;Same result. More typing. And at scale, SAMPLE BY runs faster because QuestDB's storage engine is built for exactly this operation.
To manage your instance:
lbase stop quest1 # Stop QuestDB
lbase start quest1 # Start it again
lbase list # See all your database instancesThe Layerbase CLI manages 20+ database engines from one CLI. Run QuestDB for time-series alongside PostgreSQL for your app and Redis for caching. If you prefer a GUI, Layerbase Desktop is available for macOS. If you want a hosted time-series database, create QuestDB or InfluxDB on Layerbase Cloud.
FAQ
What is a time-series database used for?
Storing and querying anything that is measured repeatedly over time: server and application metrics, IoT sensor readings, financial market ticks, event and activity logs. The common thread is that rows arrive in roughly timestamp order, almost never get updated afterwards, and are nearly always queried by a time range.
How is a time-series database different from a regular database?
A regular database can index a timestamp column; a time-series database organizes its storage around one. That difference buys you partitioning by time interval so a "last 24 hours" query touches only the relevant partitions, compression that exploits how regular timestamps and adjacent readings are, an append-optimized write path, and time-bucketed aggregation as real syntax rather than a date_trunc expression you rewrite every time.
Do I need a time-series database, or is PostgreSQL enough?
PostgreSQL is enough more often than people expect. If you are writing hundreds or low thousands of rows per second, your dataset fits comfortably on one server, and a month-long aggregation taking a couple of seconds is acceptable, stay where you are. Reach for a dedicated engine when write volume climbs into the tens of thousands per second, when row counts reach the billions, when you need retention and downsampling handled for you, or when dashboards demand sub-second answers.
What are examples of time-series databases?
QuestDB and InfluxDB are the two we run managed, and they take opposite approaches: QuestDB extends SQL with time-series primitives, InfluxDB brings its own data model and the Telegraf ecosystem. TimescaleDB is worth knowing as the middle path, since it adds partitioning, continuous aggregates, compression, and retention policies to PostgreSQL itself. Prometheus is the other name you will hear, though it is really a monitoring system with a time-series store inside it.
Is PostgreSQL with TimescaleDB a real alternative?
Yes, and it is the natural first step if leaving PostgreSQL is the part you do not want to do. You get automatic partitioning, continuous aggregates, compression, and retention policies while keeping your existing SQL, drivers, tooling, and operational knowledge. Where a purpose-built engine still pulls ahead is raw ingestion throughput and the very large scans, which is why QuestDB advertises SAMPLE BY rather than a date_trunc workaround.
What does downsampling mean?
Keeping fresh data at full resolution and progressively summarizing it as it ages. Per-second readings are valuable this week and mostly noise from two years ago, so you might keep raw points for 7 days, roll them into one-minute averages for 90 days, then delete anything older than a year. A time-series database does this on a policy you set once, instead of the cron jobs and DELETE queries you would otherwise maintain by hand.
Keep reading
- The InfluxDB 3 Core 72-hour query limit: what it is, why it exists, and your two ways outInfluxDB 3 Core refuses queries that touch more than 432 Parquet files, which at the default 10-minute file duration works out to about 72 hours of data per query. The data is still there. Here is the mechanism, the knob, what Layerbase actually runs, and the two honest exits.
- Branching time-series databasesNobody branches a time-series database. Neon branches Postgres, PlanetScale branches MySQL, and telemetry data gets a stale CSV sample. QuestDB and InfluxDB branch on Layerbase now, and it changes how you test against real metrics.
- Best Database for IoTA practical guide to picking the right database for IoT sensor data, covering QuestDB, InfluxDB, and PostgreSQL with SpinDB examples you can run locally.
- QuestDB vs InfluxDBA side-by-side comparison of QuestDB and InfluxDB for time-series workloads, with identical sensor pipelines in TypeScript to show how different they are.