Home / Blog / Why your Looker dashboards are slow

Why your Looker dashboards are slow — and how to fix each cause

Slow dashboards are never random. This is the diagnostic we run in every performance engagement: eight causes, how to confirm each one, and the fix that goes with it.

By Martín Vélez July 8, 2026 15 min read

"Looker is slow" is the most common complaint we hear — and it's almost never true. Looker is a thin layer that writes SQL, sends it to your warehouse, and renders the result. When a dashboard takes 40 seconds to load, the time is going somewhere specific: a query scanning too much data, a PDT that hasn't finished building, an Explore joining tables it doesn't need, a cache that never hits, or a dashboard firing 25 queries at once.

The difference between guessing and fixing is knowing which one. In our performance work — where the outcome has been 40%+ faster dashboards, alongside 30%+ lower BigQuery cost, because slow and expensive are usually the same problem — the first week is always diagnosis, not tuning. This article is that diagnostic, written down.

For each cause below: the symptom, how to confirm it in System Activity or the SQL Runner, and the fix. They're ordered roughly by how often we find them. Work through them in order and you'll either fix your dashboard or know exactly what to hand to whoever does.

One tool before you start. Almost everything here is confirmed in Looker's System Activity (Admin → System Activity, or the i__looker Explores). The History Explore gives you runtime per query, per dashboard, per user; the PDT Event Log gives you build times. If you have Admin or see_system_activity access, you have everything this article requires.

Cause 1 — The underlying tables force big scans

Symptom: a single tile is slow even when run alone in an Explore, and the same logic is slow in SQL Runner too. The problem isn't Looker at all — it's the warehouse.

Confirm it: open the tile's Explore, view the SQL, and run it (or check its bytes scanned in BigQuery's query history). If the query scans gigabytes to answer a question about last week, the table is unpartitioned, the filter isn't reaching the partition column, or there's no clustering on the columns being filtered.

Fix it: partition large fact tables by date, cluster by the most-filtered columns, and make sure Looker's filters map directly onto the partition column — a filter applied to a transformed expression can disable partition pruning. Add an always_filter on the partition field so no query can run unbounded. This is warehouse work, not LookML work, but it's the foundation everything else sits on: no amount of Looker tuning saves a dashboard built on full-table scans. The full warehouse-side playbook is in how to cut BigQuery costs.

This cause alone often explains both the slowness and the BigQuery bill. If cost is the sharper pain, the same diagnosis feeds our BigQuery cost optimization playbook.

Cause 2 — Queries answer summary questions at detail grain

Symptom: an executive dashboard showing monthly trends takes 30 seconds, and the SQL reveals it's aggregating millions of raw rows on every load.

Confirm it: look at the generated SQL for the slowest tiles. If a "revenue by month" tile is scanning an event-level table, you're paying detail-grain cost for summary-grain questions — on every single view.

Fix it: aggregate awareness. Define aggregate_table rollups in LookML at the grain your dashboards actually query (day/week/month by a handful of dimensions). Looker automatically routes matching queries to the rollup — dashboards get faster, bytes scanned collapse, and nobody has to change anything on the dashboard side. Design the rollups from real query patterns in System Activity, not from guesses, or the aggregates will never match.

Cause 3 — PDTs are stale, rebuilding, or built wrong

Symptom: the dashboard is fast at 11 a.m. but crawls at 8 a.m.; or tiles intermittently take minutes; or data freshness slips and morning dashboards show yesterday.

Confirm it: System Activity → PDT Event Log. Look for builds measured in hours, builds that overlap with peak dashboard usage, chains of PDTs that build sequentially, and multiple PDTs with suspiciously similar SQL.

Fix it, in escalating order:

  1. Reschedule — move builds off peak hours and stagger them so they don't compete for warehouse capacity.
  2. Make them incremental — a PDT that rebuilds three years of history every night to append one day is the single most common PDT anti-pattern. Incremental PDTs process only new data.
  3. Deduplicate — consolidate PDTs computing overlapping data into one shared table.
  4. Reconsider the layer — for straightforward aggregations, a BigQuery materialized view is often cheaper and always fresher than a scheduled PDT.

A user who hits a dashboard while its PDT is mid-build waits for the build. If users ever see multi-minute loads that you can't reproduce, this is almost always why.

Cause 4 — The Explore joins more than the question needs

Symptom: simple-looking tiles generate SQL with six joins; queries get slower as the model grows; some numbers are inflated (a fanout — the join multiplied rows before aggregation).

Confirm it: read the generated SQL. If a tile counting orders joins users, items, shipments, and refunds to do it, the Explore is over-built. Fanouts show up as symmetric aggregate functions in the SQL, which are correct but expensive.

