Performance Optimization
17. Performance Optimization
Section titled “17. Performance Optimization”Query-Level Optimization
Section titled “Query-Level Optimization”-- ✅ Select only needed columns (avoid SELECT *)SELECT id, name FROM employees WHERE dept_id = 1;
-- ✅ Use indexes for filtering, avoid functions on indexed columns-- ❌ Bad:SELECT * FROM employees WHERE UPPER(email) = 'ALICE@COMPANY.COM';-- ✅ Good:SELECT * FROM employees WHERE email = 'alice@company.com';
-- ✅ Use LIMIT with ORDER BY for top-NSELECT * FROM employees ORDER BY salary DESC LIMIT 10;
-- ✅ Use EXISTS instead of COUNT for existence checks-- ❌ Slow:SELECT * FROM employees WHERE (SELECT COUNT(*) FROM orders WHERE emp_id = employees.id) > 0;-- ✅ Fast:SELECT * FROM employees WHERE EXISTS (SELECT 1 FROM orders WHERE emp_id = employees.id);
-- ✅ Avoid OR on indexed columns (use UNION instead)-- ❌SELECT * FROM employees WHERE dept_id = 1 OR dept_id = 2;-- ✅SELECT * FROM employees WHERE dept_id IN (1, 2);Index Optimization
Section titled “Index Optimization”-- ✅ Use composite indexes wisely (leftmost prefix rule)CREATE INDEX idx_dept_salary ON employees(dept_id, salary);
-- ✅ Covering index for frequently queried columnsCREATE INDEX idx_cover ON employees(dept_id, salary, name);SELECT name, salary FROM employees WHERE dept_id = 1; -- index-only scan
-- ✅ Analyze index usageSELECT * FROM sys.schema_unused_indexes; -- find unused indexesSELECT * FROM sys.schema_redundant_indexes; -- find duplicate indexesSchema-Level Optimization
Section titled “Schema-Level Optimization”-- ✅ Use appropriate data types (smaller = faster)-- ❌ Using VARCHAR(255) for a 2-char country codecountry_code VARCHAR(2) -- better than VARCHAR(255)
-- ✅ Use INT for FK references (not VARCHAR)dept_id INT -- not VARCHAR(50) for department name as FK
-- ✅ Partition large tablesCREATE TABLE orders ( id BIGINT AUTO_INCREMENT, order_date DATE, ...)PARTITION BY RANGE (YEAR(order_date)) ( PARTITION p2022 VALUES LESS THAN (2023), PARTITION p2023 VALUES LESS THAN (2024), PARTITION p2024 VALUES LESS THAN (2025));Configuration-Level Tips
Section titled “Configuration-Level Tips”-- Check slow query logSHOW VARIABLES LIKE 'slow_query_log%';SET GLOBAL slow_query_log = 'ON';SET GLOBAL long_query_time = 1; -- log queries > 1 second
-- Check buffer pool (InnoDB)SHOW VARIABLES LIKE 'innodb_buffer_pool_size';-- Should be 70-80% of available RAM for dedicated MySQL servers
-- Query cache (deprecated in MySQL 8)-- Use application-level caching (Redis/Memcached) insteadThe N+1 Problem
Section titled “The N+1 Problem”-- ❌ N+1: 1 query for employees + N queries for each dept nameSELECT * FROM employees; -- 100 rows-- Then for each employee:SELECT dept_name FROM departments WHERE dept_id = ?; -- 100 more queries!
-- ✅ Fix: use a JOINSELECT e.*, d.dept_nameFROM employees eJOIN departments d ON e.dept_id = d.dept_id;-- 1 query, done!