-
Dear community, https://clickhouse.com/docs/en/optimize/sparse-primary-indexes However, when I read articles about making efficient time-series tables, I always see DateTime types in the ORDER BY clause, which by definition, if there's no PRIMARY KEY clause, then the ORDER BY clause become as well the PRIMARY KEY. Some of those examples can be found here: https://clickhouse.com/blog/working-with-time-series-data-and-functions-ClickHouse#querying
In my use-case, I'm generating over 2k entries per second (they are batched before inserting them in clickhouse), and each entry has a DateTime timestamp which would generate basically entries covering every millisecond. If I follow the example above, where timestamp is part of the ORDER BY (and therefore, also the PRIMARY KEY), then that would contradict the recommendation of not using high-cardinality columns as PRIMARY KEY. Yet, all (or most) queries I'll do to the database will always filter by time, usually in the hours to a few weeks time window. So my question is: Should I use DateTime fields as part of the PRIMARY KEY in order to make my queries more efficient? |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 5 replies
-
No, nothing like this. Number of granules is not related to columns in keys, it's defined by a number of rows. 1 granule=8192 rows. So in the case
I would definitely use For timeseries tables I suggest a very strange approach:
see discussion and examples #33056 |
Beta Was this translation helpful? Give feedback.
-
Thanks for your answer, @den-crane
Assuming I would use that key, I guess CH would know that next time I do a query with something like
it would only go search in those respective indexes? Would you also suggest, on top of this, partitioning with something like:
|
Beta Was this translation helpful? Give feedback.
-
I plan to store data approximately 1 year. But queries will be usually in a few days to few weeks range. |
Beta Was this translation helpful? Give feedback.
-
Hey @den-crane. I myself is trying to figure out the indexing for a time-series data. Essentially my question is Clickhouse SpecsCPU - 16vCPU AIMI have an observability pipeline like this.
It is expected that almost ~5 Billion rows of telemetry data (metrics, logs and traces combined) will be generated everyday, which needs to be stored. I need to retain the same for 90 days, and hence I am using the TTL as 90 days in the table definition. Attaching a screenshot of current amount of data in the DB. I use the native Clickhouse Exporter of Otel Collector to export our data in batches. Writing the data into the DB is not at all an issue. Everything works well here. The ProblemThe problem comes in reading telemetry data from Grafana, as explained below. Table Definitions [Current]Logs Table (Only sharing the logs table definition in full) CREATE TABLE IF NOT EXISTS otel.otel_logs ON CLUSTER otel_cluster (
Timestamp DateTime64(9) CODEC(Delta, ZSTD(1)),
TraceId String CODEC(ZSTD(1)),
SpanId String CODEC(ZSTD(1)),
TraceFlags UInt32 CODEC(ZSTD(1)),
SeverityText LowCardinality(String) CODEC(ZSTD(1)),
SeverityNumber Int32 CODEC(ZSTD(1)),
ServiceName LowCardinality(String) CODEC(ZSTD(1)),
Body String CODEC(ZSTD(1)),
ResourceSchemaUrl String CODEC(ZSTD(1)),
ResourceAttributes Map(LowCardinality(String), String) CODEC(ZSTD(1)),
ScopeSchemaUrl String CODEC(ZSTD(1)),
ScopeName String CODEC(ZSTD(1)),
ScopeVersion String CODEC(ZSTD(1)),
ScopeAttributes Map(LowCardinality(String), String) CODEC(ZSTD(1)),
LogAttributes Map(LowCardinality(String), String) CODEC(ZSTD(1)),
INDEX idx_trace_id TraceId TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_res_attr_key mapKeys(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_res_attr_value mapValues(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_scope_attr_key mapKeys(ScopeAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_scope_attr_value mapValues(ScopeAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_log_attr_key mapKeys(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_log_attr_value mapValues(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_body Body TYPE tokenbf_v1(32768, 3, 0) GRANULARITY 1
) ENGINE ReplicatedMergeTree('/clickhouse/tables/{shard}/{database}/{table}', '{replica}')
TTL toDateTime(Timestamp) + toIntervalDay(90)
PARTITION BY toDate(Timestamp)
ORDER BY (ServiceName, SeverityText, toUnixTimestamp(Timestamp), TraceId)
SETTINGS index_granularity=8192, ttl_only_drop_parts = 1; Traces Table ...
...
TTL toDateTime(Timestamp) + toIntervalDay(90)
PARTITION BY toDate(Timestamp)
ORDER BY (ServiceName, SpanName, toUnixTimestamp(Timestamp), TraceId)
SETTINGS index_granularity=8192, ttl_only_drop_parts = 1;
...
... Query
Querying on the above tables generally takes more than 60s and the query processes all the rows. 99% of the queries are going to be based on Timestamp, i.e the very first WHERE in all the queries is always going to be Timestamp clause. With this said, I would like to ask now how I can optimise the same ? QuestionsQ1. Will changing the ORDER BY (toStartOfHour(Timestamp), ServiceName, SeverityText, TraceId, Timestamp)
PRIMARY KEY (toStartOfHour(Timestamp), ServiceName, SeverityText, TraceId) Q2. Since there might still be a ton of data in every hour of time, I was also thinking of doing something like this - ORDER BY (toStartOfHour(Timestamp), toStartOfFifteenMinutes(Timestamp), ServiceName, SeverityText, TraceId, Timestamp)
PRIMARY KEY (toStartOfHour(Timestamp), toStartOfFifteenMinutes(Timestamp), ServiceName, SeverityText, TraceId) Does this actually help, or rather degrades the performance ? Q3. I also have a case where the exact same data needs to stored in another Clickhouse cluster BUT with TTL as 2182 days ~ 5 Years. Should I change the Any inputs will be greatly appreciated 🙏. Thanks and Regards PS: Also Apologies for hijacking this issue thread for my own issue. I just felt, they were similar in nature, and so might help other people in the future. |
Beta Was this translation helpful? Give feedback.
No, nothing like this. Number of granules is not related to columns in keys, it's defined by a number of rows. 1 granule=8192 rows.
High cardinal rows are not recommended at the leading positions of the primary key , because they de-valuate other columns in the primary key, BUT only if you are omitting these high cardinal columns in the where section with equality expression. Also they may reduce compression rate.
So in the case
order by (event_id, city, region, country)
I would bluntly suggest to chan…