Mysql Best Practices

by mindrally97184105b5daNo license269 starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 5 weeks ago

MySQL development best practices for schema design, query optimization, and database administration

AI-generated overview

Reference guidance on MySQL schema design, indexing, query optimization, transactions, replication, security and maintenance.

What it does
Provides MySQL development best practices covering storage engine selection, data types, primary keys, indexing strategies, query optimization with EXPLAIN, JSON support, transaction management, replication, security and routine maintenance. It supplies SQL examples and configuration recommendations that an agent can apply when advising on or reviewing database work. It is an instructions-only skill and produces no files or scripts.
When to use it
Use it when designing or reviewing MySQL schemas, indexes or queries, or when advising on MySQL administration, replication, security hardening or configuration. It suits database performance and maintenance questions rather than general application coding.
Requirements
No scripts or tooling are included; it is guidance only. Applying the advice assumes access to a MySQL server and client for running the example SQL, but nothing is required to read the instructions.

MySQL Best Practices

Core Principles

  • Design schemas with appropriate storage engines (InnoDB for most use cases)
  • Optimize queries using EXPLAIN and proper indexing
  • Use proper data types to minimize storage and improve performance
  • Implement connection pooling and query caching appropriately
  • Follow MySQL-specific security hardening practices

Schema Design

Storage Engine Selection

  • Use InnoDB as the default engine (ACID compliant, row-level locking)
  • Consider MyISAM only for read-heavy, non-transactional workloads
  • Use MEMORY engine for temporary tables with high-speed requirements
sql
CREATE TABLE orders (    order_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,    customer_id INT UNSIGNED NOT NULL,    order_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,    total_amount DECIMAL(12, 2) NOT NULL,    status ENUM('pending', 'processing', 'shipped', 'delivered', 'cancelled')        NOT NULL DEFAULT 'pending',    INDEX idx_customer (customer_id),    INDEX idx_date_status (order_date, status),    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)        ON DELETE RESTRICT ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Data Types

  • Use smallest data type that fits your needs
  • Prefer INT UNSIGNED over BIGINT when possible
  • Use DECIMAL for financial calculations, not FLOAT/DOUBLE
  • Use ENUM for fixed sets of values
  • Use VARCHAR for variable-length strings, CHAR for fixed-length
  • Always use utf8mb4 charset for full Unicode support
sql
-- Appropriate data type selectionCREATE TABLE products (    product_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,    sku VARCHAR(50) NOT NULL,    name VARCHAR(255) NOT NULL,    description TEXT,    price DECIMAL(10, 2) NOT NULL,    quantity SMALLINT UNSIGNED NOT NULL DEFAULT 0,    weight DECIMAL(8, 3),    is_active TINYINT(1) NOT NULL DEFAULT 1,    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,    UNIQUE KEY uk_sku (sku)) ENGINE=InnoDB;

Primary Keys

  • Use AUTO_INCREMENT integer primary keys for InnoDB tables
  • Consider UUIDs stored as BINARY(16) for distributed systems
  • Avoid composite primary keys when possible
