Guide April 18, 2026 · 20 mins · The D23 Team

Apache Superset Performance Profiling: Where Slow Dashboards Come From

Learn to profile slow Apache Superset dashboards. Master query plans, caching strategies, and frontend tracing to diagnose and fix performance bottlenecks.

Apache Superset Performance Profiling: Where Slow Dashboards Come From

Understanding Dashboard Performance Bottlenecks

A slow dashboard isn’t always a slow query. That’s the first thing you need to internalize when you start profiling Apache Superset performance. Your users might be staring at a loading spinner because their browser is rendering 50,000 data points, or because the database is scanning a 2 billion-row table without an index, or because Redis is evicting cached results under memory pressure. The symptoms look identical from the frontend—but the root causes are completely different, and so are the fixes.

Apache Superset sits at the intersection of three performance domains: the database layer, the API and caching layer, and the frontend. A comprehensive profiling approach touches all three. When you’re debugging slow dashboards at scale, you need to know where to look first, what metrics matter, and how to read the evidence that Superset leaves behind.

This guide walks through the mechanics of Superset performance profiling, from understanding query execution plans to tracing frontend rendering, with practical steps you can run today. We’ll focus on the engineering reality: slow dashboards happen in production, they’re expensive to leave unfixed, and the diagnostic process is learnable.

The Three Layers of Performance: Database, API, Frontend

Superset dashboards execute across three distinct layers, and slowness can originate in any of them. Understanding which layer is the bottleneck is the first step in profiling.

Database Layer: Query Execution and Plan Analysis

The database layer is where most dashboard slowness originates. Your query might be well-written, but if it’s scanning the wrong indexes or forcing a full table scan, it will be slow regardless of how fast Superset’s API responds.

Every database—PostgreSQL, MySQL, BigQuery, Snowflake, Redshift—provides a query execution plan that shows you exactly how it intends to execute your query before it actually runs. In PostgreSQL and MySQL, you use EXPLAIN and EXPLAIN ANALYZE. In BigQuery and Snowflake, you get execution statistics in the query results or the console.

Here’s what a typical slow query plan looks like in PostgreSQL:

Seq Scan on orders (cost=0.00..450000.00 rows=10000000)
  Filter: (created_at > '2024-01-01')

That Seq Scan means the database is reading every single row in the orders table and then filtering. If your orders table has 10 million rows, that’s 10 million row reads just to apply a date filter. The cost estimate (450000.00) is the database’s guess at how expensive this operation will be.

Now compare that to an indexed query:

Index Only Scan using idx_orders_created_at on orders (cost=0.00..5000.00 rows=1000000)
  Index Cond: (created_at > '2024-01-01')

Same query, same data, but the database uses an index to jump directly to rows matching your date condition. The cost dropped from 450,000 to 5,000. That’s a 90x improvement, and it’s free—you just need the right index.

When you’re profiling a slow dashboard in Superset, your first move is to run EXPLAIN ANALYZE on each chart’s underlying query. This tells you:

  • Whether the query is doing a full table scan or using indexes
  • How many rows the database estimates vs. how many it actually reads
  • Which operations (joins, aggregations, sorts) are expensive
  • Whether the database’s statistics are stale (estimated rows ≠ actual rows)

You can run EXPLAIN ANALYZE directly in Superset’s SQL Lab by prepending it to your query. The execution plan appears in the results, along with the actual runtime.

API and Caching Layer: Request Latency and Cache Hit Rates

Once the database finishes executing the query, Superset’s API serializes the results, applies any post-processing (formatting, sorting), and returns them to the frontend. This layer also includes caching—Redis by default—which can dramatically reduce database load if configured correctly.

The API layer is where you measure:

  • Query latency: How long does the database take to return results?
  • API response time: How long from when Superset receives the request to when it returns a response?
  • Cache hit rate: What percentage of queries are served from cache vs. hitting the database?

Superset logs all of this. If you’re running Superset in production, you should be collecting these metrics. When a dashboard is slow, you need to know: Is the query actually slow, or is it just not cached?

A properly configured Superset instance with Redis caching should show cache hit rates of 70-90% for typical dashboards. If your hit rate is below 50%, you either have a caching configuration issue or your queries are too dynamic (different filter values on every load) to benefit from caching.

You can inspect cache performance by:

  1. Checking Redis directly: redis-cli info stats shows evictions, hits, and misses
  2. Reviewing Superset’s application logs for cache hit/miss indicators
  3. Using Superset’s built-in metrics (if you have the metrics database configured) to track query performance over time

The caching layer is also where timeouts happen. If a query takes longer than your configured timeout (default is 30 seconds in Superset), the API will cancel it and return an error. This is often the first sign that your database queries need optimization.

