MySQL doesn't get the enthusiastic default recommendation Postgres does anymore, but it still powers a huge share of the web's largest applications, and dismissing it outright ignores real reasons teams still choose it deliberately.
MySQL is a mature, widely deployed relational database known for its simplicity, strong replication story, and the InnoDB storage engine providing full ACID transaction support. Its ecosystem — hosting options, tooling, and operational knowledge across the industry — is enormous, which counts for a lot in practice even when a feature-by-feature comparison favors Postgres.
Why MySQL Matters (and When to Skip It)
MySQL's replication model (especially with InnoDB) is battle-tested at extreme scale — some of the largest web applications in the world run on MySQL specifically because its read-replica scaling story is simple and well understood. It's also frequently the default or most-supported option on managed platforms and legacy hosting environments, which is a real practical consideration, not just inertia.
Skip MySQL if you specifically need Postgres's more advanced feature set — richer JSONB support, more sophisticated indexing options (GIN, GiST), or window functions and CTEs that historically were more mature in Postgres (though MySQL has closed much of this gap in recent versions).
Getting Started with MySQL
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_user_id (user_id)
) ENGINE=InnoDB;
Using it from Node.js:
import mysql from "mysql2/promise";
const pool = mysql.createPool({ uri: process.env.DATABASE_URL });
const [rows] = await pool.query("SELECT * FROM users WHERE id = ?", [userId]);
Core MySQL Concepts Every Developer Should Know
InnoDB is the storage engine you almost always want. It's been the default since MySQL 5.5 and provides row-level locking, foreign key support, and full ACID transactions — the older MyISAM engine lacks transaction support and should be avoided for anything needing data integrity guarantees.
Replication is a first-class, mature feature. MySQL's binlog-based replication (statement-based, row-based, or mixed) powers read-replica scaling patterns that are simple to reason about and widely documented:
-- on a replica, reads scale horizontally while writes stay on the primary
SHOW SLAVE STATUS\G
EXPLAIN works the same way conceptually as in Postgres, showing you the query execution plan and whether indexes are being used effectively:
EXPLAIN SELECT * FROM posts WHERE user_id = 5;
Character set and collation choices matter more than in some other databases. Using utf8mb4 (not the legacy utf8, which doesn't support full Unicode including emoji) is the correct default for new tables — an easy mistake to carry forward from older MySQL conventions.
Common MySQL Mistakes and How to Fix Them
Mistake 1: using the legacy utf8 character set instead of utf8mb4. This is a surprisingly persistent default-configuration mistake that causes silent data issues with certain Unicode characters. Fix: always specify utf8mb4 explicitly for new databases and tables.
Mistake 2: relying on MyISAM for tables needing transactional integrity. Some older tutorials and legacy schemas still default to MyISAM, which lacks foreign keys and transactions entirely. Fix: use InnoDB (the modern default) for any table where data integrity matters.
Mistake 3: not indexing foreign keys explicitly. Similar to Postgres, foreign key columns need an explicit index for join performance — MySQL does auto-create an index for foreign key constraints in InnoDB, but composite query patterns still need deliberate index design beyond that baseline. Fix: verify actual query patterns with EXPLAIN rather than assuming the auto-created index covers every case.
When Should You Use MySQL Instead of PostgreSQL?
Use MySQL when your hosting environment, existing team expertise, or a specific platform's tooling favors it, or when you need its particular replication characteristics at scale. Use PostgreSQL as the general default for new projects without a specific reason to choose otherwise — its broader feature set (especially around JSONB and advanced indexing) covers more use cases out of the box.
MySQL in Production
Use a managed MySQL service (PlanetScale, AWS RDS, Google Cloud SQL) for production rather than self-hosting unless you have specific infrastructure requirements — replication setup, backups, and failover are meaningfully easier managed. Also monitor slow query logs and index usage the same way you would with any relational database; MySQL's tooling for this (performance_schema, slow query log) is mature and worth enabling from the start.
If you're inheriting an existing MySQL codebase, there's rarely a strong case for migrating to Postgres purely on principle — MySQL handles the vast majority of real-world relational workloads well; migrate only if you hit a genuine feature gap.