MySQL Advanced Features
Advanced MySQL cheat sheet covering query optimization, transactions, stored procedures, JSON functions, and window function examples.
New to SQL? Start Here First!
This sheet covers MySQL-specific advanced features. If you're new to SQL or need a refresher on basic SQL commands, we recommend starting with our SQL fundamentals sheet first.
Start with SQL FundamentalsSign in to mark items as known and track your progress.
Sign inMySQL Data Types & Storage
MySQL-specific data types and storage engines
MySQL Data Types
Common data types and their MySQL specifics
-- Numeric types
INT -- 4-byte integer
BIGINT -- 8-byte integer
DECIMAL(10,2) -- Exact decimal
FLOAT / DOUBLE -- Floating point
BIT(8) -- Bit values
-- String types
VARCHAR(255) -- Variable length (max 65,535)
CHAR(10) -- Fixed length
TEXT -- 65,535 chars
MEDIUMTEXT -- 16 MB
LONGTEXT -- 4 GB
-- Date/Time types
DATE -- YYYY-MM-DD
TIME -- HH:MM:SS
DATETIME -- YYYY-MM-DD HH:MM:SS
TIMESTAMP -- Auto-update capable
YEAR -- Year value
-- DETAILED_TAB:
-- AUTO_INCREMENT
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) UNIQUE,
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
-- ENUM and SET
CREATE TABLE products (
status ENUM('active', 'inactive', 'pending'),
tags SET('new', 'sale', 'featured')
);
-- Binary types
BINARY(16) -- Fixed binary (UUID)
VARBINARY(255) -- Variable binary
BLOB, MEDIUMBLOB, LONGBLOB -- Binary objectsStorage Engines
InnoDB vs MyISAM and other storage engines
-- Check storage engine
SHOW TABLE STATUS WHERE Name = 'users';
SHOW ENGINES;
-- Create with specific engine
CREATE TABLE transactions (
id INT PRIMARY KEY,
amount DECIMAL(10,2)
) ENGINE=InnoDB; -- Default, supports transactions
CREATE TABLE logs (
id INT PRIMARY KEY,
message TEXT
) ENGINE=MyISAM; -- Fast, no transactions
-- Change storage engine
ALTER TABLE users ENGINE=InnoDB;
-- DETAILED_TAB:
-- InnoDB features (default)
• Transactions (ACID compliant)
• Foreign keys
• Row-level locking
• Crash recovery
• Better for write-heavy
-- MyISAM features
• Table-level locking only
• No transactions
• Smaller disk footprint
• Better for read-heavy
• Full-text indexing (older MySQL)
-- Memory engine
CREATE TABLE cache (
key_name VARCHAR(255) PRIMARY KEY,
value TEXT
) ENGINE=MEMORY; -- RAM storage, lost on restart
-- Convert all tables to InnoDB
SELECT CONCAT('ALTER TABLE ', table_name, ' ENGINE=InnoDB;')
FROM information_schema.tables
WHERE engine = 'MyISAM' AND table_schema = 'your_db';JSON Support
Working with JSON data in MySQL 5.7+
-- Create table with JSON
CREATE TABLE events (
id INT AUTO_INCREMENT PRIMARY KEY,
data JSON NOT NULL
);
-- Insert JSON
INSERT INTO events (data) VALUES
('{"user": "john", "action": "login"}'),
(JSON_OBJECT('user', 'jane', 'action', 'purchase'));
-- Query JSON
SELECT data->>'$.user' AS username
FROM events
WHERE data->>'$.action' = 'login';
-- Update JSON field
UPDATE events
SET data = JSON_SET(data, '$.status', 'processed')
WHERE id = 1;
-- DETAILED_TAB:
-- JSON functions
SELECT
JSON_EXTRACT(data, '$.user') AS user,
JSON_TYPE(data),
JSON_VALID(data),
JSON_LENGTH(data),
JSON_KEYS(data)
FROM events;
-- JSON array operations
SELECT data->>'$.items[0]' AS first_item
FROM events
WHERE JSON_CONTAINS(data, '"book"', '$.items');
-- Create generated column from JSON
ALTER TABLE events
ADD COLUMN username VARCHAR(255)
GENERATED ALWAYS AS (data->>'$.user') STORED,
ADD INDEX idx_username(username);
-- JSON aggregation
SELECT JSON_ARRAYAGG(data) AS all_events
FROM events;Indexes & Performance
Optimize MySQL query performance
Index Types
Different index types and when to use them
-- Primary key (clustered)
ALTER TABLE users ADD PRIMARY KEY (id);
-- Unique index
CREATE UNIQUE INDEX uk_email ON users(email);
-- Composite index
CREATE INDEX idx_name ON users(last_name, first_name);
-- Prefix index (for long strings)
CREATE INDEX idx_email_prefix ON users(email(10));
-- Fulltext index
CREATE FULLTEXT INDEX ft_content
ON articles(title, content);
-- Spatial index
CREATE SPATIAL INDEX sp_location ON stores(location);
-- DETAILED_TAB:
-- Show indexes
SHOW INDEX FROM users;
-- Force/Ignore index
SELECT * FROM users
USE INDEX (idx_email)
WHERE email = 'john@example.com';
SELECT * FROM users
IGNORE INDEX (idx_name)
WHERE last_name = 'Smith';
-- Invisible index (MySQL 8.0+)
ALTER TABLE users ALTER INDEX idx_name INVISIBLE;
-- Descending index (MySQL 8.0+)
CREATE INDEX idx_created ON orders(created_at DESC);
-- Functional index (MySQL 8.0+)
CREATE INDEX idx_month
ON sales((MONTH(sale_date)));Query Optimization
Analyze and optimize slow queries
-- Explain query
EXPLAIN SELECT * FROM users WHERE email = 'john@example.com';
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'pending';
-- Enable slow query log
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
-- Query profiling
SET profiling = 1;
SELECT * FROM large_table WHERE status = 'active';
SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;
-- DETAILED_TAB:
-- Optimizer hints (MySQL 8.0+)
SELECT /*+ INDEX(users idx_email) */ *
FROM users WHERE email = 'john@example.com';
-- Analyze table statistics
ANALYZE TABLE users;
OPTIMIZE TABLE users; -- Defragment
-- Query cache (deprecated in 8.0)
SHOW VARIABLES LIKE 'query_cache%';
-- Performance schema
SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY sum_timer_wait DESC LIMIT 10;
-- Find tables without primary key
SELECT tables.table_schema, tables.table_name
FROM information_schema.tables
LEFT JOIN information_schema.key_column_usage AS c
ON tables.table_name = c.table_name
AND c.constraint_name = 'PRIMARY'
WHERE tables.table_schema NOT IN ('information_schema', 'mysql', 'performance_schema')
AND c.constraint_name IS NULL;Partitioning
Split large tables for better performance
-- Range partitioning
CREATE TABLE orders (
id INT,
created DATE,
amount DECIMAL(10,2)
)
PARTITION BY RANGE (YEAR(created)) (
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
-- List partitioning
CREATE TABLE users_by_region (
id INT,
region VARCHAR(10)
)
PARTITION BY LIST(region) (
PARTITION p_us VALUES IN ('US', 'CA'),
PARTITION p_eu VALUES IN ('UK', 'DE', 'FR'),
PARTITION p_asia VALUES IN ('JP', 'CN', 'IN')
);
-- DETAILED_TAB:
-- Hash partitioning
CREATE TABLE user_sessions (
id INT,
user_id INT,
data TEXT
)
PARTITION BY HASH(user_id)
PARTITIONS 10;
-- Manage partitions
ALTER TABLE orders ADD PARTITION (
PARTITION p2025 VALUES LESS THAN (2026)
);
ALTER TABLE orders DROP PARTITION p2022;
-- Query specific partition
SELECT * FROM orders PARTITION (p2024)
WHERE created >= '2024-01-01';
-- Check partition info
SELECT table_name, partition_name, table_rows
FROM information_schema.partitions
WHERE table_schema = 'your_db';Transactions & Locking
ACID compliance and concurrent access control
Transaction Control
Manage transactions and isolation levels
-- Start transaction
START TRANSACTION; -- or BEGIN
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- Rollback on error
START TRANSACTION;
UPDATE inventory SET quantity = quantity - 1 WHERE id = 123;
-- Error occurs
ROLLBACK;
-- Savepoints
START TRANSACTION;
UPDATE users SET credits = 100;
SAVEPOINT before_delete;
DELETE FROM logs;
ROLLBACK TO SAVEPOINT before_delete;
COMMIT;
-- DETAILED_TAB:
-- Isolation levels
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- Default
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Check current level
SELECT @@transaction_isolation;
-- Autocommit control
SET autocommit = 0; -- Manual commit required
SET autocommit = 1; -- Default, auto-commit each statement
-- Transaction with lock timeout
SET innodb_lock_wait_timeout = 5;
START TRANSACTION;
-- Operations here
COMMIT;Locking Mechanisms
Row-level and table-level locking strategies
-- Row-level locking (InnoDB)
START TRANSACTION;
SELECT * FROM orders
WHERE id = 123
FOR UPDATE; -- Exclusive lock
-- Do work
COMMIT;
-- Shared lock
SELECT * FROM products
WHERE category = 'electronics'
FOR SHARE; -- Multiple reads OK
-- Table locks
LOCK TABLES users WRITE, orders READ;
-- Do work
UNLOCK TABLES;
-- DETAILED_TAB:
-- Skip locked rows (MySQL 8.0+)
SELECT * FROM orders
WHERE status = 'pending'
FOR UPDATE SKIP LOCKED
LIMIT 10;
-- Nowait option (MySQL 8.0+)
SELECT * FROM inventory
WHERE product_id = 456
FOR UPDATE NOWAIT; -- Fail immediately if locked
-- Check locks
SHOW ENGINE INNODB STATUS;
-- Find blocking queries
SELECT * FROM information_schema.innodb_lock_waits;
-- Kill blocking connection
SHOW PROCESSLIST;
KILL CONNECTION 1234;
-- Deadlock retry pattern
DELIMITER $$
CREATE PROCEDURE transfer_with_retry()
BEGIN
DECLARE retry_count INT DEFAULT 3;
DECLARE CONTINUE HANDLER FOR 1213 -- Deadlock error
BEGIN
SET retry_count = retry_count - 1;
IF retry_count > 0 THEN
ROLLBACK;
START TRANSACTION;
END IF;
END;
-- Transaction logic here
END$$Stored Procedures & Functions
Server-side programming with MySQL
Stored Procedures
Create and use stored procedures
-- Simple procedure
DELIMITER $$
CREATE PROCEDURE GetUserById(IN user_id INT)
BEGIN
SELECT * FROM users WHERE id = user_id;
END$$
DELIMITER ;
-- Call procedure
CALL GetUserById(123);
-- Procedure with OUT parameter
DELIMITER $$
CREATE PROCEDURE GetUserCount(OUT total INT)
BEGIN
SELECT COUNT(*) INTO total FROM users;
END$$
DELIMITER ;
-- Call with output
CALL GetUserCount(@count);
SELECT @count;
-- DETAILED_TAB:
-- Complex procedure with error handling
DELIMITER $$
CREATE PROCEDURE TransferFunds(
IN from_account INT,
IN to_account INT,
IN amount DECIMAL(10,2)
)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
UPDATE accounts
SET balance = balance - amount
WHERE id = from_account AND balance >= amount;
IF ROW_COUNT() = 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Insufficient funds';
END IF;
UPDATE accounts
SET balance = balance + amount
WHERE id = to_account;
COMMIT;
END$$
DELIMITER ;Functions & Triggers
User-defined functions and automatic triggers
-- Create function
DELIMITER $$
CREATE FUNCTION CalculateTax(price DECIMAL(10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
RETURN price * 0.08;
END$$
DELIMITER ;
-- Use function
SELECT price, CalculateTax(price) as tax
FROM products;
-- Create trigger
DELIMITER $$
CREATE TRIGGER update_modified
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
SET NEW.modified_at = NOW();
END$$
DELIMITER ;
-- DETAILED_TAB:
-- Complex trigger with logging
DELIMITER $$
CREATE TRIGGER audit_user_changes
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
INSERT INTO audit_log (
table_name,
record_id,
action,
old_values,
new_values,
changed_by,
changed_at
) VALUES (
'users',
NEW.id,
'UPDATE',
JSON_OBJECT('name', OLD.name, 'email', OLD.email),
JSON_OBJECT('name', NEW.name, 'email', NEW.email),
USER(),
NOW()
);
END$$
DELIMITER ;
-- List triggers
SHOW TRIGGERS;
DROP TRIGGER IF EXISTS trigger_name;Replication & Backup
High availability and disaster recovery
Replication Setup
Configure master-slave replication
-- On master server
-- Edit my.cnf
[mysqld]
log-bin=mysql-bin
server-id=1
-- Create replication user
CREATE USER 'replica'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'replica'@'%';
-- Get master status
SHOW MASTER STATUS;
-- Note File and Position
-- On slave server
-- Edit my.cnf
[mysqld]
server-id=2
-- Configure slave
CHANGE MASTER TO
MASTER_HOST='master_ip',
MASTER_USER='replica',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=154;
-- Start replication
START SLAVE;
SHOW SLAVE STATUS\G
-- DETAILED_TAB:
-- Check replication lag
SHOW SLAVE STATUS\G
-- Look for Seconds_Behind_Master
-- Skip replication errors
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;
-- Reset replication
STOP SLAVE;
RESET SLAVE ALL;
-- Semi-synchronous replication
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
-- Read from slave, write to master pattern
-- Application logic:
-- Writes: connect to master
-- Reads: connect to slave (load balance)Backup & Restore
Backup strategies and recovery procedures
-- Logical backup with mysqldump
mysqldump -u root -p database_name > backup.sql
mysqldump -u root -p --all-databases > all_backup.sql
-- Backup with compression
mysqldump -u root -p database_name | gzip > backup.sql.gz
-- Backup specific tables
mysqldump -u root -p database_name table1 table2 > tables.sql
-- Restore from backup
mysql -u root -p database_name < backup.sql
-- DETAILED_TAB:
-- Consistent backup with single transaction
mysqldump --single-transaction --quick \
--lock-tables=false -u root -p database > backup.sql
-- Binary backup with Percona XtraBackup
xtrabackup --backup --target-dir=/backup/
xtrabackup --prepare --target-dir=/backup/
xtrabackup --copy-back --target-dir=/backup/
-- Point-in-time recovery
-- 1. Restore full backup
mysql -u root -p < full_backup.sql
-- 2. Apply binary logs
mysqlbinlog mysql-bin.000001 mysql-bin.000002 | mysql -u root -p
-- Export/Import with MySQL Shell
mysqlsh -u root -p
util.dumpSchemas(['database'], '/backup/dir')
util.loadDump('/backup/dir')
-- Verify backup
mysql -u root -p -e "SELECT COUNT(*) FROM database.table"Administration & Security
User management and security best practices
User Management
Create and manage MySQL users and permissions
-- Create user
CREATE USER 'john'@'localhost' IDENTIFIED BY 'password';
CREATE USER 'app'@'%' IDENTIFIED BY 'secure_password';
-- Grant privileges
GRANT SELECT, INSERT, UPDATE ON database.* TO 'john'@'localhost';
GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost' WITH GRANT OPTION;
-- Specific table permissions
GRANT SELECT, INSERT ON database.users TO 'app'@'%';
-- Show grants
SHOW GRANTS FOR 'john'@'localhost';
-- Revoke privileges
REVOKE INSERT ON database.* FROM 'john'@'localhost';
-- DETAILED_TAB:
-- Password management
ALTER USER 'john'@'localhost' IDENTIFIED BY 'new_password';
-- Password expiry
ALTER USER 'john'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;
-- Account locking
ALTER USER 'john'@'localhost' ACCOUNT LOCK;
ALTER USER 'john'@'localhost' ACCOUNT UNLOCK;
-- Resource limits
ALTER USER 'app'@'%' WITH
MAX_QUERIES_PER_HOUR 1000
MAX_CONNECTIONS_PER_HOUR 100
MAX_USER_CONNECTIONS 10;
-- List all users
SELECT user, host FROM mysql.user;
-- Drop user
DROP USER 'john'@'localhost';
-- Flush privileges (if modifying mysql tables directly)
FLUSH PRIVILEGES;Performance Monitoring
Monitor MySQL performance and health
-- Show current connections
SHOW PROCESSLIST;
SHOW FULL PROCESSLIST;
-- Kill long-running query
KILL QUERY 1234;
KILL CONNECTION 1234;
-- Server status
SHOW STATUS;
SHOW STATUS LIKE 'Threads%';
SHOW STATUS LIKE 'Innodb_buffer_pool%';
-- Variables
SHOW VARIABLES LIKE 'max_connections';
SHOW VARIABLES LIKE 'innodb%';
-- DETAILED_TAB:
-- Performance schema queries
SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY sum_timer_wait DESC LIMIT 10;
-- Table sizes
SELECT
table_schema AS 'Database',
table_name AS 'Table',
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)'
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema', 'mysql', 'performance_schema')
ORDER BY (data_length + index_length) DESC;
-- Connection stats
SHOW STATUS WHERE Variable_name IN (
'Connections',
'Max_used_connections',
'Aborted_connects',
'Threads_connected'
);
-- InnoDB metrics
SHOW ENGINE INNODB STATUS\G
-- Buffer pool efficiency
SELECT
(1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)) * 100
AS buffer_pool_hit_rate
FROM (
SELECT variable_name, variable_value
FROM information_schema.global_status
WHERE variable_name IN ('Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests')
) AS t;