|
| 1 | +--- |
| 2 | +title: Tune TraceQL query performance |
| 3 | +menuTitle: Tune query performance |
| 4 | +description: Practical ways to make TraceQL queries faster in Tempo |
| 5 | +weight: 310 |
| 6 | +keywords: |
| 7 | + - TraceQL |
| 8 | + - performance |
| 9 | + - optimization |
| 10 | +--- |
| 11 | + |
| 12 | +# Tune TraceQL query performance |
| 13 | + |
| 14 | +Use these techniques to make TraceQL queries faster and more cost‑efficient in Tempo. The guidance assumes you already understand tracing concepts and how to write basic TraceQL queries. |
| 15 | + |
| 16 | +Before you begin, ensure you have the following: |
| 17 | + |
| 18 | +- A running Tempo deployment and access to query it from Grafana |
| 19 | +- Familiarity with TraceQL expressions and pipelines |
| 20 | +- Recent Tempo storage formats (for example, vParquet4+) for best performance |
| 21 | + |
| 22 | +## Use only and (&&) when possible |
| 23 | + |
| 24 | +Queries that consist only of logical and (`&&`) conditions are much faster than queries that include other logical or structural operators (`||`, `>>`, and so on). These queries let the TraceQL engine push filtering down into the Parquet layer (`predicate pushdown`), which is the most efficient path. |
| 25 | + |
| 26 | +Rewrite or simplify queries to use `&&` when possible. |
| 27 | + |
| 28 | +For example, to match spans with a non‑200 status code on a specific path, use a single conjunction inside one selector: |
| 29 | + |
| 30 | +The following query returns a single span where the HTTP status code is not `200` and the URL is `/api`: |
| 31 | + |
| 32 | +```traceql |
| 33 | +{ span.http.status_code != 200 && span.http.url = "/api" } |
| 34 | +``` |
| 35 | + |
| 36 | +Also avoid splitting these conditions across multiple selectors joined by `&&`, which matches them on different spans and changes semantics: |
| 37 | + |
| 38 | +```traceql |
| 39 | +{ span.http.url = "/api" } && { span.http.status_code != 200 } |
| 40 | +``` |
| 41 | + |
| 42 | +Use a single selector when you want both conditions to be true on the same span. |
| 43 | + |
| 44 | +```traceql |
| 45 | +{ span.http.url = "/api" && span.http.status_code != 200 } |
| 46 | +``` |
| 47 | + |
| 48 | +## Prefer scoped attributes over attributes without a scope |
| 49 | + |
| 50 | +Always scope attributes with `span.`, `resource.`, `event.`, or `link.`. Attributes without a scope make Tempo check in multiple places (for example, span and resource), which is slower. |
| 51 | + |
| 52 | +To find spans by HTTP status code, prefer a scoped attribute: |
| 53 | + |
| 54 | +The following query scopes the attribute to the span and runs faster than an equivalent without a scope: |
| 55 | + |
| 56 | +```traceql |
| 57 | +{ span.http.status_code = 500 } |
| 58 | +``` |
| 59 | + |
| 60 | +Avoid forms without a scope that force extra lookups: |
| 61 | + |
| 62 | +```traceql |
| 63 | +{ .http.status_code = 500 } |
| 64 | +``` |
| 65 | + |
| 66 | +## Access as few attributes as possible |
| 67 | + |
| 68 | +Tempo stores trace data in a columnar format (Parquet). It only reads the columns referenced by your query. If you can achieve the same effect while referencing fewer attributes, the query runs faster. |
| 69 | + |
| 70 | +For example, if filtering by status code already identifies error spans in your environment, you can drop a redundant status check. |
| 71 | + |
| 72 | +This query filters on both an HTTP status and an explicit status: |
| 73 | + |
| 74 | +```traceql |
| 75 | +{ span.http.status_code = 500 && status = error } |
| 76 | +``` |
| 77 | + |
| 78 | +If the status code alone is sufficient, prefer the simpler form: |
| 79 | + |
| 80 | +```traceql |
| 81 | +{ span.http.status_code = 500 } |
| 82 | +``` |
| 83 | + |
| 84 | +## Filter by trace‑ and resource‑level attributes |
| 85 | + |
| 86 | +Columns for trace‑level intrinsic fields such as `trace:duration`, `trace:rootName`, and `trace:rootService`, and for resource attributes such as `resource.service.name`, are much smaller than span‑level columns. Filtering on these fields reduces data that must be scanned. |
| 87 | + |
| 88 | +To find long traces for a given service, filter on a trace intrinsic and a resource attribute: |
| 89 | + |
| 90 | +```traceql |
| 91 | +{ trace:duration > 5s && resource.service.name = "api" } |
| 92 | +``` |
| 93 | + |
| 94 | +To target a specific root operation in production, filter on `trace:rootName` and a resource attribute: |
| 95 | + |
| 96 | +```traceql |
| 97 | +{ trace:rootName = "POST /api/orders" && resource.deployment.environment = "production" } |
| 98 | +``` |
| 99 | + |
| 100 | +## Use dedicated columns for common fields |
| 101 | + |
| 102 | +Tempo exposes many frequently used fields as dedicated columns in Parquet to accelerate filtering and selection. Prefer these well‑known, scoped fields instead of relying on nested or ambiguous attribute paths. |
| 103 | + |
| 104 | +For example, the following queries benefit from dedicated columns and scope: |
| 105 | + |
| 106 | +```traceql |
| 107 | +{ span.http.method = "GET" } |
| 108 | +{ span.db.system = "postgresql" } |
| 109 | +{ resource.cloud.region =~ "us-east-1|us-west-1" } |
| 110 | +``` |
| 111 | + |
| 112 | +## Use sampling for large metrics queries |
| 113 | + |
| 114 | +For TraceQL metrics queries on very large datasets, enable sampling to return approximate results faster. Sampling can dramatically reduce scan time while retaining useful accuracy for operational dashboards. |
| 115 | + |
| 116 | +To get the 90th percentile span duration for a service with sampling enabled: |
| 117 | + |
| 118 | +```traceql |
| 119 | +{ resource.service.name = "api" } | quantile_over_time(duration, 0.9) with(sample=true) |
| 120 | +``` |
| 121 | + |
| 122 | +For guidance on when and how to use sampling, refer to the [Sampling guide](https://grafana.com/docs/tempo/<TEMPO_VERSION>/set-up-for-tracing/instrument-send/set-up-collector/tail-sampling/). |
| 123 | + |
| 124 | +## Next steps |
| 125 | + |
| 126 | +- Build stronger selectors with the [Construct a TraceQL query](https://grafana.com/docs/tempo/<TEMPO_VERSION>/traceql/construct-traceql-queries/) guide. |
| 127 | +- Explore aggregations and grouping in [TraceQL metrics queries](https://grafana.com/docs/tempo/<TEMPO_VERSION>/traceql/metrics-queries/). |
| 128 | +- Learn about intrinsic fields and attribute scopes in [TraceQL selection and fields](https://grafana.com/docs/tempo/<TEMPO_VERSION>/traceql/construct-traceql-queries/#select-spans). |
0 commit comments