PostgreSQL vs MySQL: Kapan Pakai yang Mana?
Sabtu, 27 Des 2025
Sebagai developer, database adalah fondasi dari hampir setiap aplikasi. PostgreSQL dan MySQL sama-sama populer, tapi karakteristiknya berbeda. Artikel ini akan membantu kamu memilih yang tepat untuk project-mu.
Overview Singkat
PostgreSQL: The Feature-Rich Database
PostgreSQL (atau “Postgres”) adalah database relasional yang dikenal dengan fitur advanced dan standards compliance. Dikembangkan sejak 1996, Postgres punya reputasi sebagai database yang powerful untuk use case kompleks.
-- PostgreSQL mendukung data types yang kaya
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
metadata JSONB, -- Native JSON dengan indexing
tags TEXT[], -- Native array support
price NUMERIC(10,2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
Filosofi: Extensibility dan SQL compliance yang ketat.
MySQL: The Reliable Workhorse
MySQL adalah database relasional paling populer di dunia. Diakuisisi Oracle dan juga punya fork populer MariaDB. MySQL dikenal karena simplicity dan reliability.
-- MySQL syntax yang straightforward
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
metadata JSON,
price DECIMAL(10,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Filosofi: Simple, fast, dan reliable untuk web applications.
Tabel Perbandingan Fitur
| Fitur | PostgreSQL | MySQL |
|---|---|---|
| ACID Compliance | ✅ Full ACID | ✅ Full ACID (InnoDB) |
| JSON Support | JSONB dengan indexing | JSON (tanpa indexing native) |
| Full-Text Search | ✅ Built-in excellent | ✅ Built-in basic |
| Replication | Streaming, logical | Master-slave, group |
| Stored Procedures | PL/pgSQL, Python, etc | SQL/PSM |
| Extensions | ✅ Sangat extensible | ❌ Terbatas |
| CTE (WITH clause) | ✅ Recursive support | ✅ Since MySQL 8.0 |
| Window Functions | ✅ Complete | ✅ Since MySQL 8.0 |
| UPSERT | ON CONFLICT | ON DUPLICATE KEY |
| Partial Indexes | ✅ Ya | ❌ Tidak |
| GIS/Spatial | PostGIS (excellent) | Spatial (basic) |
Deep Dive: ACID Compliance
Keduanya mendukung ACID, tapi ada nuansa penting:
PostgreSQL
- ACID compliance by default
- MVCC (Multi-Version Concurrency Control) yang mature
- Isolation levels lengkap termasuk Serializable
MySQL
- ACID compliance hanya dengan InnoDB engine
- MyISAM (engine lama) tidak ACID compliant
- Default isolation level: REPEATABLE READ
-- PostgreSQL: Check isolation level
SHOW transaction_isolation;
-- Default: read committed
-- MySQL: Check isolation level
SELECT @@transaction_isolation;
-- Default: REPEATABLE-READ
Deep Dive: JSON Support
Ini salah satu perbedaan terbesar yang sering jadi deciding factor.
PostgreSQL JSONB
-- PostgreSQL: JSONB dengan GIN indexing
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
data JSONB
);
-- Create index on JSONB field
CREATE INDEX idx_orders_data ON orders USING GIN (data);
-- Query nested JSON dengan operator
SELECT * FROM orders
WHERE data @> '{"status": "completed"}';
-- Update nested field
UPDATE orders
SET data = jsonb_set(data, '{shipping,address}', '"Jakarta"')
WHERE id = 1;
MySQL JSON
-- MySQL: JSON tanpa native indexing
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
data JSON
);
-- Query JSON field
SELECT * FROM orders
WHERE JSON_EXTRACT(data, '$.status') = 'completed';
-- Atau dengan shorthand
SELECT * FROM orders
WHERE data->>'$.status' = 'completed';
-- Update nested field
UPDATE orders
SET data = JSON_SET(data, '$.shipping.address', 'Jakarta')
WHERE id = 1;
Verdict: PostgreSQL JSONB jauh lebih powerful untuk JSON-heavy applications. Indexing dan querying lebih efisien.
Deep Dive: Replication
PostgreSQL Replication
-- Streaming Replication (physical)
-- di postgresql.conf
wal_level = replica
max_wal_senders = 3
-- Logical Replication (table-level)
CREATE PUBLICATION my_pub FOR TABLE users, orders;
CREATE SUBSCRIPTION my_sub
CONNECTION 'host=primary dbname=app'
PUBLICATION my_pub;
Kelebihan:
- Logical replication untuk selective table sync
- Synchronous replication option
- pg_basebackup untuk backup
MySQL Replication
-- Binary Log Replication
-- di my.cnf
server-id = 1
log_bin = mysql-bin
-- Setup replica
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='primary',
SOURCE_USER='repl',
SOURCE_PASSWORD='password';
START REPLICA;
Kelebihan:
- Setup lebih straightforward
- Group Replication untuk high availability
- MySQL Router untuk load balancing
Deep Dive: Extensions
Ini adalah game changer untuk PostgreSQL.
PostgreSQL Extensions
-- PostGIS untuk geospatial
CREATE EXTENSION postgis;
SELECT ST_Distance(
ST_GeomFromText('POINT(106.8456 -6.2088)'), -- Jakarta
ST_GeomFromText('POINT(110.4262 -7.0051)') -- Semarang
);
-- pg_trgm untuk fuzzy search
CREATE EXTENSION pg_trgm;
SELECT * FROM products
WHERE name % 'samsu' -- Matches "Samsung"
ORDER BY similarity(name, 'samsu') DESC;
-- uuid-ossp untuk UUID
CREATE EXTENSION "uuid-ossp";
SELECT uuid_generate_v4();
-- timescaledb untuk time-series
CREATE EXTENSION timescaledb;
MySQL tidak punya sistem extension seperti ini. Fitur harus built-in atau pakai external tools.
Performance Comparison
| Skenario | PostgreSQL | MySQL |
|---|---|---|
| Read-heavy, simple queries | Bagus | ⚡ Sedikit lebih cepat |
| Write-heavy | ⚡ Lebih baik | Bagus |
| Complex queries | ⚡ Jauh lebih baik | Cukup |
| JSON operations | ⚡ Sangat cepat | Lambat |
| Full-text search | ⚡ Excellent | Basic |
| Concurrent writes | ⚡ MVCC superior | Bagus |
Catatan: Perbedaan performance seringkali marginal untuk typical workloads. Pilih berdasarkan fitur, bukan micro-benchmarks.
Perbedaan Syntax Penting
Auto Increment
-- PostgreSQL
CREATE TABLE users (
id SERIAL PRIMARY KEY, -- atau BIGSERIAL
name TEXT
);
-- Alternatif modern (PostgreSQL 10+)
CREATE TABLE users (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT
);
-- MySQL
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
UPSERT (Insert or Update)
-- PostgreSQL: ON CONFLICT
INSERT INTO users (email, name)
VALUES ('[email protected]', 'John')
ON CONFLICT (email)
DO UPDATE SET name = EXCLUDED.name;
-- MySQL: ON DUPLICATE KEY
INSERT INTO users (email, name)
VALUES ('[email protected]', 'John')
ON DUPLICATE KEY UPDATE name = VALUES(name);
String Concatenation
-- PostgreSQL
SELECT first_name || ' ' || last_name AS full_name FROM users;
-- MySQL
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM users;
Boolean
-- PostgreSQL: Native boolean
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
is_published BOOLEAN DEFAULT FALSE
);
-- MySQL: Pakai TINYINT (1/0)
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
is_published TINYINT(1) DEFAULT 0
);
LIMIT dengan OFFSET
-- PostgreSQL
SELECT * FROM products LIMIT 10 OFFSET 20;
-- MySQL (sama, tapi juga support)
SELECT * FROM products LIMIT 20, 10; -- LIMIT offset, count
Managed Services Populer
PostgreSQL
| Service | Highlight |
|---|---|
| Supabase | Firebase alternative, realtime, auth built-in |
| Neon | Serverless, branching, generous free tier |
| Railway | Simple deployment, good DX |
| Render | Easy setup, auto-scaling |
| AWS RDS | Enterprise, highly configurable |
| DigitalOcean | Affordable managed database |
MySQL
| Service | Highlight |
|---|---|
| PlanetScale | Serverless, branching, Vitess-powered |
| AWS RDS | Enterprise, Aurora compatible |
| DigitalOcean | Simple managed MySQL |
| Railway | Easy deployment |
| Aiven | Multi-cloud support |
Rekomendasi Managed Services
Untuk startup/side project:
- PostgreSQL → Supabase atau Neon
- MySQL → PlanetScale
Untuk production serius:
- PostgreSQL → AWS RDS atau Neon
- MySQL → PlanetScale atau AWS Aurora
Use Cases
Pilih PostgreSQL untuk:
-
Aplikasi dengan data kompleks
- JSON-heavy applications
- Geospatial (maps, location)
- Time-series data
-
Analytics dan reporting
- Complex queries
- Window functions
- CTEs yang rumit
-
Fintech dan banking
- ACID yang strict
- Data integrity paramount
- Audit trail
-
Full-text search
- Built-in tsvector
- Trigram matching
- Language-aware stemming
Pilih MySQL untuk:
-
Web applications standard
- Blog, CMS, e-commerce
- CRUD operations
- Read-heavy workloads
-
Legacy systems
- Banyak PHP apps pakai MySQL
- WordPress, Drupal, Magento
- Existing MySQL expertise
-
High-read scenarios
- Caching-friendly
- Read replicas
- CDN-backed content
-
Simplicity is priority
- Quick setup
- Familiar syntax
- Abundant tutorials
Rekomendasi by Project Type
| Project Type | Rekomendasi | Alasan |
|---|---|---|
| Personal Blog | Either | Keduanya overkill, pilih yang familiar |
| SaaS Startup | PostgreSQL | Flexibility untuk pivot |
| E-commerce | Either | MySQL proven, PostgreSQL more features |
| Fintech | PostgreSQL | ACID strict, better data types |
| Analytics | PostgreSQL | Complex queries, window functions |
| CMS (WordPress) | MySQL | Native support |
| Geospatial App | PostgreSQL | PostGIS unbeatable |
| Real-time App | PostgreSQL | LISTEN/NOTIFY, Supabase realtime |
| IoT/Time-series | PostgreSQL | TimescaleDB extension |
| Microservices | Either | Depends on service needs |
ORM & Driver Support
Keduanya punya support excellent di semua bahasa populer:
Node.js
// PostgreSQL dengan Prisma
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
// datasource di schema.prisma
// provider = "postgresql"
// MySQL dengan Prisma
// provider = "mysql"
Drizzle ORM
// PostgreSQL
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle(pool);
// MySQL
import { drizzle } from 'drizzle-orm/mysql2';
import mysql from 'mysql2/promise';
const connection = await mysql.createConnection(process.env.DATABASE_URL);
const db = drizzle(connection);
Migration dari Satu ke Lainnya
Jika kamu perlu migrate:
MySQL → PostgreSQL
Tools:
pgloader(recommended)- AWS DMS
- Manual dengan CSV export/import
# Pakai pgloader
pgloader mysql://user:pass@localhost/mydb \
postgresql://user:pass@localhost/mydb
PostgreSQL → MySQL
Lebih challenging karena:
- Data types berbeda (JSONB, arrays)
- Fitur yang tidak ada di MySQL
- Syntax differences
Tools:
- AWS DMS
- Custom scripts
- Manual conversion
Kesimpulan
PostgreSQL adalah pilihan tepat jika:
- Butuh fitur advanced (JSONB, arrays, extensions)
- Data model kompleks
- Prioritas: flexibility dan feature-richness
- Pakai Supabase atau Neon
MySQL adalah pilihan tepat jika:
- Aplikasi web standard
- Tim familiar dengan MySQL
- Simple CRUD operations
- Pakai PlanetScale atau existing MySQL infra
TL;DR Decision Tree
Butuh JSONB/Geospatial? → PostgreSQL
Pakai WordPress/PHP legacy? → MySQL
Startup butuh flexibility? → PostgreSQL
Simple web app? → Either (pilih yang familiar)
Pakai Supabase? → PostgreSQL
Pakai PlanetScale? → MySQL
Pada akhirnya, keduanya adalah database excellent. Pilihan terbaik adalah yang sesuai dengan:
- Kebutuhan teknis project
- Expertise tim
- Ecosystem yang sudah ada
Ada pertanyaan tentang PostgreSQL atau MySQL? Reach out di Twitter @nayakayp!