Databases 19 min read

Master MySQL Performance: 20 Essential SQL Optimization Techniques

This article presents a comprehensive guide for Java engineers on MySQL query optimization, covering index usage, avoiding SELECT *, proper JOINs, efficient WHERE clauses, LIMIT, EXISTS vs IN, function indexes, connection pooling, batch processing, data types, execution plan analysis, materialized views, and regular performance monitoring, all illustrated with practical code examples.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master MySQL Performance: 20 Essential SQL Optimization Techniques

1. Introduction

SQL optimization is a key skill for Java engineers. Efficient database interaction can significantly improve application performance, response time, and user experience.

2. Practical Cases

2.1 Use Indexes

Indexes allow the database to locate data quickly, greatly improving query performance. Create indexes on columns used in WHERE , JOIN , ORDER BY and GROUP BY clauses, and consider covering indexes that contain all needed columns.

Wrong example

<code>SELECT * FROM users WHERE name = 'pack'</code>

Correct example

<code>CREATE INDEX idx_name ON users (name);</code><code>SELECT name, email FROM users WHERE name = 'pack';</code>

Creating an index on the name column of the users table speeds up the query.

2.2 Avoid SELECT *

Using SELECT * retrieves all columns, which can reduce efficiency and cause unnecessary data transfer.

Wrong example

<code>SELECT * FROM users;</code>

Correct example

<code>SELECT name, age FROM users;</code>

Specifying only required columns reduces the amount of data transmitted.

2.3 Proper Use of JOIN

Incorrect join syntax can lead to performance problems. Use the appropriate join type for the query.

Wrong example

<code>SELECT u.name, o.create_time FROM users u, orders o WHERE u.id = o.uid;</code>

Correct example

<code>SELECT u.name, o.create_time FROM users u INNER JOIN orders o ON u.id = o.uid;</code>

This query uses INNER JOIN to combine data from the users and orders tables.

2.4 Filter Data Early with WHERE

Filtering data early reduces the amount of data processed.

Wrong example

<code>SELECT name, age FROM users;</code>

Correct example

<code>SELECT name, age FROM users WHERE status = 0;</code>

Only rows with a normal status are retrieved, reducing processing load.

2.5 Limit Result Set

Use LIMIT to restrict the number of returned rows when you do not need the entire dataset.

Wrong example

<code>SELECT name, age FROM users WHERE status = 0;</code>

Correct example

<code>SELECT name, age FROM users WHERE status = 0 LIMIT 10;</code>

This query retrieves the first 10 valid rows, reducing data transfer.

2.6 Use EXISTS Instead of IN

EXISTS can be more efficient than IN , especially for large result sets.

<code># 1. Using IN
SELECT * FROM customers WHERE id IN (SELECT cid FROM orders);
# 2. Using EXISTS
SELECT * FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.cid = c.id);</code>

2.7 Avoid Functions in WHERE

Using functions in the WHERE clause can prevent index usage and slow queries.

Wrong example

<code>SELECT name, age FROM users WHERE YEAR(create_time) = 2024;</code>

Correct example

<code>SELECT name, age FROM users WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';</code>

This avoids the function and allows the index on create_time to be used.

2.8 Use JOIN Instead of Subquery

JOINs are usually more efficient than subqueries, especially on large data sets.

Wrong example

<code>SELECT name, (SELECT create_time FROM orders WHERE uid = users.id) AS create_time FROM users;</code>

Correct example

<code>SELECT u.name, o.create_time FROM users u JOIN orders o ON u.id = o.uid;</code>

The JOIN provides better performance.

2.9 Optimize GROUP BY and ORDER BY

These clauses can be resource‑intensive. Use indexes on the involved columns and reduce the number of columns.

Wrong example

<code>SELECT uid, COUNT(*), MAX(create_time) FROM orders GROUP BY uid, create_time ORDER BY create_time;</code>

Correct example

<code>SELECT uid, COUNT(*) FROM orders GROUP BY uid ORDER BY uid;</code>

Indexing the grouped column improves performance.

2.10 Choose Proper Data Types

Selecting appropriate data types greatly affects performance and storage efficiency. Avoid using TEXT or BLOB unless necessary.

Wrong example

<code>CREATE TABLE users (id BIGINT auto_increment PRIMARY KEY, name TEXT, create_time TIMESTAMP);</code>

Correct example

<code>CREATE TABLE users (id BIGINT auto_increment PRIMARY KEY, name VARCHAR(100), create_time TIMESTAMP);</code>

Using the correct type improves performance and storage.

2.11 Analyze Execution Plan

Use EXPLAIN to analyze query execution plans and identify performance bottlenecks.

