The MySQL Performance Schema (P_S) is an instrumentation framework built into the server that records query execution times, lock waits, I/O waits, and memory usage at microsecond granularity — without the overhead of general query logging. For WordPress performance tuning, P_S provides the ground truth for questions like “which query spends the most time waiting on locks?” and “which table has the most row-level lock contention?”
Problem: A WordPress production database has intermittent slow queries but the slow log and EXPLAIN output do not reveal the root cause — the bottleneck is in wait events (lock waits, I/O waits, memory allocation) rather than query execution logic.
Solution: Enable the MySQL Performance Schema and query the events_statements_summary_by_digest table to see aggregated query statistics. Use events_waits_summary_global_by_event_name to identify the most time-consuming wait events across the server. Correlate specific slow queries with their wait events using the Statement Digest table and the events_stages_history_long table.
The queries below enable the most useful P_S consumers, find the top queries by total wait time, identify the tables with the most lock contention, profile a single query execution, and show how to reset P_S counters between test runs.
-- ── 1. Enable the key consumers (add to my.cnf for persistence) ──────────
UPDATE performance_schema.setup_consumers
SET enabled = 'YES'
WHERE name IN (
'events_statements_history_long',
'events_waits_history_long',
'events_stages_history_long'
);
UPDATE performance_schema.setup_instruments
SET enabled = 'YES', timed = 'YES'
WHERE name LIKE 'statement/%'
OR name LIKE 'wait/io/table/%'
OR name LIKE 'wait/lock/%';
-- ── 2. Top 20 queries by total execution time (slowest overall) ───────────
SELECT
DIGEST_TEXT AS query_pattern,
COUNT_STAR AS executions,
ROUND( SUM_TIMER_WAIT / 1e12, 3 ) AS total_sec,
ROUND( AVG_TIMER_WAIT / 1e9, 3 ) AS avg_ms,
ROUND( MAX_TIMER_WAIT / 1e9, 3 ) AS max_ms,
SUM_ROWS_EXAMINED AS rows_examined,
SUM_NO_INDEX_USED AS no_index_used
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;
-- ── 3. Tables with the most lock wait time (lock contention hotspots) ─────
SELECT
OBJECT_SCHEMA AS db,
OBJECT_NAME AS tbl,
COUNT_READ_WITH_SHARED_LOCKS AS shared_locks,
COUNT_WRITE AS writes,
ROUND( SUM_TIMER_WAIT / 1e12, 3 ) AS total_lock_sec
FROM performance_schema.table_lock_waits_summary_by_table
WHERE OBJECT_SCHEMA = 'helloadmin'
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
-- ── 4. I/O wait by table (which tables are causing disk reads) ────────────
SELECT
OBJECT_NAME AS tbl,
COUNT_READ AS reads,
COUNT_WRITE AS writes,
ROUND( SUM_TIMER_READ / 1e12, 3 ) AS read_sec,
ROUND( SUM_TIMER_WRITE / 1e12, 3 ) AS write_sec
FROM performance_schema.table_io_waits_summary_by_table
WHERE OBJECT_SCHEMA = 'helloadmin'
ORDER BY ( SUM_TIMER_READ + SUM_TIMER_WRITE ) DESC
LIMIT 10;
-- ── 5. Profile a single session: see per-stage breakdown ─────────────────
-- Step 1: In your session, run your query
-- Step 2: Find the EVENT_ID of the last statement
SELECT EVENT_ID, SQL_TEXT, TIMER_WAIT/1e9 AS ms
FROM performance_schema.events_statements_history
ORDER BY EVENT_ID DESC LIMIT 5;
-- Step 3: Show per-stage timing for that EVENT_ID
SELECT EVENT_NAME, TIMER_WAIT/1e9 AS stage_ms
FROM performance_schema.events_stages_history_long
WHERE NESTING_EVENT_ID =
ORDER BY EVENT_ID;
-- ── 6. Reset counters between test runs ──────────────────────────────────
TRUNCATE TABLE performance_schema.events_statements_summary_by_digest;
TRUNCATE TABLE performance_schema.table_io_waits_summary_by_table;
NOTE: Performance Schema instruments add 5–10% CPU overhead when all consumers are enabled — on production servers, enable only the consumers you actively need (events_statements_summary_by_digest is low-overhead and always useful) and disable events_waits_history_long and events_stages_history_long unless you are actively profiling a specific problem.