Frontend Layer: Rendering and Data Volume

The frontend is the browser. After Superset’s API returns data, the browser has to render it. This is where most teams overlook performance problems.

A common mistake: returning 100,000 rows to the frontend because “the user might want to see all the data.” Browsers struggle with rendering datasets that large. The JavaScript charting library (Echarts, by default in Superset) has to create DOM elements or canvas objects for each data point, and modern browsers will slow to a crawl.

Frontend slowness shows up as:

  • A long delay between when the API returns data (you can see this in network tab) and when the chart actually appears
  • Unresponsive UI when you try to interact with the dashboard (filtering, drilling down, exporting)
  • High CPU usage on the user’s machine

You can diagnose frontend performance using your browser’s Developer Tools:

  1. Open the Network tab and reload the dashboard
  2. Look for the API request that returns the chart data (usually a POST to /api/v1/chart/data)
  3. Note the response size and the time it takes
  4. Switch to the Performance tab and record a profile while the chart renders
  5. Look for long tasks and slow paint operations

If the API returns in 500ms but the chart doesn’t appear for 3 seconds, you have a frontend rendering problem, not a database problem. The fix is different: limit the number of rows returned, aggregate data server-side, or switch to a more efficient visualization (a number card instead of a 100k-row table).

Profiling Query Execution Plans

Query plans are the most direct window into database performance. They tell you exactly what the database is doing and how expensive it is.

Reading a Query Plan

Let’s walk through a real example. Here’s a query that’s likely slow:

SELECT 
  user_id, 
  COUNT(*) as order_count,
  SUM(amount) as total_spent
FROM orders
WHERE created_at > '2024-01-01'
GROUP BY user_id
ORDER BY total_spent DESC
LIMIT 100;

And here’s what the plan might look like (PostgreSQL):

Limit (cost=450000.00..450000.10 rows=100)
  -> Sort (cost=450000.00..460000.00 rows=400000)
        Sort Key: (sum(amount)) DESC
        -> GroupAggregate (cost=0.00..400000.00 rows=400000)
              -> Seq Scan on orders (cost=0.00..350000.00 rows=10000000)
                    Filter: (created_at > '2024-01-01')

Reading from the bottom up (that’s how the database executes it):

  1. Seq Scan on orders: The database reads all 10 million rows from the orders table
  2. Filter: It applies the date filter, leaving ~400,000 rows
  3. GroupAggregate: It groups by user_id and calculates COUNT and SUM
  4. Sort: It sorts by total_spent in descending order
  5. Limit: It returns the top 100 rows

The total cost is 450,000 units. That’s expensive because of the sequential scan—the database has to read all 10 million rows just to find the ones after 2024-01-01.

Now, if you add an index on created_at:

CREATE INDEX idx_orders_created_at ON orders(created_at);

The plan changes:

Limit (cost=45000.00..45000.10 rows=100)
  -> Sort (cost=45000.00..55000.00 rows=400000)
        Sort Key: (sum(amount)) DESC
        -> GroupAggregate (cost=0.00..40000.00 rows=400000)
              -> Index Scan using idx_orders_created_at on orders (cost=0.00..35000.00 rows=10000000)
                    Index Cond: (created_at > '2024-01-01')

The cost dropped from 450,000 to 45,000—a 10x improvement. The database now uses the index to jump to the first row matching your condition, rather than reading all 10 million rows.

When you’re profiling slow dashboards, look for these red flags in query plans:

  • Seq Scan on large tables: If you’re scanning a table with millions of rows without an index, that’s slow. Add an index on your filter columns.
  • Hash Join or Nested Loop on large result sets: Joins can be expensive, especially if they’re joining large tables without indexes on the join keys.
  • Sort on large result sets: Sorting is expensive. If you’re sorting millions of rows, consider aggregating or limiting earlier.
  • Estimated rows ≠ Actual rows: If the database estimates 1000 rows but actually reads 100,000, its statistics are stale. Run ANALYZE on the table.

Using EXPLAIN ANALYZE in Superset

In Superset’s SQL Lab, you can prepend EXPLAIN ANALYZE to any query to see the actual execution plan and runtime:

EXPLAIN ANALYZE
SELECT ...

Superset will run the query and show you the plan alongside the actual timing. This is invaluable for profiling. You can see not just the estimated cost, but the actual time each operation took.

When you’re optimizing a dashboard, run EXPLAIN ANALYZE on each chart’s query. Identify the expensive operations and fix them:

  • Missing indexes? Add them.
  • Stale statistics? Run ANALYZE on the table.
  • Inefficient joins? Rewrite the query to use better join conditions.
  • Too much data? Add a LIMIT or aggregate earlier.

