DEV Community

Cover image for MySQL as a bottleneck in real-time WebSocket dashboards
turboline-ai
turboline-ai

Posted on

MySQL as a bottleneck in real-time WebSocket dashboards

When Your Real-Time Dashboard Isn't Actually Real-Time

FastAPI plus WebSockets is a genuinely satisfying stack to build with. The async primitives feel natural, the developer experience is tight, and getting a live dashboard running feels fast. Until you look at what's sitting behind your WebSocket handler.

Async MySQL.

That part deserves more scrutiny than it usually gets.

The Problem With Relational Databases in High-Frequency Pipelines

MySQL, even async MySQL, was designed for a workload where you write a record, read it back, maybe join it to something else. That's a great model for a lot of things. It's not a great model when you're pushing tick-level data, event streams, or anything where the write rate climbs above a few hundred rows per second.

The issue isn't the database itself. It's the impedance mismatch between how relational databases think about data (rows, transactions, consistency guarantees) and how real-time streams work (ordered sequences of timestamped events, high write volume, time-range queries).

When your WebSocket handler is blocking on a MySQL query under load, the "real-time" part of your dashboard is already a lie. You're serving the last thing the database could keep up with, not the last thing that actually happened.

What Actually Breaks First

It's usually not the WebSocket layer. FastAPI's async WebSocket handling is solid. The connection management, the broadcast pattern, the lifecycle hooks, all of that works.

What breaks is the read path under concurrent clients. Here's why:

  • MySQL has connection pool limits. Each async query is still consuming a connection from that pool.
  • Reads compete with writes. High-frequency inserts mean your SELECT queries are hitting a table that's being written to constantly, lock contention becomes real.
  • Aggregation queries don't scale linearly. If your dashboard needs rolling averages or windowed stats, those are expensive to compute on the fly from a row-based store.

A dashboard that feels fine with one browser tab open starts drifting from "real-time" when ten people are watching it simultaneously.

What the Architecture Actually Needs

The row store is the wrong layer to be querying in your WebSocket handler. The data needs to move through something designed for this before it reaches MySQL.

A typical pattern that holds up better:

  1. Events land in a stream (Kafka, Redpanda, or a similar log-structured store).
  2. A consumer maintains a materialized, in-memory view of current state.
  3. Your WebSocket handler reads from that materialized view, not the database.
  4. MySQL (or whatever your persistence layer is) gets written to asynchronously, for historical queries and audit, not for live reads.

This separates the hot read path from the write path entirely. The dashboard never waits on a database write to complete before pushing an update.

The "Async" Qualifier Does Less Than It Looks Like

aiomysql or asyncmy make your database calls non-blocking at the Python event loop level. That's real and useful, you're not blocking other coroutines while waiting on I/O.

But async doesn't change what the database is doing on its end. The query still takes the same amount of time. The lock contention still exists. The connection pool is still finite.

Async I/O solves a different problem than throughput. It's great for keeping your server responsive under concurrent connections. It's not a fix for a data layer that isn't built for streaming write volumes.

When the Tutorial Approach Is Fine

To be clear: the FastAPI + WebSockets + async MySQL pattern is completely reasonable for a lot of real dashboards. If your update frequency is measured in seconds, not milliseconds. If you have a small number of concurrent viewers. If the data is transactional by nature and doesn't spike.

The problem is when teams take that pattern and scale it into genuinely high-frequency territory without changing the architecture. The dashboard appears to work, the WebSocket connection stays open, but the latency creeps up and the data starts lagging, and it's not obvious where the bottleneck is.

The Honest Takeaway

Real-time dashboards are mostly a data architecture problem, not a transport protocol problem. WebSockets solve how data moves from server to browser. They don't solve how quickly fresh data reaches your server in the first place.

If you're building something where the freshness of the data actually matters, trading, live ops, crypto analytics, anything where a five-second lag has consequences, the database choice and where in your pipeline reads happen is worth getting right early. Retrofitting the data layer is significantly more painful than getting it right from the start.

Top comments (0)