{"post":{"slug":"time-series-databases-2026","title":"Time-Series Databases: When a Normal Table Stops Coping","excerpt":"Metrics, events, and sensor readings share a shape ordinary databases handle badly at volume. Purpose-built stores exploit that shape, and a partitioned relational table gets you most of the way.","category":"code","date":"January 13, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">M</span>etrics, application events, sensor readings, and audit logs all have the same shape: timestamped records that arrive continuously, are almost never updated, are queried over time ranges, and become less interesting as they age.\n\nThat shape is specific enough to optimise for, which is why purpose-built time-series stores exist and why they can be dramatically more efficient than a general-purpose table for the same data.\n\n## What changed in 2026\n\n- **Relational extensions closed much of the gap.** Time-series capabilities added to general databases made adopting a separate system less necessary.\n- **Compression improved further.** Specialised encodings for ordered numeric data pushed storage efficiency higher.\n- **Retention automation became standard.** Automatic downsampling and expiry moved from configuration to built-in behaviour.\n- **Cardinality limits stayed the main constraint.** High-cardinality label sets remained the failure mode across implementations.\n\n## What makes the workload distinctive\n\n| Property | Time-series | General OLTP |\n| --- | --- | --- |\n| Writes | Append-only, high rate | Mixed insert and update |\n| Updates | Rare or never | Common |\n| Queries | Time ranges, aggregates | Point lookups, joins |\n| Recency bias | Strong — recent data queried most | Weak |\n| Retention | Old data dropped or downsampled | Retained |\n| Cardinality | Can be enormous | Moderate |\n\nEach row enables an optimisation. Append-only writes mean no in-place update machinery. Time-range queries mean data can be physically ordered by time so a range is a contiguous read. Recency bias means recent data can live on fast storage and old data on cheap storage. Predictable retention means dropping old data can be a metadata operation.\n\n## Compression is the headline\n\nTime-series data compresses extraordinarily well, and this is usually the largest practical difference.\n\nConsecutive readings from the same source are similar. Timestamps increase at regular intervals. Values change slowly. Stored as columns rather than rows, those properties enable specialised encodings — storing differences rather than values, run-length encoding for repeated values, and delta encoding for regular timestamps.\n\nCompression ratios well beyond what general-purpose storage achieves are normal, which changes what is affordable to retain. See [columnar storage](/blog/columnar-storage-explained-2026) for the underlying mechanism.\n\n## Retention and downsampling\n\nOld time-series data is rarely needed at full resolution. Per-second metrics from two years ago are useful as hourly averages and not as individual points.\n\nDownsampling aggregates old data to a coarser resolution and discards the detail, which is frequently a hundredfold reduction. Purpose-built stores automate this: keep full resolution for a week, hourly for a year, daily beyond that.\n\nDropping old data must also be cheap. Deleting a month of rows with a `DELETE` is expensive and produces bloat; dropping a partition is a metadata operation — see [table partitioning](/blog/table-partitioning-explained-2026).\n\nThat is the single most valuable capability for this workload, and it is available in a general database with partitioning.\n\n## Try partitioning first\n\nBefore adopting another database, consider what a partitioned relational table gives you:\n\nTime-based partitioning, so range queries prune irrelevant partitions and dropping old data is instant. An index on the time column and any dimension you filter by. Compression where your engine supports it per-column or per-partition.\n\nThat covers a substantial fraction of time-series workloads, and it keeps everything in one system you already operate — which is worth more than most people account for. Another database means another thing to back up, monitor, upgrade, and be paged about.\n\nPurpose-built stores earn their place at genuinely high ingest rates, where compression ratios materially change storage cost, where automatic downsampling is needed, and where query patterns are overwhelmingly time-range aggregations.\n\nThe constraint to check either way is **cardinality**. Time-series systems index by the combination of dimensions, and a dimension with unbounded values — a user identifier, a request identifier — produces enormous cardinality that degrades every implementation. Keeping high-cardinality identifiers out of the dimension set is the single most important schema decision.\n\n## Common mistakes\n\n- **Adopting a specialist store for modest volume.** Operating another database is a real cost.\n- **High-cardinality dimensions.** The dominant failure mode.\n- **Deleting old rows instead of dropping partitions.** Expensive and produces bloat.\n- **No downsampling policy.** Storage grows without bound at full resolution.\n- **Treating it as a general database.** Joins and updates are not what it is for.\n- **Not partitioning a relational table used this way.** Gives up the cheapest win.\n\n## FAQ\n\n**Can I use my existing database?**\nFrequently yes, with time-based partitioning and appropriate indexing. That covers a lot of ground before a specialist system is warranted.\n\n**What ingest rate justifies a specialist store?**\nThere is no clean threshold — it depends on your hardware and schema. The practical trigger is when partitioning and compression in your existing database stop keeping up.\n\n**What about high-cardinality data?**\nStore high-cardinality identifiers as values rather than as indexed dimensions, or use a different store for that data. This is the constraint that most often forces a rethink.\n\n**Is this the same as an event store?**\nRelated and different. Event sourcing stores events as the source of truth for reconstructing state; time-series storage optimises for aggregating measurements over time.\n\n## Where to go next\n\nFor the partitioning approach in a general database, read [table partitioning](/blog/table-partitioning-explained-2026) and [hypertables](/blog/hypertables-explained-2026). For the compression mechanism, [columnar storage](/blog/columnar-storage-explained-2026).","hue":318,"metaTitle":"Time-Series Databases: When a Normal Table Stops Coping","metaDescription":"What makes time-series workloads distinctive, how purpose-built stores exploit that shape, and when a partitioned relational table is sufficient.","keywords":["time series database","metrics storage","downsampling retention","columnar compression","append only workload","partitioned time series","observability storage","database selection 2026"],"tldr":["**Time-series data is append-only and queried by range** — that shape enables the optimisations.","**Compression is the biggest win** — ordered similar values compress dramatically.","**Retention and downsampling are first-class features** — dropping old data must be cheap.","**A partitioned relational table covers a lot of ground** — before adopting a new system, try that.","**Skip:** a specialist store for modest volumes. Operating another database is a real cost."],"readTime":"4 min read","wordCount":883},"related":[{"slug":"hypertables-explained-2026","title":"Hypertables: Automatic Partitioning for Time-Series Data","excerpt":"Manual time partitioning works and requires you to create next month's partition before next month arrives. Hypertables automate that, and add compression and retention on top.","category":"code","date":"January 8, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":313},{"slug":"columnar-storage-explained-2026","title":"Columnar Storage Explained: Why Analytics Databases Are Fast","excerpt":"Storing a table by column instead of by row changes which queries are cheap. It is the single biggest reason an analytics query that crawls in Postgres flies in a warehouse.","category":"code","date":"August 14, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":316},{"slug":"table-partitioning-explained-2026","title":"Table Partitioning: Splitting One Table Without Sharding","excerpt":"Partitioning divides a table into pieces inside one database. It is not sharding, it is not a general speed-up, and it solves a narrower problem than most people expect.","category":"code","date":"August 12, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":310}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"streaming-json-parsing-2026","title":"Streaming JSON: Parsing Something That Is Not Valid Yet","excerpt":"You want to show structured output as it arrives, but partial JSON does not parse. Handling that properly is the difference between a responsive interface and a spinner.","category":"ai","date":"January 13, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":206},"older":{"slug":"vision-encoders-explained-2026","title":"Vision Encoders: How a Model Sees an Image","excerpt":"An image becomes a sequence of tokens the language model reads alongside text. Understanding that conversion explains why resolution costs so much and why small text disappears.","category":"ai","date":"January 13, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":202}}