Optimizing MySQL or MariaDB performance reduces query latency, improves concurrency, and preserves headroom before traffic growth turns small inefficiencies into outages.

Database speed is largely a product of query plans (indexes, joins, and sorting), InnoDB caching (buffer pool behavior and internal structures), and storage characteristics (fsync latency, I/O contention, and throughput). Instrumentation such as the slow query log, execution-plan inspection, and runtime status counters helps isolate the small set of statements and resources that dominate total time.

Server-level changes can shift memory pressure, increase log volume, or change locking and flushing behavior, so tuning works best as a measured loop: capture a baseline, apply one change, re-test, and keep a rollback path. Maintenance operations such as table rebuilds or large buffer pool changes can be disruptive on large datasets and may require a maintenance window.

MySQL/MariaDB performance optimization overview:

  1. Capture a baseline of throughput, concurrency, and InnoDB read pressure before making changes.
    mysql> SHOW GLOBAL STATUS WHERE Variable_name IN ('Queries','Threads_connected','Threads_running','Innodb_buffer_pool_reads','Innodb_buffer_pool_read_requests');
    +----------------------------------+-----------+
    | Variable_name                    | Value     |
    +----------------------------------+-----------+
    | Innodb_buffer_pool_reads         | 1842      |
    | Innodb_buffer_pool_read_requests | 59218471  |
    | Queries                          | 221885913 |
    | Threads_connected                | 38        |
    | Threads_running                  | 6         |
    +----------------------------------+-----------+

    Capture at least two samples under similar load and compare deltas rather than single snapshots.

  2. Check the current slow query threshold before enabling logging.
    mysql> SHOW VARIABLES LIKE 'long_query_time';
    +-----------------+-----------+
    | Variable_name   | Value     |
    +-----------------+-----------+
    | long_query_time | 10.000000 |
    +-----------------+-----------+

    Start with a higher threshold if log volume is unknown, then lower it as the workflow stabilizes.

  3. Enable slow query logging during representative load.
    mysql> SET GLOBAL slow_query_log = ON;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SHOW VARIABLES WHERE Variable_name IN ('slow_query_log','slow_query_log_file','log_output');
    +--------------------+-------------------------------+
    | Variable_name      | Value                         |
    +--------------------+-------------------------------+
    | log_output         | FILE                          |
    | slow_query_log     | ON                            |
    | slow_query_log_file| /var/log/mysql/mysql-slow.log |
    +--------------------+-------------------------------+

    Persist settings in the server configuration so logging survives a restart.

  4. Summarize the slow query log.

    Digests and totals tend to be more actionable than the single slowest entry.

  5. Pick the top candidates by total time and call count.

    High-frequency statements with moderate latency often dominate overall load.

  6. Run EXPLAIN on each candidate statement to confirm the access path.
    mysql> EXPLAIN SELECT o.id, o.created_at
        -> FROM orders o
        -> WHERE o.customer_id = 123
        -> ORDER BY o.created_at DESC
        -> LIMIT 10;
    +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------------+
    | id | select_type | table | type | possible_keys | key          | key_len | ref   | rows | Extra                       |
    +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------------+
    |  1 | SIMPLE      | o     | ref  | idx_customer  | idx_customer | 4       | const |   42 | Using where; Using filesort |
    +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------------+

    Flags like Using filesort or large rows estimates often point to missing composite indexes or predicates that are not index-friendly.

  7. Rewrite queries that force scans, filesorts, or large temporary tables.

    Change one query at a time and re-check the plan after each change.

  8. Add or adjust indexes only where EXPLAIN shows a measurable reduction in scanned rows or avoided sorts.

    Extra indexes speed reads but increase write cost and can bloat disk usage on high-churn tables.

  9. Tune innodb_buffer_pool_size to reduce physical reads without pushing the host into swapping.
    mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
    +------------------------+------------+
    | Variable_name          | Value      |
    +------------------------+------------+
    | innodb_buffer_pool_size| 8589934592 |
    +------------------------+------------+

    Oversizing memory settings can trigger swapping and make latency worse.

  10. Tune connection and cache variables to match workload and memory budget.
    mysql> SHOW VARIABLES WHERE Variable_name IN ('max_connections','table_open_cache','thread_cache_size');
    ##### snipped #####

    Connection spikes often show up as higher Threads_running and scheduler contention rather than higher throughput.

  11. Refresh table statistics when plans become unstable after major data changes.
    mysql> ANALYZE TABLE db.table;
    ##### snipped #####

    Updated statistics help the optimizer choose better join orders and index usage.

  12. Rebuild tables only when reclaiming space or reducing fragmentation.

    Table rebuilds can be disruptive on large tables and may require a maintenance window.

  13. Tune the query cache only on versions that still support it and only when the hit rate is healthy.

    MySQL removed the query cache in newer versions; some MariaDB builds still support it.

  14. Run a tuning tool.

    Tool output is a starting point, not a checklist.

  15. Validate tuning-tool recommendations with before/after measurements.

    Apply only changes that improve the workload metrics and do not introduce regressions under peak load.

  16. Re-check the baseline metrics after each change to confirm improvement and avoid regressions.
    mysql> SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_running','Innodb_buffer_pool_reads','Innodb_buffer_pool_read_requests');
    ##### snipped #####

    Keep a rollback plan for changes that shift memory usage or require a restart.

Discuss the article:

Comment anonymously. Login not required.