Schema Design For Time Series Data in Bigtable
Schema Design For Time Series Data in Bigtable
cloud.google.com/bigtable/docs/schema-design-time-series
This page describes schema design patterns for storing time series data in Cloud Bigtable.
This page builds on Designing your schema and assumes you are familiar with the
concepts and recommendations described on that page.
A time series is a collection of data that consists of measurements and the times when the
measurements are recorded. Examples of time series include the following:
A good schema results in excellent performance and scalability, and a bad schema can
lead to a poorly performing system. However, no single schema design provides the best
fit for all use cases.
The patterns described on this page provide a starting point. Your unique dataset and the
queries you plan to use are the most important things to consider as you design a schema
for your time-series data.
The basic design patterns for storing time-series data in Bigtable are as follows:
Measurement Example
Humidity (percentage) 65
1/6
Measurement Example
Balloon ID 3698
Location asia-southeast1
Timestamp1 t2021-03-05-1204
Time buckets
In a time bucket pattern, each row in your table represents a "bucket" of time, such as an
hour, day, or month. A row key includes a non-timestamp identifier, such as week49 , for
the time period recorded in the row, along with other identifying data.
The size of the bucket that you use — such as minute, hour, or day — depends on the
queries that you plan to use and on Bigtable data size limits. For instance, if rows that
contain an hour of data are bigger the recommended maximum size per row of 100 MB,
then rows that represent a half hour or a minute are probably a better choice.
You'll see better performance. For example, if you store 100 measurements, Bigtable
writes and reads those measurements faster if they are in one row than if they are in
100 rows.
Data stored in this way is compressed more efficiently than data in tall, narrow
tables.
In this time bucket pattern, you write a new column to a row for each event, storing the
data in the column qualifier rather than as a cell value. This means that for each cell, you
send the column family, column qualifier, and timestamp, but no value.
Using this pattern for the sample weather balloon data, each row contains all the
measurements for a single metric, such as pressure , for a single weather balloon, over
the course of a week. Each row key contains the location, balloon ID, metric that you are
2/6
recording in the row, and a week number. Every time a balloon reports its data for a
metric, you add a new column to the row. The column qualifier contains the
measurement, the pressure in Pascals, for the minute identified by the cell timestamp.
In this example, after three minutes a row might look like this:
Using the weather balloon data as an example, each row contains all the measurements
for a single weather balloon over the course of a week. The row key prefix is an identifier
for the week, so you can read an entire week's worth of data for multiple balloons with a
single query. The other row key segments are the location where the balloon operates and
the ID number for the balloon. The table has one column family, measurements , and
that column family has one column for each type of measurement: pressure ,
temperature , humidity , and altitude .
Every time a balloon sends its measurements, the application writes new values to the row
that holds the current week's data for the balloon, writing additional timestamped cells to
each column. At the end of the week, each column in each row has one measurement for
each minute of the week, or 10,080 cells (if your garbage collection policy allows it).
Each column in each row holds a measurement for each minute of the week. In this case,
after three minutes, the first two columns in a row might look like this:
3/6
You want to be able to measure changes in measurements over time.
Single-timestamp rows
In this pattern, you create a row for each new event or measurement instead of adding
cells to columns in existing rows. The row key suffix is the timestamp value. Tables that
follow this pattern tend to be tall and narrow, and each column in a row contains only
one cell.
Important: To avoid hotspots, never use a timestamp value as a row key prefix.
Single-timestamp serialized
In this pattern, you store all the data for a row in a single column in a serialized format
such as a protocol buffer (protobuf). This approach is described in more detail on
Designing your schema.
For example, if you use this pattern to store the weather balloon data, your table might
look like this after four minutes:
us-west2#3698#2021-03-05-1200 protobuf_1
us-west2#3698#2021-03-05-1201 protobuf_2
us-west2#3698#2021-03-05-1202 protobuf_3
us-west2#3698#2021-03-05-1203 protobuf_4
Storage efficiency
Speed
The inability to retrieve only certain columns when you read the data
You are not sure how you will query the data or your queries might fluctuate.
Your need to keep costs down outweighs your need to be able to filter data before
you retrieve it from Bigtable.
Each event contains so many measurements that you might exceed the 100 MB per-
row limit if you store the data in multiple columns.
4/6
Single-timestamp unserialized
In this pattern, you store each event in its own row, even if you are recording only one
measurement. The data in the columns is not serialized.
You might spend less time refining your schema before using it.
Data stored this way is not as efficiently compressed as data in wider columns.
Even when the timestamp is at the end of the row key, this pattern can result in
hotspots.
You want to always retrieve all columns but only a specified range of timestamps,
but you have a reason not to store the data in a serialized structure.
Using the weather balloon example data, the column family and column qualifiers are the
same as the example using time buckets and new cells. In this pattern, however, every set
of reported measurements for each weather balloon is written to a new row. The
following table shows five rows that are written using this pattern:
Additional strategies
If you need to send multiple different queries for the same dataset, consider storing your
data in multiple tables, each with a row key designed for one of the queries.
You can also combine patterns in some cases. For example, you can store serialized data
in rows that represent time buckets, as long as you don't let the rows become too big.
5/6
What's next
Review the steps involved in planning a schema.
Understand the best practices for designing a schema.
Read about the performance you can expect from Bigtable.
Explore the diagnostic capabilities of Key Visualizer.
Work through a tutorial on monitoring time-series data with OpenTSDB and Google
Cloud.
6/6