MySQL vs MariaDB
Short version: either one will run a standard web application without complaint, so pick on the specifics. MySQL gives you the broader ecosystem, managed hosting on every provider, and multi-valued JSON indexes. MariaDB gives you system-versioned tables, sequences, thread pooling without an Enterprise license, and a plain GPL v2 with no proprietary edition above it. They still share a wire protocol, so this is one of the least permanent database decisions you will make.
Your new project needs a relational database and you've settled on the MySQL family. Maybe your framework defaults to it, maybe your hosting provider supports it, maybe you've just used it before and it works. Then someone on your team asks: "Why not MariaDB? It's the same thing, right?"
It's not quite the same thing. MariaDB forked from MySQL in 2009 after Oracle acquired Sun Microsystems, and the two have been diverging ever since. MariaDB added sequences and system-versioned tables. MySQL doubled down on JSON support and enterprise tooling. They share a wire protocol and most of the SQL syntax, but they make different tradeoffs that start to matter once your project grows past a prototype.
This post puts both side by side with runnable code so you can see exactly where they agree and where they don't.
Contents
- Quick Comparison
- Set Up Both Locally
- Project Setup
- Side-by-Side: The Basics
- What MariaDB Has That MySQL Doesn't
- What MySQL Has That MariaDB Doesn't
- Licensing and Governance
- Performance
- Ecosystem and Hosting
- When to Pick MySQL
- When to Pick MariaDB
- The Verdict
- FAQ
- Wrapping Up
Quick Comparison
| MySQL | MariaDB | |
|---|---|---|
| License | GPL v2 + proprietary (Oracle) | GPL v2 (MariaDB Foundation) |
| Governance | Oracle Corporation | MariaDB Foundation (community) |
| Sequences | No (use AUTO_INCREMENT workarounds) | Yes, first-class CREATE SEQUENCE |
| System-versioned tables | No | Yes, built-in temporal queries |
| JSON support | Strong (JSON_TABLE, multi-valued indexes on JSON arrays) | JSON_TABLE since 10.6, no multi-valued indexes |
| Default storage engine | InnoDB | InnoDB (also ships Aria, ColumnStore) |
| Oracle compatibility mode | No | Yes (SET SQL_MODE='ORACLE') |
| Wire protocol | MySQL protocol | MySQL protocol (compatible) |
| Node.js driver | mysql2 | mariadb (official connector) |
| Cloud availability | Every major provider (AWS RDS, GCP Cloud SQL, Azure) | AWS RDS, plus first-party MariaDB Cloud since 2025 |
The shared foundation means most ORMs, admin tools, and hosting providers treat them interchangeably. The differences emerge when you need a specific feature from one side.
Set Up Both Locally
We'll run both side by side using the Layerbase CLI (formerly SpinDB). No Docker, no manual installs. (What is the Layerbase CLI?)
Install the Layerbase CLI:
npm i -g layerbase # npm
pnpm add -g layerbase # pnpmCreate both instances. The Layerbase CLI assigns non-conflicting ports automatically:
lbase create mysql1 -e mysql --start
lbase create maria1 -e mariadb --startVerify they're running:
lbase url mysql1
lbase url maria1mysql://root@127.0.0.1:3306
mysql://root@127.0.0.1:3307/testYour ports may differ. The Layerbase CLI picks the next available port if the default is taken.
Or skip local setup entirely and spin up managed instances on Layerbase Cloud: MySQL or MariaDB.
Project Setup
MySQL and MariaDB have separate Node.js packages, so we need both drivers:
mkdir mysql-vs-mariadb && cd mysql-vs-mariadb
pnpm init
pnpm add mysql2 mariadb
pnpm add -D tsx typescriptCreate a file called compare.ts. All the code in this post goes into that file.
Side-by-Side: The Basics
Connect to both, create the same table, insert data, query it. The SQL is nearly identical; the driver APIs differ slightly.
import mysql from 'mysql2/promise'
import mariadb from 'mariadb'
// Connect to both
const my = await mysql.createConnection({
host: '127.0.0.1',
port: 3306,
user: 'root',
database: 'test',
})
const maria = await mariadb.createConnection({
host: '127.0.0.1',
port: 3307,
user: 'root',
database: 'test',
})
console.log('Connected to MySQL and MariaDB')Now create identical tables on both:
const createProducts = `
CREATE TABLE IF NOT EXISTS products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
sku VARCHAR(50) NOT NULL UNIQUE,
price DECIMAL(10, 2) NOT NULL,
category VARCHAR(100) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`
// Clean up from previous runs
await my.execute('DROP TABLE IF EXISTS products')
await maria.query('DROP TABLE IF EXISTS products')
await my.execute(createProducts)
await maria.query(createProducts)
console.log('Created products table on both databases')One driver difference to note: mysql2 uses .execute() for parameterized queries, while the mariadb connector uses .query() for everything. Both support ? placeholders.
Insert the same data into both:
const products = [
{ name: 'Mechanical Keyboard', sku: 'KB-001', price: 129.99, category: 'peripherals' },
{ name: '27" 4K Monitor', sku: 'MN-001', price: 449.99, category: 'displays' },
{ name: 'Standing Desk', sku: 'DK-001', price: 599.99, category: 'furniture' },
{ name: 'Wireless Mouse', sku: 'MS-001', price: 39.99, category: 'peripherals' },
{ name: 'Noise-Canceling Headphones', sku: 'HP-001', price: 249.99, category: 'audio' },
]
for (const p of products) {
const params = [p.name, p.sku, p.price, p.category]
const sql = 'INSERT INTO products (name, sku, price, category) VALUES (?, ?, ?, ?)'
await my.execute(sql, params)
await maria.query(sql, params)
}
console.log(`Inserted ${products.length} products into both databases`)Query both and compare results:
const myProducts = (await my.execute(
'SELECT name, price, category FROM products ORDER BY price DESC',
))[0] as Array<{ name: string; price: number; category: string }>
const mariaProducts = await maria.query(
'SELECT name, price, category FROM products ORDER BY price DESC',
)
console.log('\nMySQL results:')
for (const row of myProducts) {
console.log(` ${row.name}: $${Number(row.price).toFixed(2)}`)
}
console.log('\nMariaDB results:')
for (const row of mariaProducts) {
console.log(` ${row.name}: $${Number(row.price).toFixed(2)}`)
}MySQL results:
Standing Desk: $599.99
27" 4K Monitor: $449.99
Noise-Canceling Headphones: $249.99
Mechanical Keyboard: $129.99
Wireless Mouse: $39.99
MariaDB results:
Standing Desk: $599.99
27" 4K Monitor: $449.99
Noise-Canceling Headphones: $249.99
Mechanical Keyboard: $129.99
Wireless Mouse: $39.99Identical output. For basic CRUD, JOINs, and aggregations, they're interchangeable. The differences show up when you reach for more advanced features.
What MariaDB Has That MySQL Doesn't
Sequences
MySQL limits you to AUTO_INCREMENT on a single column per table. MariaDB has standalone sequences: named counters that live independently of any table. Useful for order numbers, invoice IDs, or any sequential value shared across tables.
await maria.query('DROP SEQUENCE IF EXISTS invoice_seq')
await maria.query(`
CREATE SEQUENCE invoice_seq
START WITH 5000
INCREMENT BY 1
`)
const invoiceNumbers = []
for (let i = 0; i < 4; i++) {
const [row] = await maria.query('SELECT NEXT VALUE FOR invoice_seq AS num')
invoiceNumbers.push(row.num)
}
console.log('\nMariaDB sequence-generated invoice numbers:', invoiceNumbers)MariaDB sequence-generated invoice numbers: [ 5000, 5001, 5002, 5003 ]In MySQL, you'd create a dedicated single-row table, lock it with SELECT ... FOR UPDATE, increment, and commit. A lot of ceremony for something that should be trivial.
System-Versioned Tables
This is MariaDB's strongest differentiator, and I think it's genuinely underrated. System versioning lets the database automatically track every historical version of every row. No triggers, no audit tables, no application code.
await maria.query('ALTER TABLE products ADD SYSTEM VERSIONING')
// Record the timestamp before changes
const [{ now: beforeChanges }] = await maria.query('SELECT NOW(6) AS now')
// Simulate a price increase on peripherals
await maria.query(`UPDATE products SET price = price * 1.15 WHERE category = 'peripherals'`)
// Query the table as it was before the update
const before = await maria.query(
`SELECT name, price FROM products FOR SYSTEM_TIME AS OF ?
WHERE category = 'peripherals' ORDER BY name`,
[beforeChanges],
)
const after = await maria.query(
`SELECT name, price FROM products WHERE category = 'peripherals' ORDER BY name`,
)
console.log('\nMariaDB temporal query - peripherals before price change:')
for (const row of before) {
console.log(` ${row.name}: $${Number(row.price).toFixed(2)}`)
}
console.log('\nPeripherals after price change:')
for (const row of after) {
console.log(` ${row.name}: $${Number(row.price).toFixed(2)}`)
}MariaDB temporal query - peripherals before price change:
Mechanical Keyboard: $129.99
Wireless Mouse: $39.99
Peripherals after price change:
Mechanical Keyboard: $149.49
Wireless Mouse: $45.99The FOR SYSTEM_TIME AS OF clause lets you query any point in the past. MySQL has no equivalent. You'd have to build a separate audit table, write triggers on every INSERT/UPDATE/DELETE, and write custom queries to reconstruct past states. MariaDB gives you this with a single ALTER TABLE statement.
This matters for compliance (GDPR audit trails, financial record-keeping), debugging ("what did the config look like yesterday at 3pm?"), and recovery ("someone deleted the wrong rows, can we get them back?"). I've seen teams spend weeks building audit infrastructure that MariaDB gives you for free.
What MySQL Has That MariaDB Doesn't
Advanced JSON Operations
Both support JSON columns and both have JSON_TABLE, which MariaDB added in 10.6. Where MySQL still pulls ahead is the indexing and validation layer around JSON rather than the query functions themselves.
JSON_TABLE converts JSON arrays into relational rows you can JOIN against. This example is MySQL, and the same query works on MariaDB 10.6 and later:
await my.execute('DROP TABLE IF EXISTS orders')
await my.execute(`
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
customer VARCHAR(100) NOT NULL,
items JSON NOT NULL
)
`)
await my.execute(
`INSERT INTO orders (customer, items) VALUES
('Alice', '["KB-001", "MS-001"]'),
('Bob', '["MN-001", "DK-001", "HP-001"]')`,
)
const [orderDetails] = await my.execute(`
SELECT
o.customer,
jt.sku,
p.name,
p.price
FROM orders o,
JSON_TABLE(o.items, '$[*]' COLUMNS (sku VARCHAR(50) PATH '$')) AS jt
JOIN products p ON p.sku = jt.sku
ORDER BY o.customer, p.price DESC
`)
console.log('\nMySQL JSON_TABLE - orders with product details:')
for (const row of orderDetails as Array<{ customer: string; sku: string; name: string; price: number }>) {
console.log(` ${row.customer}: ${row.name} ($${Number(row.price).toFixed(2)})`)
}MySQL JSON_TABLE - orders with product details:
Alice: Mechanical Keyboard ($129.99)
Alice: Wireless Mouse ($39.99)
Bob: Standing Desk ($599.99)
Bob: 27" 4K Monitor ($449.99)
Bob: Noise-Canceling Headphones ($249.99)The real MySQL advantage is multi-valued indexes on JSON arrays, which index the individual elements so a lookup into an array does not scan the table. MariaDB has no equivalent, so at scale you either normalize those arrays into a proper relational table or accept the scan. Honestly, normalization is often the better design anyway, but sometimes you need the flexibility.
(JSON claims re-checked 2026-08-25 against the MariaDB JSON_TABLE reference, which states JSON_TABLE is available from MariaDB 10.6. An earlier version of this post said MariaDB lacked it until 11.2, which was wrong.)
Licensing and Governance
This is where opinions get strong.
MySQL is dual-licensed: GPL v2 for the community edition, proprietary for enterprise. Oracle controls the roadmap, the release schedule, and which features land in community vs. enterprise. Community MySQL is fully functional, but Thread Pool, audit logging, and MySQL Router for high availability are paid.
MariaDB is GPL v2, period. The MariaDB Foundation oversees it as a genuine open-source effort. No proprietary fork with hidden features. The MariaDB Corporation (a separate entity) sells commercial support and the MaxScale proxy, but the database itself is fully open.
If your organization cares about software freedom or long-term license stability, MariaDB's governance model is more predictable. If you want Oracle-backed enterprise support and don't mind the dual-license model, MySQL Enterprise is a polished product.
Performance
For most workloads, performance shouldn't be your deciding factor. Both use InnoDB. Both handle concurrent reads and writes well. Both support connection pooling, query caching, and the usual tuning knobs.
Where they diverge:
- Thread pool: MySQL gates this behind Enterprise. MariaDB includes it in the community edition, which can improve performance under high concurrency
- Optimizer: MariaDB's query optimizer has diverged from MySQL's and sometimes produces different execution plans. In some benchmarks MariaDB is faster for complex JOINs; in others MySQL wins on simple lookups. Benchmark your actual queries
- Storage engines: MariaDB ships Aria (crash-safe MyISAM replacement) and ColumnStore (for analytics). These don't affect InnoDB performance but give you more options for specialized workloads
Ecosystem and Hosting
MySQL wins on ecosystem breadth. It's not particularly close.
Every major cloud provider offers managed MySQL: AWS RDS, Google Cloud SQL, Azure Database for MySQL, DigitalOcean, PlanetScale. MariaDB is available on AWS RDS but not as universally across the other hyperscalers.
The first-party picture changed in 2025, though. MariaDB plc reacquired SkySQL in August 2025 and relaunched the managed offering as MariaDB Cloud, so there is now a self-serve managed MariaDB from the company that makes the database, which was not true when this post first went up. It is still a smaller footprint than managed MySQL, which runs on essentially every provider you might already be using. (Verified 2026-08-25 against mariadb.com/products/cloud.)
WordPress, Drupal, Magento, and most PHP-era CMSes were built on MySQL. They generally work with MariaDB (many Linux distributions have actually switched their default to MariaDB), but MySQL is what the documentation targets.
ORMs and database tools support both through the shared wire protocol. Prisma, Drizzle, TypeORM, Sequelize, Knex, TablePlus: they all connect to either one. You rarely need to change application code when switching.
Layerbase Cloud supports both MySQL and MariaDB as managed instances with TLS, so you can try either one without committing to a local install. It can also bring an existing database in: pick Migrating from another platform in the create flow and paste a MySQL/MariaDB connection string (it runs mysqldump once and copies schema + data), or, if you're leaving PlanetScale, paste a service token and it pulls the database across for you.
When to Pick MySQL
- Your hosting provider offers managed MySQL but not MariaDB. Don't fight your platform.
- You lean heavily on JSON.
JSON_TABLE, multi-valued indexes, andJSON_SCHEMA_VALIDare genuinely useful if your schema mixes relational and document data. - You want Oracle-backed enterprise support. MySQL Enterprise includes commercial SLAs, Oracle Cloud integration, and HeatWave analytics.
- Your existing stack assumes MySQL. WordPress, Laravel Forge, Vitess: dozens of tools are MySQL-first. If you're deep in that ecosystem, stick with it.
- You value the largest possible hiring pool. More developers have MySQL on their resume than MariaDB.
When to Pick MariaDB
- You need temporal data. System-versioned tables alone justify MariaDB for audit-heavy applications. Building the equivalent in MySQL takes significant custom infrastructure.
- You want sequences.
CREATE SEQUENCEbeats the MySQL workaround of a dedicated table with row-level locking. Not even close. - Open-source governance matters. If you need certainty that your database will stay fully open, MariaDB's Foundation model provides that.
- You're migrating from Oracle DB. Oracle compatibility mode (
SET SQL_MODE='ORACLE') eases the transition. - You want thread pooling without paying for Enterprise. MariaDB includes it out of the box.
The Verdict
If you're starting a new project and don't have specific requirements pulling you one way, here's the practical answer:
Pick MySQL if you want the largest ecosystem, the most managed hosting options, and strong JSON support. It's the safe default with the widest safety net.
Pick MariaDB if you need system-versioned tables, care about open-source licensing, or want features like sequences and thread pooling that MySQL locks behind Enterprise. MariaDB gives you more database for fewer constraints. If that is where you land, managed MariaDB on Layerbase runs the same server you just tested locally.
Both are production-grade with active communities and long track records. You won't regret either choice for a standard web application. The differences matter at the margins. This post should help you figure out which margins matter to you.
FAQ
Is MariaDB the same as MySQL?
Not any more, though they started that way. MariaDB forked from MySQL in 2009 after Oracle acquired Sun Microsystems, and sixteen years of separate development has put real distance between them. They still share the wire protocol and most of the SQL syntax, which is why your ORM and your admin tools treat them interchangeably, but the feature sets, the optimizers, and the licensing have all diverged.
Can I switch from MySQL to MariaDB without changing my code?
For a normal application, usually yes. The shared wire protocol means Prisma, Drizzle, TypeORM, Sequelize, Knex, and TablePlus all connect to either one, and the SQL you wrote for a CRUD app is very likely portable. Where it gets interesting is if you leaned on something version-specific: multi-valued JSON indexes going to MariaDB, or system-versioned tables and sequences coming back the other way. Dump, restore into the other engine, and run your test suite; that is the honest way to find out. What actually breaks on the way into MariaDB is the error-by-error version of that dump, and the free way to try it.
Which is faster, MySQL or MariaDB?
Close enough that it should not decide this for you. Both use InnoDB, so the baseline is the same engine. The optimizers have diverged and produce different execution plans, which means MariaDB wins some complex JOIN benchmarks and MySQL wins some simple-lookup ones. The one structural difference worth knowing is that MariaDB ships thread pooling in the community edition while MySQL keeps it behind Enterprise, which shows up under high concurrency.
Is MariaDB really free?
Yes, GPL v2 with no proprietary edition held above it. MariaDB plc sells commercial support and the MaxScale proxy, but the database itself is fully open, and features like thread pooling that MySQL gates behind Enterprise ship in the community build. MySQL is dual-licensed: the community edition is GPL v2 and genuinely functional, while Thread Pool, audit logging, and MySQL Router for high availability are paid.
Does MariaDB support JSON_TABLE?
Yes, since MariaDB 10.6. This is a common misconception and one this post got wrong until August 2026. What MariaDB still does not have is multi-valued indexes on JSON arrays, so if you need to index individual array elements rather than just query them, that remains a MySQL-only capability.
Is there a managed MariaDB cloud service?
Several. AWS RDS has offered MariaDB for years, and MariaDB plc reacquired SkySQL in August 2025 and relaunched it as MariaDB Cloud, so there is a self-serve first-party option again. Layerbase Cloud runs managed MariaDB too, next to MySQL, so you can put both on the same account and compare them against your own schema.
Which should I pick for a new project in 2026?
MySQL if your hosting or framework already assumes it, if you are deep in the WordPress or Laravel ecosystem, or if you need multi-valued JSON indexes. MariaDB if you want system-versioned tables for audit history, sequences instead of AUTO_INCREMENT workarounds, thread pooling without an Enterprise bill, or a license with nothing proprietary above it. With no specific requirement pulling either way, MySQL is the safer default purely on ecosystem breadth.
Wrapping Up
Close your connections:
await my.end()
await maria.end()To manage your local instances with the Layerbase CLI:
lbase stop mysql1 # Stop MySQL
lbase stop maria1 # Stop MariaDB
lbase start mysql1 # Start MySQL again
lbase start maria1 # Start MariaDB again
lbase list # See all your database instancesThe Layerbase CLI supports 20+ database engines, so you can run MySQL and MariaDB side by side without juggling separate installs. Layerbase Desktop is available if you prefer a GUI on macOS.
Keep reading
- Free MySQL hosting: start on MariaDB, upgrade to MySQL when you need itMySQL is not on the Layerbase free tier and MariaDB is, and they speak the same wire protocol. Here is the dump command that survives a restricted source account, every error the restore actually throws, what MySQL keeps that MariaDB does not, and the route back to real MySQL later.
- Moving a MySQL database off cPanel shared hostingYour database is on a shared host that binds MySQL to localhost, runs a version you did not pick, and keeps the only backup. Here is the whole move: getting a dump that is actually complete, landing it on managed MariaDB, repointing the app, and the parts of shared hosting you will genuinely miss.
- Migrating from PlanetScale to LayerbasePlanetScale retired its free Hobby tier, and the smallest Vitess MySQL cluster is now a 3-node HA SKU at $39 a month. Here is how to move to plain managed MySQL on Layerbase: what changes, how to copy your data with one service token, and the driver swap.
- SkySQL alternatives: serverless MariaDB hosting in 2026MariaDB shut SkySQL down in 2023, spun it out, then bought it back in 2025 and renamed it MariaDB Cloud. Here is what managed MariaDB hosting actually looks like now, and the options worth comparing.