Databases 9 min read

Why Your MySQL Queries Slow Down in Production and How to Fix Them

The article explains why locally fast MySQL queries can become painfully slow in production, identifies three common pitfalls such as ineffective indexes, oversized Go connection pools, and hidden type conversions, and provides concrete optimization techniques spanning index design, query planning, Go‑MySQL integration, and systematic self‑checks.

Code Wrench
Code Wrench
Code Wrench
Why Your MySQL Queries Slow Down in Production and How to Fix Them

Common Pitfalls in Production

Even a query that runs in 10 ms locally can take 1 s after deployment. Typical issues include:

Indexes exist but queries remain slow.

Increasing the Go connection pool overloads the database.

A simple WHERE clause triggers a full table scan.

The root cause is often a lack of understanding of how MySQL executes queries, covering index paths, optimizer behavior, and Go connection‑pool dynamics.

1. Index Optimization: Reduce I/O, Not Just Add Indexes

Goal: Minimize back‑table lookups and random I/O. SELECT * FROM orders WHERE user_id = 1001; Even with an index, this can be slow because MySQL performs a back‑table lookup:

secondary index → primary key → data page (random I/O)

Correct approach: use a covering index that includes all needed columns.

SELECT id, user_id, amount FROM orders WHERE user_id = 1001;

If id, user_id and amount are in the index, MySQL returns results directly without back‑table access. A covering index therefore saves one B+‑tree jump and one random I/O.

2. Function Operations: Hidden Index Killers

Applying functions to indexed columns disables index usage.

SELECT * FROM orders WHERE DATE(created_at) = '2024-01-01';

Because the function breaks the index’s ordered property, the optimizer cannot use the index.

Solution: create a generated column and index it.

ALTER TABLE orders
  ADD COLUMN order_date DATE GENERATED ALWAYS AS (DATE(created_at)) VIRTUAL;
CREATE INDEX idx_order_date ON orders(order_date);

Now the query can use the index efficiently.

3. Index Condition Pushdown (ICP)

No ICP: Scan index → full back‑table → filter.

With ICP: Scan index → filter → back‑table (if needed).

Example: WHERE name LIKE '张%' AND age = 25 MySQL filters age during the index scan. Verify with: EXPLAIN SELECT ... Look for the Using index condition flag, which indicates ICP is active and reduces back‑table calls.

4. Slow Query Core: Wrong Execution Plan

Most slow queries stem from the optimizer choosing a sub‑optimal plan. Focus on three EXPLAIN fields:

type (access method): ALL means full table scan.

rows: Estimated rows examined; keep this low.

Extra: Flags such as Using index (covering index), Using index condition (ICP), Using filesort (expensive sort), Using temporary (temporary table creation).

5. JOIN Performance

Driving table size dominates JOIN cost. Avoid letting a large table drive a small filtered table.

SELECT * FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.status = 'active';

If orders has 1 M rows and users filters to 10 k, the join performs 1 M matches. Prefer the filtered table as the driver or force order with STRAIGHT_JOIN when the optimizer’s choice is clearly wrong.

6. Go + MySQL Pitfalls

Implicit Type Conversion

Mismatch between Go types and column types forces MySQL to convert values, breaking index usage.

-- Wrong
db.Where("user_id = ?", 123) // user_id is VARCHAR

MySQL converts the integer to a string, preventing index use.

-- Correct
db.Where("user_id = ?", "123")

Connection Pool Size

Oversized pools cause lock contention, increased context switches, and DB overload. Monitor with: db.Stats().WaitCount If WaitCount rises, optimize slow queries instead of adding connections.

Context Timeout Misunderstanding

Timeouts abort the client but do not guarantee MySQL stops execution immediately.

Proper three‑layer control:

Go context with deadline.

MySQL max_execution_time setting.

Continuous slow‑query optimization.

Timeouts are a safety net, not a performance fix.

Conclusion: Underlying Logic of SQL Optimization

Three fundamental goals:

Reduce I/O.

Lower scanned rows.

Avoid ineffective calculations.

Key takeaway: "Make MySQL do less work, not just work faster."

Production Self‑Check Checklist

SQL Checklist

Avoid SELECT *.

Use covering indexes.

Eliminate implicit type conversions.

Ensure queries hit the leftmost prefix of composite indexes.

Watch for filesort or temporary in EXPLAIN.

Go Checklist

Match Go types with DB column types.

Prefer QueryContext over plain queries.

Always close Rows.

Monitor connection‑pool health.

System Checklist

Analyze slow queries regularly.

Load‑test connection pools.

Implement timeout controls.

Set up alerting for performance anomalies.

Understanding problems across SQL, execution plan, storage engine, and application layers helps you advance from troubleshooting to proactive performance engineering.

indexingGoQuery OptimizationMySQL
Code Wrench
Written by

Code Wrench

Focuses on code debugging, performance optimization, and real-world engineering, sharing efficient development tips and pitfall guides. We break down technical challenges in a down-to-earth style, helping you craft handy tools so every line of code becomes a problem‑solving weapon. 🔧💻

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.