Fix it: build narrower Explores for high-traffic use cases instead of one mega-Explore that joins everything to everything. Looker only joins tables that a query references — but a poorly factored model forces references through unnecessary paths, and every join is warehouse work. Where one-to-many joins are unavoidable, pre-aggregate the many side in a derived table so the join happens at matching grain. This is modeling work — the kind of LookML refactoring we do in Looker consulting — and it's also where refactoring pays back in trust, because fanout bugs are how dashboards end up showing numbers nobody believes.

Cause 5 — The cache never hits

Symptom: the same dashboard, viewed by ten people in the same hour, runs its full query set ten times.

Confirm it: System Activity History has a cache flag per query. If your busiest dashboards show near-zero cache hits, caching is misconfigured — or actively disabled.

Fix it: datagroups that match reality. If your ELT lands at 6 a.m., a datagroup with a sql_trigger on the load timestamp serves cached results all day and refreshes exactly once — instead of a blanket 1-hour policy that re-queries unchanged data 24 times a day. Also check: dashboard filters set to unique per-user defaults (each variation is a separate cache entry), tiles configured to bypass cache, and auto-refresh dashboards on office TVs quietly re-running every few minutes. BigQuery adds its own free 24-hour result cache underneath, but only for byte-identical, deterministic queries — one CURRENT_TIMESTAMP() in a derived table breaks it.

Cause 6 — The dashboard itself is the problem

Symptom: every tile is individually fine, but the dashboard takes forever to become usable.

Confirm it: count the tiles. A dashboard with 25 tiles fires up to 25 queries on load. They queue — against Looker's per-user and per-instance concurrency limits and against your warehouse's capacity — so tile 25 waits for tiles 1 through 24 even if its own query takes two seconds.

Fix it:

  • Split it. A 25-tile dashboard is usually three dashboards wearing a trench coat. Separate by audience and decision, not by "everything the team might ever ask."
  • Cut redundant tiles. Six single-value tiles that could be one table are six queries that could be one.
  • Don't auto-run. For filter-heavy exploratory dashboards, require filter selection before queries fire, so users don't pay for a default view nobody wanted.
  • Mind merged results and table calcs. Merged-results tiles run multiple queries and join them in Looker's backend; heavy table calculations post-process in memory. Both belong in the model (or the warehouse) once they're on a high-traffic dashboard.

Cause 7 — Instance-level contention

Symptom: everything is slower at specific times — Monday 9 a.m., the first of the month — regardless of which dashboard.

Confirm it: plot query runtimes and queue times from System Activity by hour of day. If runtimes are flat but total latency spikes at peak, you have contention: schedules, alerts, PDT builds, and human traffic all competing at once.

Fix it: spread the load. Re-sequence scheduled deliveries away from peak human hours (does the 9:00 a.m. email blast need to run at 9:00, or does 7:40 work?). Move PDT builds to the night window (cause 3). Check whether renderer-heavy schedules (PDFs, images) are stacking. On the warehouse side, this is where slot capacity and workload management matter: peak-hour BI deserves its own reservation so a heavy ELT job can't starve it.

Cause 8 — Nobody is watching

Symptom: performance problems surface as complaints, not alerts. Every incident is an investigation from scratch.

Confirm it: if this article is the first time anyone has opened System Activity in months, this cause applies to you by definition.

Fix it: treat dashboard performance like uptime. A small monitoring layer — System Activity dashboards tracking p90 load times for your key dashboards, PDT build durations, cache hit rates, and query cost per dashboard — plus alerts when any of them breaks baseline. This is how fixes stay fixed: every cause above regresses over time (new tiles get added, new joins creep in, data grows past design assumptions) unless something is measuring it. It's also the difference between "users say it feels slow lately" and "dashboard X regressed 40% after Tuesday's model change."

Two special cases worth knowing

Embedded dashboards. If Looker is embedded in your product, everything above applies with less margin: your customers won't tolerate load times your internal users grumble through. Two additions to the checklist — the embed authentication handshake adds its own latency before any query runs (measure it separately, or you'll optimize queries that aren't the bottleneck), and embedded traffic concentrates on a small set of dashboards, which makes them ideal candidates for aggregate tables and, where the workload fits, BI Engine. Sub-second embedded analytics is achievable; it just has to be designed, not hoped for.

First load vs repeat load. Users report "slow" as one feeling, but it's two different problems. If the first load of the day is slow and repeats are fast, your issue is cache warming and PDT freshness (causes 3 and 5) — consider scheduling a cache-priming query set before business hours. If every load is slow, the problem is structural (causes 1, 2, 4, 6). Asking users which pattern they see is the fastest triage question available, and it costs nothing.

The diagnostic, in one pass

When we audit an instance, the sequence looks like this — you can run it yourself in an afternoon:

  1. System Activity → History: rank dashboards by total runtime and views. Your problem is almost always in the top ten.
  2. For the worst dashboard: is it many tiles or one slow tile? Many → causes 5, 6, 7. One → keep going.
  3. For the slow tile: read the SQL. Big scan → causes 1, 2. Many joins / symmetric aggregates → cause 4.
  4. Check the PDT Event Log for anything the tile depends on → cause 3.
  5. Check the cache flag on repeat views → cause 5.
  6. Whatever you fix — put a metric on it → cause 8.