<code>EXPLAIN SELECT name, sex, age FROM big_table t WHERE t.name = 'Pack';</code>
Execution plan example
Execution plan example

Based on the result, analyze whether indexes are used and their types.

2.12 Use Connection Pool

Connection pools reduce the overhead of establishing database connections.

Wrong example

<code>Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "xxxooo");
conn.close();</code>

Correct example

<code>HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
config.setUsername("root");
config.setPassword("xxxooo");
config.setMaximumPoolSize(10);
HikariDataSource ds = new HikariDataSource(config);
Connection conn = ds.getConnection();
// ...
conn.close();</code>

In Spring Boot, this can be configured via properties without manual code.

2.13 Use Batch Processing

Batch inserts/updates greatly improve performance for multiple data modifications.

Wrong example

<code>Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
for (User user : userList) {
    stmt.executeUpdate("INSERT INTO users (name, age) VALUES ('" + user.getName() + "', '" + user.getAge() + "')");
}
stmt.close();
conn.close();</code>

Correct example

<code>Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("INSERT INTO users (name, age) VALUES (?, ?)");
for (User user : userList) {
    ps.setString(1, user.getName());
    ps.setInt(2, user.getAge());
    ps.addBatch();
}
ps.executeBatch();
ps.close();
conn.close();</code>

Batch processing inserts data efficiently.

2.14 Optimize JOIN Order

Ensure join columns are indexed and start joining from the smallest table.

Wrong example

<code>SELECT u.name, o.create_time FROM orders o JOIN users u ON u.id = o.uid WHERE u.status = 0;</code>

Correct example

<code>SELECT u.name, o.create_time FROM users u JOIN orders o ON u.id = o.uid WHERE u.status = 0;</code>

This query joins on indexed columns, improving performance.

2.15 Optimize Subqueries

Replace subqueries with joins or CTEs when possible.

Wrong example

<code>SELECT o.* FROM orders o WHERE o.amount > (SELECT AVG(o2.amount) FROM orders o2 WHERE o2.customer_id = o.customer_id);</code>

Correct example

<code>WITH customer_avg AS (
    SELECT customer_id, AVG(amount) AS avg_amount FROM orders GROUP BY customer_id
)
SELECT o.* FROM orders o JOIN customer_avg ca ON o.customer_id = ca.customer_id WHERE o.amount > ca.avg_amount;</code>

2.16 Optimize Aggregation Queries

Index columns used in GROUP BY , consider summary tables or materialized views for frequent aggregations.

Wrong example

<code>SELECT customer_id, SUM(amount) AS total_amount, COUNT(*) AS order_count FROM orders GROUP BY customer_id;</code>

Correct example

<code>CREATE INDEX idx_customer_id ON orders (customer_id);
SELECT customer_id, SUM(amount) AS total_amount, COUNT(*) AS order_count FROM orders GROUP BY customer_id;</code>

2.17 Use Summary Columns

Store pre‑computed aggregates in a summary column to avoid expensive calculations at query time.

Wrong example

<code>SELECT user_id, SUM(amount) AS total_amount FROM orders GROUP BY uid;</code>

Correct example

<code>ALTER TABLE users ADD total_order_amount DECIMAL(10,2);
UPDATE users u SET total_order_amount = (SELECT SUM(amount) FROM orders o WHERE o.uid = u.id);</code>

The summary column holds each user's total order amount.

2.18 Use Materialized Views

Materialized views cache complex query results, improving read performance.

Wrong example

<code>SELECT uid, COUNT(*), SUM(amount) FROM orders GROUP BY uid;</code>

Correct example

<code>CREATE MATERIALIZED VIEW user_order_summary AS
SELECT uid, COUNT(*) AS order_count, SUM(amount) AS total_amount FROM orders GROUP BY uid;</code>

The view stores pre‑computed order summaries.

2.19 Monitor and Tune Database Settings

Regularly monitor and adjust settings such as buffer pool size, cache size, and use tools like EXPLAIN and ANALYZE to identify bottlenecks.

2.20 Regularly Review and Refactor SQL

Periodic code reviews help identify performance issues; refactor complex queries into simpler, more efficient forms.

Wrong example

<code>SELECT u.name, (SELECT COUNT(*) FROM orders o WHERE o.uid = u.id) AS order_count FROM users u;</code>

Correct example

<code>SELECT u.name, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON u.id = o.uid GROUP BY u.name;</code>

This refactored query uses a join and GROUP BY for better performance.

Illustration
Illustration
JavaDatabasePerformance TuningMySQLIndexesSQL Optimization
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login 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.