Caching Strategies and Configuration

Caching is the most effective way to improve dashboard performance at scale. A well-configured cache can reduce database load by 80-90% and make dashboards feel instant.

Superset uses Redis for caching by default. The cache stores:

  • Query results (the actual data returned by the database)
  • Metadata (table and column information)
  • Computed metrics

Cache Key Generation

Superset generates cache keys based on the query, filters, and parameters. Two requests with identical queries and filters will hit the same cache entry. Two requests with different filters will miss.

This is important to understand: if your dashboard has filters that change on every load (like “last 24 hours”), the cache will rarely hit because the query changes every time. If your filters are static (like “region = US”), the cache will hit almost every time.

You can check cache hit rates by monitoring Redis:

redis-cli INFO stats

Look for keyspace_hits and keyspace_misses. The hit rate is hits / (hits + misses). If it’s below 50%, you either have:

  1. Too many dynamic queries (different filters on every load)
  2. Cache evictions due to memory pressure
  3. Cache TTL set too low (results expire before they can be reused)

Configuring Cache TTL and Size

In Superset, you configure caching in superset_config.py:

CACHE_CONFIG = {
    'CACHE_TYPE': 'redis',
    'CACHE_REDIS_URL': 'redis://localhost:6379/0',
    'CACHE_DEFAULT_TIMEOUT': 3600,  # 1 hour
}

DATA_CACHE_CONFIG = {
    'CACHE_TYPE': 'redis',
    'CACHE_REDIS_URL': 'redis://localhost:6379/1',
    'CACHE_DEFAULT_TIMEOUT': 3600,  # 1 hour
}

The CACHE_DEFAULT_TIMEOUT is how long results stay in cache before expiring. A longer timeout means better cache hit rates but staler data. A shorter timeout means fresher data but more database hits.

For most dashboards, 1 hour is a good default. For real-time dashboards, you might use 5-10 minutes. For executive dashboards that don’t need to be current, you might use 24 hours.

You also need to ensure your Redis instance has enough memory. If Redis runs out of memory, it will evict the oldest entries (by default, using LRU—Least Recently Used). This defeats the purpose of caching.

Monitor Redis memory usage:

redis-cli INFO memory

If you’re seeing high eviction rates, either increase Redis memory or reduce the number of unique queries (by consolidating dashboards or limiting filters).

Cache Invalidation

Cache invalidation is the hard problem. When data in your database changes, the cache becomes stale. Superset has a few strategies:

  1. Time-based expiration: Results expire after a set time (the TTL). Simple but can serve stale data.
  2. Manual invalidation: You manually clear the cache when data changes. Reliable but operationally expensive.
  3. Dependency tracking: You track which tables a query depends on and invalidate the cache when those tables change. Complex but optimal.

For most teams, time-based expiration is sufficient. Set the TTL to match your data freshness requirements.

Frontend Performance and Data Volume Limits

The frontend is often overlooked in performance discussions, but it’s critical. A query that returns 100,000 rows will be slow to render, regardless of how fast the database is.

Limiting Row Count

In Superset, every chart has a Row limit setting. This limits the number of rows returned to the frontend. The default is 10,000, which is usually too high for interactive dashboards.

When you’re profiling a slow dashboard, check the row limit on each chart:

  1. Open the chart in edit mode
  2. Go to the Query section
  3. Look for the Row limit field
  4. If it’s blank or very high (>5000), lower it

For most use cases:

  • Line charts: 1000-2000 rows (one per time period)
  • Bar charts: 50-500 rows (one per category)
  • Tables: 100-1000 rows (depends on use case)
  • Number cards: 1 row (just an aggregate)

Lowering the row limit has two benefits:

  1. The database returns fewer rows (faster)
  2. The browser renders fewer data points (faster)

It’s a win-win.

Aggregation and Pre-computation

When you need to show a lot of data, aggregate it server-side instead of returning raw rows.

For example, instead of returning every order:

SELECT order_id, user_id, amount, created_at FROM orders

Aggregate by day:

SELECT DATE(created_at) as date, COUNT(*) as order_count, SUM(amount) as total
FROM orders
GROUP BY DATE(created_at)

The second query returns 365 rows (one per day) instead of millions. It’s much faster to execute and render.

For complex aggregations that are used across multiple dashboards, consider materializing them:

CREATE MATERIALIZED VIEW daily_orders AS
SELECT DATE(created_at) as date, COUNT(*) as order_count, SUM(amount) as total
FROM orders
GROUP BY DATE(created_at);

