MySQL Tuning skill for Codex

InnoDB-aware indexes, plans and settings. Codex tunes MySQL starting from performance_schema digests and the slow log rather than guesswork. It reads EXPLAIN ANALYZE, designs composite and covering indexes around InnoDB's clustered primary key, retires indexes by making them invisible first, adds histograms for skewed columns, and reviews buffer pool and redo log sizing from status counters. Schema changes state ALGORITHM explicitly so nothing silently copies a big table.

  • Databases
  • $mysql-tuning
  • 168 lines
  • 2 files
  • ~1,950 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- mysql-tuning

Installs to ~/.agents/skills/mysql-tuning/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $mysql-tuning, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Tunes MySQL 8.0 and 8.4 LTS workloads on InnoDB - finds expensive statements in performance_schema and the slow query log, reads EXPLAIN and EXPLAIN ANALYZE, designs composite and covering indexes around the clustered primary key, uses invisible indexes and histograms, sizes the buffer pool and redo log, and runs online DDL with ALGORITHM=INSTANT or INPLACE. Use when a MySQL or MariaDB-compatible query is slow, CPU or IO is pegged, a schema change needs to run on a live table, or my.cnf settings need review.

Use it when

  • A MySQL query or endpoint is slow
  • CPU or IO on a MySQL server is pegged
  • Deciding which MySQL indexes to add or remove
  • Reviewing InnoDB configuration or running online DDL

Use something else when

How to check the result

EXPLAIN ANALYZE before and after shows fewer rows examined and no unwanted filesort or temporary table, and the digest's average latency drops in performance_schema after deploy.

The mistake it prevents

Running ALTER TABLE without an explicit ALGORITHM, so MySQL silently falls back to copying a large table while queries pile up on the metadata lock.

~/.agents/skills/mysql-tuning/SKILL.md

--- name: mysql-tuning description: Tunes MySQL 8.0 and 8.4 LTS workloads on InnoDB - finds expensive statements in performance_schema and the slow query log, reads EXPLAIN and EXPLAIN… license: MIT ---

Fix the statements that cost the most total time, with indexes designed for InnoDB’s clustered layout, and touch server configuration only when metrics point at it. Done well means each change is justified by a digest, a plan or a status counter, and was measured before and after.

Find what to tune

Read AGENTS.md for how to reach a production-like database and whether you may run queries there. Then:

SELECT DIGEST_TEXT, COUNT_STAR AS calls,
       ROUND(SUM_TIMER_WAIT/1e12, 1) AS total_s,
       ROUND(AVG_TIMER_WAIT/1e9, 2)  AS avg_ms,
       SUM_ROWS_EXAMINED, SUM_ROWS_SENT, SUM_NO_INDEX_USED
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC LIMIT 15;

Timers are in picoseconds. Red flags: SUM_ROWS_EXAMINED hundreds of times SUM_ROWS_SENT (scanning to find little), SUM_NO_INDEX_USED > 0 on hot digests. The sys schema has shortcuts: sys.statements_with_full_table_scans, sys.statements_with_temp_tables, sys.statements_with_sorting.

Slow log for history and exact parameter values:

SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;
SET GLOBAL log_slow_extra = ON;    -- 8.0.14+: rows examined, bytes, temp tables per entry

SET GLOBAL changes a live server: confirm with the user first, and use SET PERSIST only if they want it to survive restarts. Summarize with pt-query-digest slow.log (Percona Toolkit, may need installing).

Read the plan

EXPLAIN FORMAT=TREE SELECT ...;   -- estimated plan, readable
EXPLAIN ANALYZE SELECT ...;       -- 8.0.18+: executes the query, shows actual rows and time

EXPLAIN ANALYZE runs the statement; do not use it on writes in production.

In the planMeaningAction
type: ALL / Table scanFull scanIndex the filter, or the predicate is not sargable
type: indexFull index scanUsually still too much; better leading columns
ref, eq_ref, range, constIndex lookupsGood; check rows estimate
Using filesortSort not served by an indexComposite index ending in the ORDER BY columns
Using temporaryTemp table for GROUP BY/DISTINCT/UNIONIndex matching GROUP BY, or reduce rows first
Using indexCovering, no row lookupIdeal for hot reads
Using index conditionIndex condition pushdownFine
rows estimate far from actual rowsBad statsANALYZE TABLE; histograms for skewed non-indexed columns

Index design for InnoDB

  • The table is stored in primary key order, and every secondary index stores the PK columns. A wide PK (UUID strings, composite natural keys) bloats every index. Prefer BIGINT UNSIGNED AUTO_INCREMENT, or UUIDs as BINARY(16) with UUID_TO_BIN(uuid, 1) (time-ordered for v1) or application-generated v7.
  • Composite order: equality columns first, then one range or the ORDER BY columns. The leftmost-prefix rule applies: (a, b, c) serves a, a,b, a,b,c, not b alone.
  • Because secondary indexes include the PK, INDEX (customer_id, created_at) already covers SELECT id FROM orders WHERE customer_id = ? ORDER BY created_at.
  • Functional indexes (8.0.13+) need double parentheses: CREATE INDEX users_email_lower ON users ((LOWER(email)));
  • Descending indexes are real in 8.0+: INDEX (account_id, created_at DESC).
  • Prefix indexes on long VARCHAR/TEXT (INDEX (url(100))) cannot cover or sort.

