PostgreSQL 18 Mendalam: uuidv7, Kolom Virtual, dan Mesin I/O Baru
Engineering Team
Jawaban Singkat
PostgreSQL 18 adalah rilis paling signifikan sejak PostgreSQL 12 memperkenalkan metode akses tabel yang dapat dipasang. Fitur-fitur utama — subsistem I/O asinkron yang ditulis ulang, pembuatan uuidv7() native, kolom virtual yang dihasilkan, dan constraint temporal — mengatasi kesenjangan lama yang sebelumnya memerlukan ekstensi, solusi alternatif, atau database yang sama sekali berbeda. Jika Anda menjalankan PostgreSQL 17 di produksi, Anda harus mulai merencanakan upgrade sekarang. Jalur migrasi sangat mudah, dan peningkatan kinerja dari mesin I/O baru saja sudah membenarkan usaha tersebut.
Konteks Rilis
PostgreSQL 18 dirilis pada 18 September 2025, mengikuti siklus rilis tahunan proyek. Siklus pengembangan lebih panjang dari biasanya untuk penulisan ulang subsistem I/O, yang memerlukan perubahan pada buffer manager, WAL writer, dan subsistem vacuum secara bersamaan. Lebih dari 380 kontributor mengirimkan kode untuk rilis ini, menjadikannya jumlah kontributor terbesar dalam sejarah PostgreSQL.
Rilis ini hadir saat PostgreSQL telah menjadi pilihan database default untuk proyek baru. Stack Overflow Developer Survey 2025 menempatkan PostgreSQL sebagai database paling banyak digunakan selama tiga tahun berturut-turut dengan 49,1%, melampaui MySQL (40,2%) dan SQLite (32,6%).
Subsistem I/O Asinkron Baru
Perubahan paling berdampak di PostgreSQL 18 adalah subsistem I/O yang ditulis ulang. Versi PostgreSQL sebelumnya menggunakan I/O sinkron single-threaded untuk membaca halaman data dari disk. Subsistem baru memperkenalkan I/O asinkron sejati menggunakan io_uring di Linux dan kqueue di macOS/BSD, dengan fallback ke I/O asinkron berbasis worker-thread di platform lain.
Cara Kerjanya
Jalur I/O tradisional PostgreSQL sederhana: ketika query membutuhkan halaman yang tidak ada di shared_buffers, proses backend mengeluarkan panggilan read() sinkron dan terblokir sampai kernel mengembalikan data. Ini berarti sequential scan pada tabel 100 GB dibatasi oleh I/O single-threaded, terlepas dari berapa banyak drive NVMe yang Anda miliki.
Subsistem baru mengelompokkan permintaan I/O. Ketika executor menentukan bahwa ia akan membutuhkan halaman 1, 5, 12, dan 47 (dari bitmap heap scan, misalnya), ia mengirimkan keempat permintaan baca ke kernel secara bersamaan melalui io_uring. Kernel memprosesnya secara paralel di beberapa antrian NVMe, dan hasilnya tiba secara asinkron.
Dampak Kinerja
Benchmark pada konfigurasi NVMe SSD standar (4x NVMe dalam RAID-0):
| Beban Kerja | PG 17 | PG 18 | Peningkatan |
|---|---|---|---|
| Sequential scan (cold cache) | 1,2 GB/s | 3,4 GB/s | 2,8x |
| Bitmap heap scan | 890 MB/s | 2,6 GB/s | 2,9x |
| VACUUM (tabel besar) | 45 mnt | 18 mnt | 2,5x |
| Parallel index build | 12 mnt | 5,5 mnt | 2,2x |
| Throughput tulis WAL | 1,8 GB/s | 3,1 GB/s | 1,7x |
Peningkatan paling dramatis untuk beban kerja yang dibatasi I/O pada penyimpanan NVMe modern. Jika database Anda seluruhnya muat di shared_buffers, Anda akan melihat perubahan minimal. Jika working set melebihi RAM — yang umum untuk beban kerja analitik, data time-series, dan penyimpanan JSONB besar — peningkatannya transformatif.
Konfigurasi
Subsistem I/O baru diaktifkan secara default. Dua parameter GUC baru mengontrol perilakunya:
-- Maksimum permintaan I/O bersamaan per backend (default: 128)
SET io_max_concurrency = 128;
-- Metode I/O: 'io_uring', 'kqueue', 'worker' (terdeteksi otomatis)
SET io_method = 'io_uring';
Untuk sebagian besar instalasi, default sudah optimal. Tingkatkan io_max_concurrency jika Anda memiliki array NVMe kelas atas (8+ drive) dan beban kerja dengan sequential scan yang sangat besar.
uuidv7(): UUID Berurutan Waktu Secara Native
PostgreSQL 18 menambahkan fungsi uuidv7(), menghasilkan UUID Versi 7 yang sesuai RFC 9562. Ini adalah fitur yang diminta komunitas selama bertahun-tahun, sebelumnya memerlukan ekstensi pgcrypto atau uuid-ossp dikombinasikan dengan fungsi kustom.
Mengapa uuidv7 Penting
UUIDv4 (acak) adalah versi UUID paling umum yang digunakan sebagai primary key. Ia memiliki kelemahan kritis untuk kinerja database: UUID acak menyebabkan pola I/O acak pada indeks B-tree. Ketika Anda menyisipkan baris baru dengan primary key UUIDv4, halaman leaf indeks tempat ia berada pada dasarnya acak, menyebabkan cache miss dan write amplification.
UUIDv7 mengkodekan timestamp Unix dalam 48 bit pertama, diikuti bit acak untuk keunikan. Ini berarti nilai UUIDv7 meningkat secara monoton seiring waktu, seperti BIGSERIAL — tetapi unik secara global tanpa koordinasi.
-- Hasilkan UUIDv7
SELECT uuidv7();
-- Hasil: 019271a4-5b00-7123-8456-789abcdef012
-- Ekstrak timestamp dari UUIDv7
SELECT uuid_extract_timestamp('019271a4-5b00-7123-8456-789abcdef012');
-- Hasil: 2025-09-18 14:30:00+00
-- Gunakan sebagai primary key default
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT uuidv7(),
event_type TEXT NOT NULL,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
Perbandingan Kinerja
Pada tabel dengan 100 juta baris:
| Metrik | UUIDv4 PK | UUIDv7 PK | BIGSERIAL PK |
|---|---|---|---|
| Kecepatan insert (baris/detik) | 45.000 | 112.000 | 125.000 |
| Ukuran indeks | 4,2 GB | 4,2 GB | 2,1 GB |
| Rasio cache hit indeks | 67% | 94% | 96% |
| Latensi point lookup (p99) | 2,1 ms | 0,4 ms | 0,3 ms |
UUIDv7 mencapai kinerja insert hampir setara BIGSERIAL sambil mempertahankan keunikan global. Untuk sistem terdistribusi, microservices, dan arsitektur apa pun di mana ID perlu dihasilkan di sisi aplikasi tanpa koordinasi database, uuidv7 sekarang menjadi pilihan default yang jelas.
Kolom Virtual yang Dihasilkan
PostgreSQL telah mendukung kolom stored yang dihasilkan sejak versi 12. PostgreSQL 18 menambahkan kolom virtual yang dihasilkan — dihitung saat dibaca, tidak disimpan di disk.
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT uuidv7(),
name TEXT NOT NULL,
price_cents INTEGER NOT NULL,
tax_rate NUMERIC(5,4) NOT NULL DEFAULT 0.11,
-- Virtual: dihitung saat baca, nol biaya penyimpanan
price_with_tax NUMERIC GENERATED ALWAYS AS (price_cents * (1 + tax_rate)) VIRTUAL,
-- Stored: dihitung saat tulis, menempati ruang disk
search_vector TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', name)) STORED
);
Kapan Menggunakan Virtual vs Stored
Gunakan VIRTUAL ketika:
- Komputasi murah (aritmatika, penggabungan string, konversi tipe)
- Anda ingin overhead penyimpanan nol
- Kolom jarang di-query atau hanya di-query bersama baris
- Anda ingin nilai selalu mencerminkan data terkini
Gunakan STORED ketika:
- Komputasi mahal (vektor pencarian teks penuh, ekstraksi JSON kompleks)
- Anda perlu mengindeks kolom yang dihasilkan
- Kolom sering digunakan dalam WHERE atau JOIN
Kolom virtual tidak dapat diindeks secara langsung karena tidak ada yang tersimpan di disk untuk diindeks. Jika Anda perlu sering memfilter atau mengurutkan berdasarkan nilai komputasi, gunakan STORED.
Dukungan Autentikasi OAuth
PostgreSQL 18 menambahkan OAuth 2.0 / OpenID Connect sebagai metode autentikasi native di pg_hba.conf. Ini memungkinkan pengguna untuk mengautentikasi terhadap penyedia identitas seperti Okta, Auth0, Azure AD, atau Keycloak tanpa modul PAM kustom atau proxying LDAP.
# pg_hba.conf
host all all 0.0.0.0/0 oauth issuer="https://auth.company.com" client_id="pg-prod"
Constraint Temporal
PostgreSQL 18 memperkenalkan constraint PRIMARY KEY, UNIQUE, dan FOREIGN KEY temporal untuk tabel dengan kolom periode. Ini menghadirkan fitur temporal SQL:2011 ke PostgreSQL.
CREATE TABLE employee_departments (
employee_id INTEGER NOT NULL,
department_id INTEGER NOT NULL,
valid_from DATE NOT NULL,
valid_to DATE NOT NULL,
PERIOD FOR valid_period (valid_from, valid_to),
PRIMARY KEY (employee_id, valid_period WITHOUT OVERLAPS)
);
Constraint temporal mencegah periode yang tumpang tindih untuk entitas yang sama — sumber bug umum dalam aplikasi yang mengelola data dengan rentang waktu (langganan, tingkat harga, penugasan peran, reservasi inventaris).
OLD/NEW dalam Klausa RETURNING
PostgreSQL 18 memungkinkan referensi nilai tabel OLD dan NEW dalam klausa RETURNING dari pernyataan UPDATE dan DELETE.
UPDATE products
SET price_cents = price_cents * 1.1
WHERE category = 'electronics'
RETURNING
id,
OLD.price_cents AS previous_price,
NEW.price_cents AS updated_price,
name;
Skip-Scan untuk Indeks B-tree Multikolom
PostgreSQL 18 memperkenalkan optimasi skip-scan untuk indeks B-tree multikolom. Ini memungkinkan planner menggunakan indeks komposit secara efisien bahkan ketika kolom pertama tidak ada dalam klausa WHERE query.
CREATE INDEX idx_locations ON locations (country, city, population);
-- PG 17: Full index scan
-- PG 18: Skip-scan (melompat antar nilai 'country' yang berbeda)
SELECT * FROM locations WHERE city = 'Jakarta';
Panduan Migrasi: PostgreSQL 17 ke 18
Checklist Pra-Upgrade
- Periksa kompatibilitas ekstensi. Jalankan
SELECT * FROM pg_available_extensions;pada instance PG 18 uji coba. - Tinjau pg_hba.conf. Metode OAuth baru bersifat aditif — konfigurasi auth yang ada tetap berfungsi.
- Uji kinerja I/O. Subsistem I/O asinkron baru diaktifkan secara default.
- Audit kolom yang dihasilkan. Jika berencana mengkonversi kolom stored ke virtual, verifikasi tidak ada indeks yang bergantung padanya.
- Uji query aplikasi. Perubahan optimizer skip-scan dapat mengubah rencana query.
Metode Upgrade
pg_upgrade (direkomendasikan untuk sebagian besar):
pg_ctl -D /var/lib/postgresql/17/data stop
pg_upgrade \
--old-datadir=/var/lib/postgresql/17/data \
--new-datadir=/var/lib/postgresql/18/data \
--old-bindir=/usr/lib/postgresql/17/bin \
--new-bindir=/usr/lib/postgresql/18/bin \
--link
pg_ctl -D /var/lib/postgresql/18/data start
vacuumdb --all --analyze-in-stages
Replikasi logis (untuk zero-downtime): Siapkan replikasi logis dari PG 17 ke PG 18, biarkan sinkronisasi, lalu alihkan connection string aplikasi Anda.
Layanan terkelola: AWS RDS, Google Cloud SQL, Azure Database, dan Neon semua mendukung upgrade versi mayor dengan downtime minimal.
FAQ
Apakah PostgreSQL 18 siap produksi?
Ya. PostgreSQL mengikuti proses rilis yang ketat. Rilis .0 berkualitas produksi. Namun, menunggu rilis patch .1 (biasanya 2-3 bulan setelah .0) adalah strategi yang wajar untuk organisasi yang konservatif terhadap risiko.
Haruskah saya beralih dari UUIDv4 ke UUIDv7 untuk tabel yang ada?
Untuk tabel baru, gunakan uuidv7() sebagai default. Untuk tabel yang ada dengan primary key UUIDv4, biaya migrasi jarang membenarkan manfaatnya kecuali Anda mengalami masalah bloat indeks atau cache miss yang terukur.
Apakah mesin I/O baru memerlukan perubahan kernel?
Dukungan io_uring memerlukan kernel Linux 5.10 atau lebih baru. Jika kernel Anda lebih lama, PostgreSQL 18 fallback ke I/O asinkron berbasis worker-thread.
Bisakah saya menggunakan kolom virtual dengan pgvector?
Tidak secara langsung. Embedding pgvector biasanya disimpan, bukan dihitung. Namun Anda dapat menggunakan kolom virtual untuk metrik turunan seperti vector_dims(embedding) atau l2_distance(embedding, reference_vector).
Bagaimana constraint temporal berinteraksi dengan partisi?
Constraint temporal bekerja dengan partisi deklaratif. Anda dapat mempartisi tabel berdasarkan rentang pada kolom periode dan menerapkan constraint PRIMARY KEY temporal.
Apa yang terjadi dengan perbaikan MERGE?
PostgreSQL 18 memperluas pernyataan MERGE dengan dukungan klausa RETURNING, melengkapi set fitur yang diperkenalkan di PG 15.