SynfraCore
Synfracore
Start Learning
Navigation

Academies

Platform

RoadmapsLabsCertificationsInterviewPYQsAI AssistantCareer
Start Learning Free🗺️ Learning Roadmaps

MySQLIntermediate

Real-world patterns, best practices, and deeper topics

✍️
Written by senior engineers. Reviewed for technical accuracy.· Updated 2025 · SynfraCore MySQL Team
Expert Content

MySQL — Intermediate

Performance Schema and Query Analysis

sql
-- Enable slow query log
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;          -- Log queries > 1 second
SET GLOBAL log_queries_not_using_indexes = 1;
SHOW VARIABLES LIKE 'slow_query_log_file';

-- EXPLAIN and EXPLAIN ANALYZE
EXPLAIN SELECT u.name, COUNT(o.id)
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id;

-- Key fields in output:
-- type: ALL (full scan)→range→ref→eq_ref→const (best)
-- key: NULL means no index used
-- rows: estimated rows examined
-- Extra: "Using filesort" or "Using temporary" = slow

-- pt-query-digest (Percona Toolkit) for slow log analysis
pt-query-digest /var/log/mysql/slow.log | head -50

Modern MySQL Features (window functions, CTEs, JSON — standard since 8.0)

sql
-- Window functions
SELECT
    name,
    department,
    salary,
    RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank,
    SUM(salary) OVER (PARTITION BY department) AS dept_total
FROM employees;

-- CTEs
WITH monthly_revenue AS (
    SELECT DATE_FORMAT(created_at, '%Y-%m') AS month, SUM(total) AS revenue
    FROM orders
    WHERE status = 'completed'
    GROUP BY 1
)
SELECT month, revenue, LAG(revenue) OVER (ORDER BY month) AS prev_month
FROM monthly_revenue;

-- JSON functions
SELECT
    JSON_EXTRACT(metadata, '$.plan') AS plan,
    JSON_EXTRACT(metadata, '$.features[0]') AS first_feature
FROM users
WHERE JSON_CONTAINS(metadata, '"enterprise"', '$.tags');

-- Generated columns
ALTER TABLE orders
ADD COLUMN total_with_gst DECIMAL(10,2) AS (total * 1.18) STORED;

-- Check constraints
ALTER TABLE products
ADD CONSTRAINT chk_price CHECK (price > 0 AND price < 1000000);

Indexing Strategy — Selectivity and the Leftmost-Prefix Rule

sql
-- Composite index: leftmost prefix rule
CREATE INDEX idx_user_status_date ON orders(user_id, status, created_at);

-- Supports these WHERE clauses:
-- WHERE user_id = ?
-- WHERE user_id = ? AND status = ?
-- WHERE user_id = ? AND status = ? AND created_at > ?

-- Does NOT efficiently support (missing the leftmost column):
-- WHERE status = ?
-- WHERE user_id = ? AND created_at > ?   (skips status in the middle)

-- Index selectivity: high cardinality = better index candidate
SELECT COUNT(DISTINCT status)/COUNT(*) FROM orders;    -- low (bad index candidate)
SELECT COUNT(DISTINCT user_id)/COUNT(*) FROM orders;    -- high (good index candidate)

Locking and Deadlocks

sql
-- Row locks (InnoDB default)
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;          -- Exclusive lock
SELECT * FROM accounts WHERE id = 1 LOCK IN SHARE MODE;  -- Shared lock

-- Deadlock detection is automatic: MySQL auto-rolls back the smaller transaction
SHOW ENGINE INNODB STATUS;   -- Look for LATEST DETECTED DEADLOCK section

-- Prevent deadlocks:
-- 1. Always access tables/rows in the SAME order across transactions
-- 2. Keep transactions short
-- 3. Use SELECT ... FOR UPDATE only when actually needed
-- 4. Add indexes (avoids gap locks caused by unindexed range scans)

Replication Types

sql
-- Row-based replication (recommended):
-- Replicates exact data changes
-- SET GLOBAL binlog_format = 'ROW';

-- Statement-based (legacy):
-- Replicates SQL statements (dangerous with non-deterministic functions)

-- GTID (Global Transaction IDs) - modern approach
-- Simplifies replication setup, automatic position tracking
-- my.cnf: gtid_mode=ON, enforce_gtid_consistency=ON

-- Check replication health — SHOW SLAVE STATUS was removed in the 8.4 LTS
-- release; use SHOW REPLICA STATUS (renamed starting 8.0.23)
SHOW REPLICA STATUS\G
-- Key fields:
-- Replica_IO_Running: Yes
-- Replica_SQL_Running: Yes
-- Seconds_Behind_Source: 0 (or low)
-- Last_Error: (should be empty)

-- Simulate replication lag detection
SELECT TIMESTAMPDIFF(SECOND,
    MAX(ts), NOW()) AS seconds_behind
FROM heartbeat;  -- With pt-heartbeat
Share:
Join our Community
Daily tips, job alerts, interview help — join engineers learning together
Up Next
🚀
MySQLAdvanced
Production patterns, performance, security hardening
Also Worth Exploring
← Back to all MySQL modules
FundamentalsAdvanced