sql
-- UUID storage optimizationCREATE TABLE distributed_events (    event_id BINARY(16) PRIMARY KEY,    event_type VARCHAR(50) NOT NULL,    payload JSON,    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
-- Insert with UUIDINSERT INTO distributed_events (event_id, event_type, payload)VALUES (UUID_TO_BIN(UUID()), 'user_signup', '{"user_id": 123}');
-- Query with UUIDSELECT * FROM distributed_eventsWHERE event_id = UUID_TO_BIN('550e8400-e29b-41d4-a716-446655440000');

Indexing Strategies

Index Types

  • Use B-tree indexes (default) for most queries
  • Use FULLTEXT indexes for text search
  • Use SPATIAL indexes for geographic data
  • Consider covering indexes for frequently executed queries
sql
-- Composite index for common query patternsCREATE INDEX idx_orders_customer_date ON orders(customer_id, order_date);
-- Covering indexCREATE INDEX idx_orders_covering ON orders(customer_id, order_date, status, total_amount);
-- Fulltext index for searchALTER TABLE products ADD FULLTEXT INDEX ft_name_desc (name, description);
-- Search using fulltextSELECT * FROM productsWHERE MATCH(name, description) AGAINST('wireless bluetooth' IN NATURAL LANGUAGE MODE);

Index Guidelines

  • Index columns used in WHERE, JOIN, ORDER BY, and GROUP BY
  • Place most selective columns first in composite indexes
  • Avoid indexing low-cardinality columns alone
  • Monitor and remove unused indexes
sql
-- Check index usageSELECT    table_schema, table_name, index_name,    seq_in_index, column_name, cardinalityFROM information_schema.STATISTICSWHERE table_schema = 'your_database'ORDER BY table_name, index_name, seq_in_index;

Query Optimization

EXPLAIN Analysis

  • Use EXPLAIN to analyze query execution plans
  • Look for full table scans (type: ALL)
  • Check for proper index usage
  • Monitor rows examined vs rows returned
sql
EXPLAIN FORMAT=JSONSELECT c.name, COUNT(o.order_id) AS order_countFROM customers cLEFT JOIN orders o ON c.customer_id = o.customer_idWHERE c.created_at > '2024-01-01'GROUP BY c.customer_id;

Query Best Practices

  • Avoid SELECT * in production code
  • Use LIMIT for pagination
  • Prefer JOINs over subqueries when possible
  • Use prepared statements for repeated queries
sql
-- Efficient paginationSELECT order_id, order_date, total_amountFROM ordersWHERE customer_id = ?ORDER BY order_date DESCLIMIT 20 OFFSET 0;
-- Keyset pagination (more efficient for large offsets)SELECT order_id, order_date, total_amountFROM ordersWHERE customer_id = ?    AND (order_date, order_id) < (?, ?)ORDER BY order_date DESC, order_id DESCLIMIT 20;

Avoiding Common Pitfalls

sql
-- Avoid: Function on indexed columnSELECT * FROM orders WHERE YEAR(order_date) = 2024;
-- Preferred: Range comparisonSELECT * FROM ordersWHERE order_date >= '2024-01-01' AND order_date < '2025-01-01';
-- Avoid: Implicit type conversionSELECT * FROM users WHERE user_id = '123';  -- user_id is INT
-- Preferred: Proper typesSELECT * FROM users WHERE user_id = 123;
-- Avoid: LIKE with leading wildcardSELECT * FROM products WHERE name LIKE '%phone%';
-- Preferred: Fulltext search for text matchingSELECT * FROM products WHERE MATCH(name) AGAINST('phone');

JSON Support

  • Use JSON data type for semi-structured data (MySQL 5.7+)
  • Create generated columns for frequently accessed JSON fields
  • Use appropriate JSON functions for queries
sql
CREATE TABLE events (    event_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,    event_type VARCHAR(50) NOT NULL,    payload JSON NOT NULL,    -- Generated column for indexing    user_id INT UNSIGNED AS (payload->>'$.user_id') STORED,    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,    INDEX idx_user_id (user_id));
-- Query JSON dataSELECT event_id, event_type,       JSON_EXTRACT(payload, '$.action') AS actionFROM eventsWHERE JSON_EXTRACT(payload, '$.user_id') = 123;
-- Or using -> operatorSELECT * FROM events WHERE payload->'$.user_id' = 123;

Transaction Management

  • Use InnoDB for transactional tables
  • Keep transactions short to minimize lock contention
  • Choose appropriate isolation level
  • Handle deadlocks gracefully
sql
-- Transaction with error handlingSTART TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- Check for errors and commit or rollbackCOMMIT;
-- Set isolation levelSET TRANSACTION ISOLATION LEVEL READ COMMITTED;

Replication and High Availability

Read Replicas

  • Direct read queries to replicas
  • Use connection pooling with read/write splitting
  • Monitor replication lag
sql
-- Check replication statusSHOW SLAVE STATUS\G
-- Check replication lagSELECT TIMESTAMPDIFF(SECOND,    MAX(LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP),    NOW()) AS lag_secondsFROM performance_schema.replication_applier_status_by_worker;

Security

  • Use strong passwords and secure connections (SSL/TLS)
  • Apply principle of least privilege
  • Use prepared statements to prevent SQL injection
  • Audit sensitive operations
sql
-- Create user with limited privilegesCREATE USER 'app_user'@'%' IDENTIFIED BY 'secure_password';GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'app_user'@'%';FLUSH PRIVILEGES;
-- Require SSLALTER USER 'app_user'@'%' REQUIRE SSL;
-- View user privilegesSHOW GRANTS FOR 'app_user'@'%';

Maintenance

Regular Maintenance Tasks

sql
-- Analyze tables for optimizer statisticsANALYZE TABLE orders, customers, products;
-- Optimize tables (reclaim space, defragment)OPTIMIZE TABLE orders;
-- Check table integrityCHECK TABLE orders;

Monitoring Queries

sql
-- Find slow queriesSELECT * FROM mysql.slow_log ORDER BY query_time DESC LIMIT 10;
-- Current process listSHOW FULL PROCESSLIST;
-- InnoDB statusSHOW ENGINE INNODB STATUS;
-- Table sizesSELECT    table_name,    ROUND(data_length / 1024 / 1024, 2) AS data_mb,    ROUND(index_length / 1024 / 1024, 2) AS index_mb,    table_rowsFROM information_schema.TABLESWHERE table_schema = 'your_database'ORDER BY data_length DESC;

Configuration Recommendations

ini
# my.cnf recommended settings
[mysqld]# InnoDB settingsinnodb_buffer_pool_size = 70%_of_RAMinnodb_log_file_size = 256Minnodb_flush_log_at_trx_commit = 1innodb_flush_method = O_DIRECT
# Connection settingsmax_connections = 500wait_timeout = 300interactive_timeout = 300
# Query cache (disabled in MySQL 8.0+)query_cache_type = 0
# Slow query logslow_query_log = 1slow_query_log_file = /var/log/mysql/slow.loglong_query_time = 2

Source and attribution

Source:mindrally/skillsinmysql-best-practicesat commit9718410

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal

Mysql Best Practices Agent Skill | SourceWeft