Materialized views are pre-computed and stored on disk. Querying a materialized view is much faster than computing the aggregation on-the-fly, especially if the underlying table is large.

As referenced in the Preset guide on optimizing database dashboard performance, materialized views and partitioning are among the most effective strategies for scaling dashboard performance.

Frontend Rendering Optimization

Once the data reaches the frontend, the browser has to render it. You can optimize this by:

  1. Choosing the right visualization: A number card is faster than a table. A line chart is faster than a scatter plot with 10,000 points.
  2. Enabling data sampling: For very large datasets, Superset can sample the data before rendering. This makes the chart appear faster (though less accurate).
  3. Using a faster charting library: Superset uses Echarts by default, which is good for most cases. For very large datasets, consider switching to a more efficient library.

You can check frontend rendering performance using your browser’s Developer Tools. Open the Performance tab, record while the chart renders, and look for long tasks and slow paint operations.

Profiling Tools and Techniques

Superset and its supporting infrastructure provide several tools for profiling performance.

Superset Logs and Metrics

Superset logs all queries and their execution times. If you have the metrics database configured, you can query this data directly:

SELECT 
  query_id,
  sql,
  execution_time,
  rows,
  cache_hit
FROM query_log
WHERE execution_time > 5  -- queries slower than 5 seconds
ORDER BY execution_time DESC;

This tells you which queries are slowest and how often they hit cache. You can use this to prioritize optimization efforts.

Database-Level Monitoring

Your database also has profiling tools. PostgreSQL has pg_stat_statements, which tracks query performance:

SELECT 
  query,
  calls,
  mean_time,
  max_time
FROM pg_stat_statements
WHERE mean_time > 1000  -- queries slower than 1 second on average
ORDER BY mean_time DESC;

This shows you which queries are slowest across all applications, not just Superset. It’s invaluable for understanding database performance.

BigQuery and Snowflake have similar tools (BigQuery’s query history and Snowflake’s query performance views).

Browser DevTools

For frontend performance, use your browser’s built-in tools:

  1. Network tab: See how long API requests take and how large the responses are
  2. Performance tab: Record a profile and see where time is spent
  3. Console: Check for JavaScript errors that might slow down rendering

When you record a performance profile, look for:

  • Long tasks: JavaScript execution that blocks the main thread
  • Slow paint: Rendering operations that take a long time
  • Network requests: API calls that are slow or large

Common Performance Bottlenecks and Fixes

Based on the Apache Superset FAQ on performance issues, here are the most common bottlenecks and how to fix them:

Missing Indexes

Symptom: Queries are slow, query plans show sequential scans on large tables

Fix: Add indexes on columns used in WHERE clauses and JOIN conditions

CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_created_at ON orders(created_at);

Stale Table Statistics

Symptom: Query plans show estimated rows that don’t match actual rows

Fix: Run ANALYZE on the table

ANALYZE orders;

Too Many Rows Returned

Symptom: API response is large (>10MB), frontend rendering is slow

Fix: Lower the row limit on the chart, or aggregate data server-side

Query Timeout

Symptom: Queries fail with a timeout error

Fix: Optimize the query (add indexes, rewrite joins), or increase the timeout

As noted in the guide on handling Superset timeouts, timeouts can be adjusted in superset_config.py:

SQLLAB_QUERY_TIMEOUT = 300  # 5 minutes

Low Cache Hit Rate

Symptom: Most queries hit the database, dashboard is slow

Fix: Increase cache TTL, consolidate dashboards to reuse queries, or check Redis memory

Inefficient Joins

Symptom: Query plans show nested loops or hash joins on large result sets

Fix: Rewrite the query to use better join conditions, or add indexes on join keys

Advanced Profiling: Async Queries and Query Queuing

For very slow queries (>30 seconds), Superset can run them asynchronously. This prevents the API from timing out and allows the user to check back later for results.

Enable async queries in superset_config.py:

SUPERSET_SQLLAB_ASYNC_TIME_LIMIT_SEC = 300  # 5 minutes
SUPERSET_CELERY_ENABLED = True

With async queries enabled, long-running queries are queued and executed in the background. The user sees a loading state and can check back for results.

This doesn’t make the queries faster—it just makes the user experience better. For actual performance improvement, you still need to optimize the queries themselves.

You can monitor async query performance by checking your Celery queue:

celery -A superset.tasks inspect active

This shows you which queries are currently running and how long they’ve been executing.

Profiling in Production: Monitoring and Alerting

Profiling isn’t a one-time activity. You need continuous monitoring to catch performance regressions early.