In practice, a slow instance has two or three of these compounding, and they share roots: the unpartitioned table (cause 1) is why the PDT takes hours to build (cause 3), which is why someone disabled caching to "see fresh data" (cause 5). That's also why fixing them together produces numbers that fixing them separately doesn't — in our production engagements, 40%+ faster dashboards and 30%+ lower BigQuery cost have come from the same six-to-eight-week effort, measured against the client's own baseline. The performance and cost optimization case study walks through one of those engagements end to end.

If you'd rather see your diagnosis than run it

Everything above is DIY-able, and we'd genuinely rather you fix it than live with it — the companion Looker audit checklist covers the rest of the environment. If you want the shortcut: our free audit is 15 minutes with read-only access — we look at System Activity and your query patterns and name the specific queries, PDTs, and Explores costing you the most time and money. If your instance is healthy, we say so, and you've spent 15 minutes confirming it.

Frequently asked questions about slow Looker dashboards

Why are my Looker dashboards so slow?

Slow Looker dashboards have specific causes, and it's rarely "Looker is slow." The most common: queries scanning unpartitioned warehouse tables, summary questions answered at detail grain (no aggregate awareness), PDTs that are stale or rebuilt inefficiently, Explores with more joins than the question needs, cache misconfiguration, too many tiles firing at once, and peak-hour contention. Looker's System Activity data tells you which combination applies to your instance.

How fast should a Looker dashboard load?

A reasonable production target is under 5 seconds for standard business dashboards, and sub-second for executive views backed by aggregate tables or BI Engine. If key dashboards routinely take 15–30+ seconds, users stop trusting and stop using them — adoption is usually the first casualty of slow dashboards, before anyone files a ticket.

How do I find which queries are slowing Looker down?

Use System Activity (Admin → System Activity, or the i__looker Explores). The History Explore shows runtime, queue time, and cache status per query and per dashboard; the PDT Event Log shows build times. Rank dashboards by total runtime and views — the top ten almost always contain the problem. Admin or see_system_activity permission is all you need.

What is a PDT and why does it make dashboards slow?

A PDT (persistent derived table) is a table Looker builds and stores in your warehouse from a query you define. PDTs slow dashboards down when they rebuild during business hours (users wait on the build), rebuild entire histories instead of processing incrementally, or chain into long sequential builds. The fixes are rescheduling, converting to incremental PDTs, deduplicating overlapping PDTs, and using materialized views for simple aggregations.

Does fixing slow dashboards also reduce BigQuery costs?

Usually, yes — they're mostly the same problem. The queries that make dashboards slow (full-table scans, detail-grain aggregations, redundant PDT rebuilds) are the queries inflating the bill. In our production engagements, the same optimization work has delivered 40%+ faster dashboards and 30%+ lower BigQuery cost, measured on the client's own billing.

Will performance optimization break my existing dashboards?

Not if it's done with Looker's own safeguards: Content Validator to catch broken references, development mode for every change before production, and data reconciliation to confirm numbers stay identical. Dashboards keep working — they just load faster. Any refactor that skips those steps is the thing that breaks dashboards, not optimization itself.

How many tiles should a Looker dashboard have?

There's no hard limit, but every tile is a query, and a 25-tile dashboard can fire 25 queries that queue against concurrency limits. In practice, dashboards built around one audience and one set of decisions rarely need more than 10–15 tiles. If a dashboard has grown beyond that, splitting it by audience usually improves both performance and usability.

Can Looker's caching really make that much difference?

Yes, when configured to match reality. If your data loads once a day but your datagroup expires the cache every hour, you're re-querying unchanged data 24 times daily — paying in both latency and warehouse cost. A sql_trigger datagroup tied to your actual load timestamp serves cached results all day and refreshes exactly when the data changes.

How long does Looker performance optimization take?

Quick wins — cache tuning, PDT rescheduling, killing obvious offenders — are visible within the first two weeks. Structural work (aggregate awareness, LookML refactoring, partitioning, incremental PDTs) lands its full impact within 6 to 8 weeks, measured against baseline load times and billing. Our free 15-minute audit names your specific offenders; the scoped performance audit ($1,500, one week) delivers the full prioritized plan.

Want your diagnosis, done for you?

Our free audit takes 15 minutes with read-only access. We look at System Activity and your query patterns and name the specific queries, PDTs, and Explores costing you the most time and money — and if your instance is healthy, we say so.

Martín Vélez

Martín Vélez

Founder @ RavenCoreX

Founder of RavenCoreX. Looker and Google Cloud specialist with 10+ years of experience in data architecture and Business Intelligence. Builds AI agents and SaaS products running in production for companies across the Americas.