MySQL#
Relational Strong consistency Storage engines
What it is#
MySQL is an open source relational database, owned by Oracle since 2010, with MariaDB as its most popular community fork. It's the M in the classic LAMP stack and still runs a huge share of the web's CMSes, e-commerce platforms, and internal tools.
How it works under the hood#
MySQL's defining trait is pluggable storage engines: the SQL layer on top is the same, but the actual storage and locking behavior underneath can differ. InnoDB is the default and only one most people use today, it's transactional, MVCC-based, and row-locking. The older MyISAM engine is table-locking and non-transactional, mostly a historical footnote now.
Why storage engines matter todayEven though InnoDB is the default, the engine concept still shows up: some managed MySQL offerings and special…
Even though InnoDB is the default, the engine concept still shows up: some managed MySQL offerings and specialized forks use alternative engines for specific workloads (columnar analytics, in-memory tables). Knowing SHOW ENGINES; exists saves confusion later.
Setting it up#
docker run --name mysql-db -e MYSQL_ROOT_PASSWORD=secret -p 3306:3306 -d mysql:8
sudo apt install mysql-server
sudo mysql_secure_installation
brew install mysql
brew services start mysql
Your first queries#
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT NOT NULL,
total DECIMAL(10,2) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
INSERT INTO orders (customer_id, total) VALUES (42, 19.99);
SELECT customer_id, SUM(total) AS spent
FROM orders
GROUP BY customer_id
ORDER BY spent DESC
LIMIT 10;
Notable functionality#
Classic binlog-based async replication
Group Replication for multi-primary setups
Widely supported by every managed cloud offering
Native
JSONcolumn type since 5.7Generated columns can index into JSON fields
Not as deep as Postgres's JSONB, but solid for most needs
Indexing and the clustered key#
InnoDB tables are clustered on the primary key, meaning row data is physically stored in primary key order. A poorly chosen primary key (like a random UUID) can fragment the table and hurt write performance far more than in databases without clustered storage.
Index type | Notes |
|---|---|
Primary key | Clustered, meaning the actual row storage order |
Secondary index | Stores the primary key value, not a row pointer, one extra lookup |
Fulltext | Basic text search, much simpler than Postgres's text search |
In production#
-- Find the queries eating the most time right now
SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY sum_timer_wait DESC
LIMIT 5;
app → read replica (SELECT)
app → primary (INSERT/UPDATE/DELETE)
Warning
Auto-incrementing integer primary keys and replication don't always play nicely with multi-primary setups, two primaries can generate the same ID for different rows unless you configure auto_increment_offset correctly.
Pros and cons#
Extremely well understood, huge hosting and tooling ecosystem
Fast for simple read-heavy web workloads
Easy replication setup for read scaling
Free and open source (with a commercial tier from Oracle)
Historically weaker standards compliance than Postgres
JSON and advanced data types feel bolted on, not native
Clustered primary key design punishes bad key choices
Fewer extensibility options than Postgres's extension system
When to reach for it#
Good fits: WordPress and other PHP-ecosystem applications, teams that already have deep MySQL operational experience, and read-heavy web applications where the tooling and hosting ecosystem matters as much as the database engine itself.
When it's not the right fit#
Applications leaning heavily on advanced SQL (window functions history was rocky, though modern MySQL has caught up) or rich JSON querying
Teams that want a single extensible platform instead of bolting on separate tools for search, geospatial, etc.
read-write-splitting covers the replica patterns MySQL setups lean on heavily.