Set up monitoring for:

  1. Query execution time: Alert if average query time exceeds a threshold
  2. Cache hit rate: Alert if hit rate drops below 50%
  3. API response time: Alert if response time exceeds 2 seconds
  4. Database connection pool: Alert if connections are exhausted
  5. Redis memory: Alert if usage exceeds 80%

You can collect these metrics using Superset’s metrics database, or by integrating with a monitoring tool like Prometheus or Datadog.

When you get an alert, follow the profiling process outlined in this guide:

  1. Check the query plan (is the database slow?)
  2. Check the cache hit rate (is caching working?)
  3. Check the API response time (is the API slow?)
  4. Check the frontend rendering (is the browser slow?)

Once you identify the bottleneck, apply the appropriate fix.

Bringing It Together: A Profiling Workflow

Here’s the step-by-step workflow for profiling a slow dashboard:

Step 1: Identify the slow chart

  • Open the dashboard in your browser
  • Open the Network tab in DevTools
  • Reload the dashboard
  • Look for the API request that takes the longest (POST to /api/v1/chart/data)
  • Note the chart ID and the response time

Step 2: Check the query plan

  • Open SQL Lab
  • Find the query for the slow chart
  • Prepend EXPLAIN ANALYZE and run it
  • Look for sequential scans, expensive joins, or sort operations
  • Check if the estimated rows match the actual rows

Step 3: Check the cache

  • In Superset, look at the chart’s cache settings
  • Check the cache hit rate in Redis: redis-cli INFO stats
  • If hit rate is low, check if the query is too dynamic (filters change on every load)

Step 4: Check the frontend

  • In DevTools, look at the Performance profile
  • See if the chart renders quickly after the API returns data
  • If rendering is slow, check the row limit on the chart

Step 5: Apply fixes

  • If the query is slow: add indexes, rewrite the query, or aggregate server-side
  • If caching is ineffective: increase TTL, consolidate queries, or reduce filter dynamism
  • If frontend is slow: lower row limit, simplify visualization, or aggregate data

Step 6: Verify the fix

  • Reload the dashboard
  • Check the Network tab to confirm API response time is lower
  • Check the Performance profile to confirm rendering is faster
  • Monitor in production to ensure the fix persists

Optimization at Scale: D23’s Approach

When you’re managing Apache Superset at scale—across multiple teams, hundreds of dashboards, and billions of rows of data—performance profiling becomes a systematic practice, not a one-off troubleshooting exercise.

At D23, we’ve built performance profiling into the managed Superset experience. Our platform includes:

  • Automated query analysis: We profile every query and alert you to performance issues before users notice
  • Intelligent caching: We optimize cache configuration based on your query patterns and data freshness requirements
  • Database optimization consulting: We work with your data and engineering teams to add indexes, materialize views, and restructure queries for performance
  • Frontend performance monitoring: We track rendering performance and recommend visualization changes when needed

Our managed Apache Superset platform handles the infrastructure so your team can focus on dashboards, not DevOps. We integrate with your database, set up Redis caching, configure async queries, and monitor performance 24/7.

For teams evaluating managed Superset as an alternative to Looker, Tableau, or Power BI, performance is often the deciding factor. Superset’s open-source foundation means you get the flexibility and cost efficiency of open source, plus the managed infrastructure and expert support of a commercial platform.

Summary: Key Takeaways

Slow Superset dashboards come from three places: the database, the API/caching layer, or the frontend. Effective profiling requires understanding all three:

  1. Database layer: Use EXPLAIN ANALYZE to read query plans. Look for sequential scans, missing indexes, and stale statistics. Add indexes and aggregate data server-side.

  2. API and caching layer: Monitor cache hit rates and query latency. Ensure Redis has enough memory and cache TTL is appropriate. Use async queries for very slow queries.

  3. Frontend layer: Limit row count, aggregate data before returning to the browser, and monitor rendering performance in DevTools.

Start with the database—that’s where most slowness originates. Then move to caching and frontend optimization. Use the tools and techniques in this guide to diagnose performance issues quickly and apply targeted fixes.

As you scale Superset across your organization, make profiling a continuous practice. Monitor query performance, cache hit rates, and frontend rendering. Alert on regressions. Build a culture where performance is measured and optimized.

For teams managing Superset in production, the Apache Superset community and the GitHub performance discussions are valuable resources. You’ll find detailed guidance on optimizing Superset for performance and scalability, including load balancing, Redis tuning, and Gunicorn configuration.

If you’re looking to scale Superset without the operational burden, D23’s managed Superset platform provides the infrastructure, caching, database optimization, and expert consulting needed to run production-grade analytics at scale. We handle performance profiling, optimization, and monitoring so your team can focus on building dashboards and driving insights.