When MariaDB high CPU appears on a dashboard, the percentage is only the beginning of the diagnosis. A process showing 800% CPU on Linux may simply be using eight cores efficiently. A host showing 100% can be genuinely saturated, constrained to two container CPUs on a 32-core node, delayed by hypervisor steal time, or reporting high load from uninterruptible I/O rather than computation. Restarting MariaDB may reduce the graph briefly while destroying the evidence and warming every cache again.
The database-side question is equally nuanced. Performance Schema can identify query digests with the most cumulative statement time, rows examined, sorts, and temporary tables, but its timer is elapsed wait time rather than direct CPU accounting. A query can rank high because it waits on locks or storage. Conversely, millions of individually fast statements can burn cores while none crosses the slow-log threshold.
This guide builds a production incident workflow from host confirmation to lasting correction. It separates CPU from I/O and throttling, captures the workload safely, maps operating-system threads to MariaDB activity, ranks normalized query families, inspects execution plans, mitigates overload without creating a retry storm, evaluates thread pooling, and verifies the result across reads, writes, replicas, and service latency.
Define what “high CPU” means for this server
Start with capacity and service objectives:
- How many logical CPUs can MariaDB actually use?
- Is the process in a container, VM, or shared host?
- What is normal busy-hour utilization?
- Are request latency and error budgets affected?
- Is throughput increasing proportionally with CPU?
- Is saturation brief, sustained, periodic, or new after a deploy?
- Are replicas or cluster nodes showing the same pattern?
A database using available CPU with stable latency can be healthy. High CPU becomes an incident when it coincides with queue growth, latency, errors, missed jobs, replica lag, throttling, or insufficient headroom for failover.
Record UTC start time, server identity, topology role, application releases, schema changes, traffic rate, batch schedule, and alerts. Without context, a later fix can be incorrectly credited to a traffic decline.
Confirm the CPU scope at the operating-system layer
Identify the server process:
systemctl show mariadb --property=MainPID --value
pidof mariadbd
If multiple instances run on one host, do not assume pidof returns one PID. Map PID to command line and data directory:
ps -o pid,ppid,user,etimes,pcpu,pmem,args -C mariadbd
Capture system-wide CPU composition:
mpstat -P ALL 1 10
vmstat 1 10
Interpret categories:
usr: user-space computation, where SQL evaluation and engine work commonly appear.sys: kernel work such as networking, filesystem, and scheduling.iowait: CPU idle while outstanding I/O exists; not computational saturation.steal: a VM waiting because the hypervisor scheduled another guest.- idle: unused logical capacity.
High load average does not equal high CPU. Linux load includes runnable tasks and tasks in uninterruptible sleep, often storage waits. Compare run queue, per-core utilization, I/O, and latency.
Sample the MariaDB process and threads:
pidstat -u -t -p "$(systemctl show mariadb --property=MainPID --value)" 1 10
top -H -p "$(systemctl show mariadb --property=MainPID --value)"
Do not paste a PID into automation without validating it is nonzero and belongs to the intended instance. Tool availability and column names vary by distribution.
Check whether another process is responsible:
ps -eo pid,ppid,user,stat,pcpu,pmem,comm,args --sort=-pcpu | head -n 30
A backup compressor, monitoring agent, antivirus scanner, log processor, or colocated application can consume cores and make MariaDB a victim rather than the source.
Check containers, cgroups, and virtual machines
Inside a container, visible host CPUs may exceed the workload's quota. On cgroup v2:
cat /sys/fs/cgroup/cpu.max
cat /sys/fs/cgroup/cpu.stat
cat /sys/fs/cgroup/cpuset.cpus.effective 2>/dev/null || true
cpu.max reports quota and period, or max when no quota applies. cpu.stat includes throttling counters on supported kernels. Capture interval deltas for nr_throttled and throttled_usec; lifetime totals alone do not prove a current problem.
In Kubernetes, inspect pod requests, limits, throttling, node pressure, and actual placement through the cluster's monitoring. A pod limited to two CPUs can be saturated while node utilization remains low. Raising the limit may help only if the node has spare capacity and the query workload scales.
For virtual machines, high steal time indicates host contention. Database tuning cannot recover CPU cycles the hypervisor does not schedule. Escalate with synchronized evidence from guest and platform metrics.
NUMA topology can also influence large servers:
lscpu
numactl --hardware 2>/dev/null || true
Do not change CPU affinity, NUMA policy, or IRQ placement during an incident based on generic advice. These changes require platform-specific benchmarking and restart planning.
Exclude I/O and memory pressure masquerading as CPU trouble
Collect storage behavior:
iostat -xz 1 10
Look at device latency, queue depth, utilization, and throughput. A query stalled on storage can create high load with modest user CPU. A very fast storage device can show high utilization while still meeting latency, so avoid judging one percentage.
Check memory and swapping:
free -h
vmstat 1 10
cat /proc/pressure/cpu
cat /proc/pressure/io
cat /proc/pressure/memory
Linux Pressure Stall Information may be unavailable or restricted. Memory reclaim and swap can increase system CPU and latency. If the server is near OOM, treat memory as a parallel incident rather than applying CPU-only fixes.
Inspect kernel and service events:
journalctl -u mariadb --since '-30 minutes' --no-pager
journalctl -k --since '-30 minutes' --no-pager
Search for throttling, OOM, filesystem, device, and MariaDB errors. Protect logs because SQL and identifiers can be sensitive.
Capture MariaDB concurrency and throughput
Query identity and key runtime values:
SELECT VERSION(), @@version_comment, NOW(6), @@GLOBAL.server_id;
SHOW GLOBAL VARIABLES WHERE Variable_name IN (
'thread_handling',
'max_connections',
'performance_schema',
'userstat',
'slow_query_log',
'long_query_time'
);
Capture counters:
SHOW GLOBAL STATUS WHERE Variable_name IN (
'Threads_connected',
'Threads_running',
'Max_used_connections',
'Questions',
'Queries',
'Com_select',
'Com_insert',
'Com_update',
'Com_delete',
'Slow_queries',
'Uptime'
);
Threads_connected includes idle sessions. Threads_running is closer to concurrent work, though “running” can include threads in server states that are not continuously executing CPU instructions. Compare its interval behavior with runnable OS threads and latency.
Compute rates from two samples or monitoring:
queries_per_second = delta(Questions) / elapsed_seconds
cpu_per_query = delta(process_cpu_seconds) / delta(Questions)
The second ratio is a rough workload indicator. Administrative statements, internal work, parallel background activity, and changing query mix limit its precision. Still, it helps distinguish “more traffic” from “more CPU per unit of traffic.”
Do not run FLUSH STATUS on a shared production server just to simplify arithmetic; it destroys context used by other observers.
Inspect current sessions without guessing
Capture full process activity:
SHOW FULL PROCESSLIST;
For sortable output:
SELECT ID,
USER,
HOST,
DB,
COMMAND,
TIME,
STATE,
LEFT(INFO, 1000) AS sql_text
FROM information_schema.PROCESSLIST
WHERE COMMAND <> 'Sleep'
ORDER BY TIME DESC;
Questions to ask:
- Are many sessions executing the same query shape?
- Is one query long-running or are thousands of short queries cycling?
- Are states CPU-like, storage-related, lock-related, sorting, or temporary-table work?
- Did an event scheduler, stored routine, migration, or replica task start?
- Is one application user or host dominant?
Process list is a point-in-time sample. Short CPU-heavy statements may finish between snapshots. Take several bounded snapshots or use digest aggregation.
SQL text can expose credentials and personal data. Store incident captures securely and redact before sharing.
Rank query families with Performance Schema
Check Performance Schema and statement consumers:
SELECT @@performance_schema;
SELECT NAME, ENABLED
FROM performance_schema.setup_consumers
WHERE NAME LIKE 'events_statements%';
Inspect the digest table schema before selecting fields:
DESCRIBE performance_schema.events_statements_summary_by_digest;
On supported versions, rank normalized query shapes by cumulative statement time:
SELECT SCHEMA_NAME,
DIGEST,
COUNT_STAR,
ROUND(SUM_TIMER_WAIT / 1000000000000, 3) AS total_seconds,
ROUND(AVG_TIMER_WAIT / 1000000000, 3) AS avg_ms,
SUM_ROWS_EXAMINED,
SUM_ROWS_SENT,
SUM_CREATED_TMP_DISK_TABLES,
SUM_SORT_ROWS,
SUM_NO_INDEX_USED,
FIRST_SEEN,
LAST_SEEN,
LEFT(DIGEST_TEXT, 500) AS digest_text
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST IS NOT NULL
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 30;
Timer units in Performance Schema are picoseconds for these common statement fields, but validate the installed version. More importantly, SUM_TIMER_WAIT is statement elapsed/wait time, not direct CPU time. A digest can rank high because of locks or I/O. Correlate it with rows, plans, OS CPU, wait states, and storage.
Run several rankings:
- Total time: aggregate workload impact.
- Execution count: chatty and N+1 patterns.
- Rows examined: scanning work.
- Average/max time: outliers and skew.
- Temporary disk tables and sort rows.
- Lock time and errors.
Check the NULL digest bucket:
SELECT COUNT_STAR, SUM_TIMER_WAIT
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST IS NULL;
When performance_schema_digests_size fills, additional digest events aggregate into a NULL row. A large NULL count weakens attribution. The size is a startup design decision; do not restart mid-incident solely to increase it.
Measure interval changes without erasing shared evidence
Digest totals span their collection lifetime. Instead of truncating a shared summary, capture two snapshots into an external monitoring system or secure files.
install -d -m 0750 /var/lib/db-diagnostics/high-cpu-2026-08-25
mariadb --batch --raw -e "
SELECT NOW(6), SCHEMA_NAME, DIGEST, COUNT_STAR,
SUM_TIMER_WAIT, SUM_ROWS_EXAMINED, SUM_ROWS_SENT,
SUM_CREATED_TMP_DISK_TABLES, SUM_SORT_ROWS,
LEFT(DIGEST_TEXT,500)
FROM performance_schema.events_statements_summary_by_digest;
" > /var/lib/db-diagnostics/high-cpu-2026-08-25/digest-before.tsv
Repeat after a representative interval and calculate deltas offline. Restrict the directory because digest text can reveal schema and business logic.
Do not TRUNCATE performance_schema.events_statements_summary_by_digest unless every observer agrees. It resets evidence for monitoring and other investigations.
Attribute activity by account or host
Performance Schema includes statement summaries by user, host, and account where enabled. Inspect available tables:
SHOW TABLES FROM performance_schema
LIKE 'events_statements_summary_by%';
Then inspect columns and rank total wait, counts, and errors. Attribution can reveal a new application deployment or abusive reporting account even when query text varies.
MariaDB also offers User Statistics. It is disabled by default to avoid collection overhead:
SHOW GLOBAL VARIABLES LIKE 'userstat';
When deliberately enabled and observed over an interval:
SET GLOBAL userstat = ON;
SELECT USER,
TOTAL_CONNECTIONS,
CONCURRENT_CONNECTIONS,
BUSY_TIME,
CPU_TIME,
ROWS_READ,
ROWS_SENT,
ROWS_INSERTED,
ROWS_UPDATED,
ROWS_DELETED
FROM information_schema.USER_STATISTICS
ORDER BY CPU_TIME DESC;
Column names should be verified. CPU_TIME requires userstat=ON; historical values do not appear retroactively. Enabling collection changes the observed system slightly, so test overhead and restore the previous state after a bounded investigation.
Account-level CPU is useful attribution but may not include every engine background cost. Do not treat it as a perfect reconciliation to process CPU.
Correlate hot OS threads with MariaDB connections
Linux thread tools show thread IDs (TIDs), while MariaDB exposes connection IDs and Performance Schema thread IDs. Mapping depends on version and instrumentation.
Inspect Performance Schema threads:
DESCRIBE performance_schema.threads;
SELECT THREAD_ID,
PROCESSLIST_ID,
PROCESSLIST_USER,
PROCESSLIST_HOST,
PROCESSLIST_DB,
PROCESSLIST_COMMAND,
PROCESSLIST_TIME,
PROCESSLIST_STATE,
LEFT(PROCESSLIST_INFO, 500) AS processlist_info
FROM performance_schema.threads
WHERE PROCESSLIST_ID IS NOT NULL;
Some releases expose an operating-system thread identifier in this or related instrumentation; feature-detect it. Do not assume the MariaDB connection ID equals Linux TID.
If direct mapping is unavailable, correlate repeated OS thread samples with events_statements_current, process list, and timestamps. Native profilers such as perf can identify server functions consuming CPU, but require symbols, kernel permissions, operational approval, and expertise. Profile in staging first; sampling production still adds overhead and can expose sensitive call context.
Never attach a debugger that stops all threads to a busy production database without an explicit incident decision.
Inspect the top query's execution plan
Reconstruct a representative query from the digest using safe literals. Preserve parameter selectivity: a large tenant can have a different plan and cost than a random small one.
Start with plain EXPLAIN:
EXPLAIN FORMAT=JSON
SELECT id, created_at, total_amount
FROM sales.orders
WHERE customer_id = 481
AND status = 'paid'
ORDER BY created_at DESC, id DESC
LIMIT 100;
Look for:
- Broad
ALLorindexscans. - High estimated rows at frequently repeated nodes.
- Poor join order or inner-loop fanout.
- Non-sargable predicates and implicit conversions.
- Temporary tables, filesorts, or materialization.
- Missing or inappropriate composite keys.
- Rows discarded after expensive access.
For a bounded SELECT in a safe environment:
ANALYZE FORMAT=JSON
SELECT id, created_at, total_amount
FROM sales.orders
WHERE customer_id = 481
AND status = 'paid'
ORDER BY created_at DESC, id DESC
LIMIT 100;
MariaDB ANALYZE executes the statement. Never use it casually with UPDATE or DELETE; those operations actually modify data. Runtime loops, r_rows, filtering, node time, and available page counters reveal work hidden by estimates.
Find high-frequency small-query CPU
Slow query logging alone can miss CPU caused by fast, frequent statements. Symptoms include:
- High
Questionsrate. - Moderate per-call latency.
- Digest
COUNT_STARdominating. - Many network round trips.
- Repeated primary-key lookups forming an N+1 pattern.
- Connection churn and authentication work.
Compute total digest time, not only average. Then fix at the application boundary:
- Batch independent lookups with bounded
INlists or joins. - Preload related rows to eliminate N+1.
- Cache immutable or safely versioned reference data.
- Reuse prepared statements and connection pools correctly.
- Avoid polling when events or longer intervals are acceptable.
- Collapse duplicate concurrent work.
Do not replace one N+1 pattern with an unbounded query returning millions of rows. Measure result size, memory, network, and latency.
Diagnose sorting, grouping, and temporary work
CPU can be spent comparing keys, evaluating expressions, aggregating, deduplicating, and copying wide rows.
Check interval rates:
SHOW GLOBAL STATUS WHERE Variable_name IN (
'Created_tmp_tables',
'Created_tmp_disk_tables',
'Sort_merge_passes',
'Sort_range',
'Sort_rows',
'Sort_scan'
);
Use per-digest totals to identify ownership. Fix query shape before increasing buffers:
- Filter and project early.
- Remove unnecessary
DISTINCTand ordering. - Avoid carrying text/JSON through aggregation.
- Add a measured composite index for filter/order patterns.
- Replace high-offset pagination with keyset pagination.
- Precompute recurring summaries where correctness allows.
A disk temporary table can shift work from CPU to storage, while an oversized in-memory table can increase copying and memory pressure. Optimize service behavior, not one counter.
Check stored programs, events, and background work
CPU may originate outside obvious application SELECTs:
- Event Scheduler jobs.
- Stored procedures and triggers.
- Purge and rollback.
- Index creation or table rebuild.
- Replication apply.
- Backup compression or encryption.
- Fulltext maintenance.
- Galera provider and applier work.
Inspect scheduled events:
SHOW GLOBAL VARIABLES LIKE 'event_scheduler';
SELECT EVENT_SCHEMA,
EVENT_NAME,
STATUS,
LAST_EXECUTED,
INTERVAL_VALUE,
INTERVAL_FIELD
FROM information_schema.EVENTS
ORDER BY LAST_EXECUTED DESC;
Performance Schema events_statements_summary_by_program, available on modern releases such as 10.5.2+, can aggregate stored procedure, function, trigger, and event activity where collection is enabled:
DESCRIBE performance_schema.events_statements_summary_by_program;
Check replication:
SHOW ALL REPLICAS STATUSG
Syntax varies on older releases. Parallel apply can intentionally use cores. If replica lag falls while CPU is high, that may be productive catch-up; if lag grows, diagnose apply conflicts, transaction shape, and storage.
Detect contention that burns CPU
High CPU is not always useful SQL computation. Many runnable threads can spin or contend on shared structures, causing context switching and cache misses.
Check:
- Runnable threads versus available CPUs.
- Voluntary and involuntary context switches.
- Performance Schema mutex/rwlock wait summaries where enabled.
- Hot rows and lock-wait storms.
- Query cache use on legacy configurations.
- Adaptive or engine-specific contention.
pidstat -w -t -p "$(systemctl show mariadb --property=MainPID --value)" 1 10
Performance Schema wait tables can add evidence, but enabling broad fine-grained instruments during peak load has overhead. Start from existing instrumentation, enable only targeted classes in a bounded test, and document the prior state.
Do not tune internal mutex settings from one stack sample. Contention fixes often lie in reducing concurrency, shortening transactions, removing hot counters, or upgrading a release with a relevant engine fix.
Mitigate a live CPU saturation incident
1. Stop workload amplification
Pause optional reports, exports, migrations, backfills, or event jobs. Disable uncontrolled retries. Apply rate limits or concurrency caps at the application/job layer.
2. Protect diagnostic access
Keep an administrative connection or tested extra port for overload scenarios. Do not open an insecure public admin endpoint.
3. Cancel the narrowest harmful work
After verifying connection ownership and impact:
KILL QUERY 12345;
This targets the current statement. It does not necessarily close an open transaction or stop a client from immediately retrying. Coordinate with the application.
KILL CONNECTION 12345;
closes the session and rolls back its transaction, potentially creating more CPU and I/O. Use only after estimating rollback and business cost.
4. Shed load before scaling blindly
Rate limiting can restore useful throughput by reducing queueing and context switching. Scaling CPU may help a parallelizable query load, but not a single-thread bottleneck, lock convoy, storage wait, or retry storm.
5. Preserve before/after evidence
Continue sampling CPU, queries per second, Threads_running, latency, errors, top digests, storage, and throttling. Otherwise recovery can be misattributed.
Do not restart MariaDB as the first CPU control. Restart causes an outage, discards transient evidence, rolls back transactions, and empties caches; workload may surge again afterward.
Evaluate MariaDB thread pooling
MariaDB's thread pool limits active server threads relative to client connections, reducing context switches and CPU-cache disruption under high concurrency. On Unix it is enabled at startup:
[mariadb]
thread_handling = pool-of-threads
It is most effective for CPU-bound OLTP workloads with relatively short queries. It is not a query optimizer. One expensive statement still consumes work, and queued simple queries can see additional latency.
Inspect current mode and pool variables:
SHOW GLOBAL VARIABLES WHERE Variable_name IN (
'thread_handling',
'thread_pool_size',
'thread_pool_max_threads',
'thread_pool_stall_limit',
'thread_pool_oversubscribe'
);
MariaDB normally derives thread_pool_size from CPU count, and official guidance notes most users should not tune every pool knob. Container CPU visibility and quota must be correct.
Test with production query mix and concurrency. Compare throughput, queue latency, tail latency, context switches, lock behavior, and overload recovery. Thread pool can queue even SELECT 1 behind work, so health-check timeouts need realistic testing.
For emergency administration, MariaDB supports extra_port and extra_max_connections:
[mariadb]
extra_port = 8385
extra_max_connections = 5
Secure the port with bind address, firewall, TLS, and least privilege. Do not expose it broadly. Changing thread handling and extra-port settings requires planned startup configuration on relevant platforms.
Fix the query and schema root cause
Common durable changes include:
Preserve sargable predicates
Replace:
WHERE DATE(created_at) = '2026-08-25'
with a semantically correct range:
WHERE created_at >= '2026-08-25 00:00:00'
AND created_at < '2026-08-26 00:00:00'
Validate time-zone meaning.
Build a composite index for the query family
CREATE INDEX ix_orders_customer_status_created
ON sales.orders (customer_id, status, created_at);
Test DDL impact, read plans, write amplification, disk, redo, replicas, and rollback. Do not add it because one example query happens to match.
Reduce row width and expression cost
Avoid selecting large payloads before sorting, grouping, or joining. Move repeated deterministic transformations out of the hot path where correctness permits. Generated columns and indexes can help expressions but add schema and write cost.
Eliminate redundant work
Remove duplicate joins, repeated scalar subqueries, unnecessary DISTINCT, and repeated count queries. Validate result semantics with tests.
Bound analytical workloads
Require date ranges, paginate exports, maintain rollups, or move suitable analytics to a resource-isolated system. A replica does not create free CPU and can fall behind.
Review configuration without cargo-cult tuning
CPU incidents often trigger unrelated buffer changes. Inspect configuration only where evidence points:
- Buffer pool too small can cause I/O, not direct user CPU alone.
- Huge sort/join buffers multiply memory and can increase copying.
- Excessive connections create scheduling and memory pressure.
- Query cache on old releases can introduce contention; version support matters.
- Compression and encryption exchange CPU for storage or security.
- Performance Schema and audit verbosity add observability cost.
Use runtime origin and option-file precedence where supported. Test one change at a time with rollback. Avoid changing optimizer_switch, thread concurrency, flushing, and memory simultaneously; you will not know which helped or which weakened durability.
Validate a fix under realistic concurrency
Build a before/after matrix:
| Metric | Before | After |
|---|---|---|
| Service P50/P95/P99 | Captured | Compared |
| Throughput and error rate | Captured | Compared |
| MariaDB process CPU seconds/s | Captured | Compared |
| CPU per query/transaction | Estimated | Compared |
Threads_running distribution |
Captured | Compared |
| Top digest total time/count | Captured | Compared |
| Rows examined and sent | Captured | Compared |
| Sort/temp work | Captured | Compared |
| Storage latency and IOPS | Captured | Compared |
| Write latency and redo | Captured | Compared |
| Replica/cluster health | Captured | Compared |
Replay the actual query mix from a separate load generator. Running the benchmark client on the same CPUs can steal cycles and distort results. MariaDB's thread-pool guidance explicitly recommends separating driver and server CPU resources or hosts.
Test burst, steady state, large tenant, failover capacity, and cache state. A fix that lowers average CPU but worsens P99 or DML latency may be unacceptable.
Troubleshoot common misleading patterns
CPU is high but queries are fast
Traffic or call frequency may have increased. Rank digest counts and total time, check N+1 behavior, connection churn, polling, and CPU per query. Fast does not mean cheap at scale.
Load average is high but CPU is not saturated
Inspect I/O wait, uninterruptible tasks, storage queues, memory reclaim, and pressure metrics. Load is not a CPU percentage.
One core is saturated while others are idle
A single query or serialized internal task may not parallelize. More cores will not accelerate one thread. Optimize the query, split safe work, or investigate the hot code path.
All cores are busy but throughput falls
The server may be past its concurrency knee, spending cycles on context switching, contention, and retries. Shed load and cap concurrency; then evaluate thread pooling.
Performance Schema top digest is waiting, not computing
Its timer is elapsed wait. Check lock state, storage, and ANALYZE FORMAT=JSON node/page metrics before labeling it CPU-bound.
CPU rose after adding an index
The optimizer may choose a new plan with more loop work, or writes may maintain a large key. Compare before/after plans, runtime rows, DML, and digest totals.
CPU drops after restart and returns later
Restart may have stopped jobs, cleared queues, reset counters, or changed cache behavior. Correlate the recurrence with traffic and scheduled work; do not institutionalize restart as treatment.
A replica has high CPU but the primary does not
Check read traffic, parallel replication, lag catch-up, schema/index differences, and backup jobs. The same node may serve both apply and analytics.
Monitoring and alert design
Monitor rates and distributions:
- MariaDB process user/system CPU seconds.
- CPU quota throttling and VM steal.
- Per-core saturation and run queue.
Threads_runningand connection counts.- Questions/transactions per second and CPU per unit.
- Total time, count, rows, errors, sorts, and temp work by digest.
- Lock waits, storage latency, and memory pressure.
- Event/backup/DDL schedules.
- Replica lag and cluster flow control.
- Application pool queues, latency, errors, and retries.
Alert on sustained saturation plus service impact or low headroom, not a single spike. Include a failover capacity policy: if one node fails, can remaining nodes absorb its traffic without entering overload collapse?
Keep metric labels bounded. Store normalized digest IDs in metrics and retrieve sanitized SQL through a secure diagnostic path.
Best practices checklist
- Confirm actual CPU quota, cores, steal, and throttling.
- Separate user CPU from system CPU, I/O wait, and load average.
- Compare MariaDB CPU with other host processes.
- Capture workload and deployment context in UTC.
- Use interval rates for queries, CPU, and status counters.
- Rank query digests by total time, count, and rows.
- Remember digest timers are not direct CPU accounting.
- Use
userstatCPU attribution only as a measured optional tool. - Inspect current queries and representative plans.
- Look for high-frequency small statements and N+1 patterns.
- Shed optional load before killing large transactions.
- Coordinate query cancellation with application retry control.
- Evaluate thread pool under the real OLTP mix.
- Measure read, write, replica, and tail-latency effects.
- Keep evidence and a rollback path for every change.
FAQ
Is 100% MariaDB CPU always a problem?
No. On Linux, one fully used core is often shown as 100%, so a multicore process can exceed it. Judge available quota, tail latency, queues, errors, and headroom.
Does a high load average mean MariaDB is CPU-bound?
No. Linux load also includes tasks blocked in uninterruptible sleep, often I/O. Compare per-core CPU, run queue, I/O wait, storage, and pressure data.
How do I find which query uses the most CPU?
Use process sampling, current statements, digest totals, rows, user statistics where enabled, and execution plans together. Performance Schema statement time is elapsed wait, not pure CPU.
Should I restart MariaDB to clear high CPU?
Not as a first response. It causes downtime, destroys transient evidence, rolls back work, and clears caches. Stop the responsible workload or shed load after identifying it.
Will MariaDB thread pool fix an expensive query?
No. It controls active concurrency and can reduce context switching under CPU-bound OLTP load. The expensive query still needs query, schema, or workload optimization.
Can a missing index cause high CPU?
Yes. Broad scans, repeated join lookups, sorting, and filtering can consume CPU. Confirm with plans and runtime rows before adding an index and measure write cost afterward.
Why is one CPU core busy while the rest are idle?
One statement or serialized task may execute mostly on one thread. More cores do not automatically parallelize it; reduce its work or redesign safe parallelism.
Can too many connections cause high CPU?
Many active connections can cause context switching and contention. Idle connections mainly consume memory. Control active concurrency and pool behavior rather than only lowering connection count.
Conclusion
Solving MariaDB high CPU requires evidence across the whole execution path. First verify cores, quotas, throttling, steal time, load composition, storage, and memory. Then measure concurrency and throughput, rank normalized query families, and correlate statement time with rows, plans, locks, and operating-system thread behavior. No single process-list snapshot or digest counter proves CPU ownership.
During saturation, stop optional work and retry amplification before taking destructive action. Long-term fixes usually reduce total work: better predicates and composite indexes, fewer round trips, bounded reports, shorter transactions, and controlled concurrency. Thread pooling and additional CPU can provide headroom when the workload fits, but they do not replace query engineering. Verify every change by CPU per useful transaction, tail latency, write cost, and failover capacity, not merely a lower dashboard percentage.
Suggested Internal Links
- Use the MariaDB Slow Query Log for Better Diagnosis
- Analyze MariaDB Queries with EXPLAIN and ANALYZE Safely
- Design Effective MariaDB Indexes Without Over-Indexing
- Find and Fix MariaDB Lock Waits and Deadlocks Safely
- Configure MariaDB Connections and Thread Handling
- Tune MariaDB Temporary Tables and Sort Operations Safely