Removing indexes safely:

SELECT * FROM sys.schema_unused_indexes;        -- since last restart only
SELECT * FROM sys.schema_redundant_indexes;
ALTER TABLE orders ALTER INDEX orders_legacy_idx INVISIBLE;   -- optimizer ignores it, still maintained
-- watch for a week of traffic, then drop, or flip back VISIBLE instantly

Check replicas too; stats are per server.

Statistics

ANALYZE TABLE orders;                                       -- refresh index stats
ANALYZE TABLE orders UPDATE HISTOGRAM ON status, region WITH 64 BUCKETS;
SELECT * FROM information_schema.COLUMN_STATISTICS WHERE TABLE_NAME = 'orders';

Histograms help non-indexed columns with skew used in filters and joins. They are not updated automatically; refresh after big data changes.

Query patterns that defeat indexes

PatternFix
WHERE DATE(created_at) = '2026-10-01'Range: created_at >= '2026-10-01' AND created_at < '2026-10-02'
WHERE phone = 5551234 on a VARCHAR columnQuote the value; numeric comparison converts every row
Join columns with different charsets/collationsAlign column definitions; mismatches force conversion and block index use
LIKE '%term'Full-text index (MATCH ... AGAINST) or external search
OR across columnsUNION ALL, or rely on index merge only after checking the plan
LIMIT 50 OFFSET 200000Keyset pagination on (created_at, id)
SELECT * on wide rows when an index could coverSelect only needed columns

Optimizer hints (/*+ INDEX(orders orders_customer_created_idx) */, FORCE INDEX) are a last resort: they freeze today’s plan and break silently when the index is renamed.

Configuration review

Collect evidence first: SHOW GLOBAL STATUS, SHOW ENGINE INNODB STATUS\G, and SHOW VARIABLES. Defaults differ between 8.0 and 8.4 (8.4 turned off the adaptive hash index and changed IO capacity defaults), so read actual values.

SettingGuidance
innodb_buffer_pool_size50-75% of RAM on a dedicated server. Check hit rate: Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests should be well under 1%
innodb_redo_log_capacity (8.0.30+)Size for roughly an hour of peak writes; small redo logs cause checkpoint stalls
innodb_flush_log_at_trx_commit1 for durability; 2 trades up to ~1 s of commits on OS crash for throughput. Ask before changing
sync_binlog1 with replication or PITR from binlogs
innodb_io_capacityMatch storage: SSD/NVMe in the thousands
max_connectionsDo not raise as a fix for pool exhaustion; use a pooler and fix slow queries
innodb_dedicated_serverON lets MySQL size buffer pool and redo itself on a dedicated host

Managed MySQL (RDS, Aurora, Cloud SQL, PlanetScale) changes settings via parameter groups or consoles; tell the user what to change rather than attempting it.

Online DDL

ALTER TABLE orders ADD COLUMN note VARCHAR(255) NULL, ALGORITHM=INSTANT;
ALTER TABLE orders ADD INDEX orders_status_idx (status), ALGORITHM=INPLACE, LOCK=NONE;
  • Always state ALGORITHM (and LOCK) so MySQL fails fast instead of silently copying the table.
  • INSTANT supports adding/dropping columns anywhere (8.0.29+), renaming columns, and changing defaults; type changes generally need COPY.
  • Every DDL needs a metadata lock. A long transaction holding the table blocks it, and all later queries queue behind it. Set SET SESSION lock_wait_timeout = 5; for DDL.
  • Large tables needing COPY: use gh-ost or pt-online-schema-change ($mysql-online-schema-change).

Verification

  • Target digests chosen from performance_schema or slow log totals
  • EXPLAIN ANALYZE before and after, on production-like data, same parameters
  • Rows examined per row sent dropped; filesort or temp table removed where intended
  • Index removals done via INVISIBLE first, checked on all replicas
  • Config changes backed by a status counter, applied with user approval, re-measured

Output template

## MySQL tuning: <digest or endpoint>
**Cost:** <calls, total s, avg ms, rows examined/sent>
**Plan before:** <access type, Extra, rows, time>
**Change:** <index DDL with ALGORITHM / rewrite / setting>
**Plan after:** <access type, Extra, rows, time>
**Side effects:** <write cost, index count, replica impact>

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "MySQL Tuning"
  short_description: "InnoDB-aware indexes, plans and settings"
  default_prompt: "$mysql-tuning Our MySQL 8 server is at 90% CPU, find the queries causing it and fix them"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem