Skip to content

CONCEPT Cited by 1 source

Per-thread memory profiling

Per-thread memory profiling is the MySQL technique of measuring a running query's memory usage by sampling the thread running that query, because MySQL binds one query to one connection to one thread for the query's lifetime — so per-thread memory is per-query memory for the duration of the query.

(Source: sources/2026-04-21-planetscale-profiling-memory-usage-in-mysql.)

Why it's a substitute, not a first-class feature

MySQL's memory instrumentation does not expose a per-query grain (see concepts/memory-profiling-granularity). The five available grains are account / host / thread / user / global. Of these, thread is the only one that uniquely identifies a specific executing query — provided you know which thread the query is running on. The connection↔thread binding property makes this identification reliable for the duration of the query.

The canonical workflow

Four steps from the Dicken post:

  1. Open two connections — one for the query under test, one for monitoring. See patterns/two-connection-profiling-setup.
  2. On the monitored connection, capture the thread id:
SET @cid = (SELECT CONNECTION_ID());
SET @tid = (SELECT thread_id
             FROM performance_schema.threads
             WHERE PROCESSLIST_ID=@cid);
SELECT @cid, @tid;

See concepts/mysql-connection-id-vs-thread-id for the cid↔tid distinction. 3. Execute the query on the monitored connection. 4. On the monitoring connection, sample memory every N ms:

SELECT event_name, current_number_of_bytes_used
  FROM performance_schema.memory_summary_by_thread_by_event_name
  WHERE thread_id = <tid>
  ORDER BY current_number_of_bytes_used DESC LIMIT 4;

See patterns/periodic-sampling-memory-profiler.

Sampling vs total

Each sample is an instantaneous snapshot of current_number_of_bytes_used — not a time-integral, not a peak. Memory usage during query execution is non-monotonic (allocations + frees happen throughout), so:

  • A single sample mid-execution is one data point, not "the" memory cost of the query.
  • The post's running example shows the steady-state top categories (Filesort_buffer::sort_keys dominating at ~200 KiB for a 100K-row ORDER BY).
  • Short queries may complete between samples; the technique is practical only for multi-second (or longer) queries.

When it breaks down

  • Sub-second queries — the sampling loop never catches the query running, or catches it only once. Dicken flags this explicitly.
  • Cross-thread allocations — some MySQL subsystems allocate on a thread other than the connection's thread (background purge, replication). Per-thread sampling misses those.
  • Shared memory structures — the InnoDB buffer pool is global; allocations charged to a thread via memory/innodb/memory are the delta this thread caused, not the absolute buffer-pool size.
  • Thread re-use — after the query completes, the thread may handle other queries; continuing to sample the same thread_id will observe unrelated workloads.

Seen in

Last updated · 470 distilled / 1,213 read