PostgreSQL 18: Async I/O, uuidv7 dan Skip Scan Dibahas

Foto oleh Brett Sayles on Pexels
PostgreSQL 18 mencapai ketersediaan umum pada 25 September 2025. Ini rilis mayor dengan sorotan subsistem I/O asinkron (AIO) baru, ditambah uuidv7() bawaan, kolom generated virtual sebagai default, B-tree skip scan, dan autentikasi OAuth.
Subsistem AIO memungkinkan backend mengantre beberapa permintaan baca sekaligus alih-alih menunggu satu per satu, sehingga penyimpanan tetap sibuk. Ini mempercepat sequential scan, bitmap heap scan, dan vacuum, dengan tolok ukur resmi melaporkan peningkatan hingga 3x pada skenario tertentu.
worker adalah default dan menjalankan I/O asinkron di proses worker khusus. io_uring memakai I/O asinkron native kernel di Linux dan butuh build dengan liburing. sync menjalankan I/O secara sinkron, pada dasarnya menonaktifkan fitur. Sebagian besar beban kerja sebaiknya tetap di worker kecuali tolok ukur membuktikan sebaliknya.
Untuk tabel dengan banyak insert, ya. uuidv7() memberi awalan timestamp sehingga kunci berurut waktu, menjaga halaman indeks B-tree terisi dari kiri ke kanan dan mengurangi page split dibanding UUIDv4 acak. PostgreSQL 18 menjadikan uuidv7() fungsi bawaan sehingga tidak butuh ekstensi.
Ya. Menulis GENERATED ALWAYS AS tanpa kata kunci kini membuat kolom VIRTUAL yang dihitung saat pembacaan dan tidak menyimpan apa pun. Jika perlu diindeks, di-join, atau sering difilter, deklarasikan STORED secara eksplisit, dan tinjau migrasi yang bergantung pada default stored lama sebelum upgrade.

Foto oleh Brett Sayles on Pexels
Ringkasan Utama
PostgreSQL 18, rilis pada 25 September 2025, menambahkan subsistem I/O asinkron yang membaca data secara bersamaan hingga 3x lebih cepat untuk scan dan vacuum, uuidv7() bawaan untuk kunci berurut waktu, kolom generated virtual sebagai default baru, B-tree skip scan, dan autentikasi OAuth di pg_hba.conf.
Saya sudah bertahun-tahun menyetel PostgreSQL untuk beban kerja ERP yang berat di sisi baca, dan satu tembok yang selalu saya tabrak sama saja: pada sequential scan besar atau vacuum, backend akan mengeluarkan satu pembacaan disk, menunggu hingga selesai, lalu mengeluarkan yang berikutnya. Di NVMe cepat atau block storage cloud, itu membuat sebagian besar perangkat menganggur, karena satu proses yang menunggu satu permintaan tidak bisa menjaga antrean tetap penuh. PostgreSQL 18, yang mencapai ketersediaan umum pada 25 September 2025, adalah rilis pertama yang mengubah hal itu di lapisan penyimpanan, bukan sekadar di sekitarnya.
Ini bukan rilis kosmetik. Sorotannya adalah subsistem I/O asinkron yang sesungguhnya, tetapi PostgreSQL 18 juga membawa beberapa fitur yang sudah lama saya inginkan sebagai pengembang aplikasi: uuidv7() bawaan sehingga saya tidak perlu lagi memakai ekstensi, kolom generated yang tidak lagi membuang ruang disk, penggunaan indeks multikolom yang lebih pintar, dan OAuth sebagai metode autentikasi kelas satu. Mari saya bahas satu per satu seperti cara saya menilainya sebelum masuk ke produksi.
Versi mayor PostgreSQL hadir setiap tahun, tetapi kebanyakan membawa peningkatan planner dan sintaks yang bertahap. PostgreSQL 18 berbeda karena perubahan andalannya bersifat arsitektural: cara server berbicara ke disk. Selama hampir seluruh sejarah Postgres, pembacaan disk bersifat sinkron — eksekutor meminta satu blok ke sistem operasi lalu menunggu. Versi 18 memperkenalkan lapisan I/O asinkron yang memungkinkan backend mengantre beberapa permintaan baca sekaligus, sehingga perangkat penyimpanan tetap sibuk alih-alih menganggur di antara permintaan. Semua hal lain di tulisan ini menempel pada perubahan itu, tetapi perubahan I/O inilah yang mengubah ekspektasi performa.
Subsistem AIO baru ini memungkinkan PostgreSQL mengeluarkan beberapa permintaan I/O secara bersamaan alih-alih menunggu masing-masing selesai secara berurutan. Dalam praktiknya itu berarti sequential scan, bitmap heap scan, dan vacuum bisa membaca lebih dahulu: selagi satu blok sedang diproses, permintaan berikutnya sudah diantrekan. Tolok ukur resmi melaporkan peningkatan performa hingga 3x pada skenario tertentu — dan yang penting, ini juga mengaktifkan effective_io_concurrency serta maintenance_io_concurrency di atas nol pada sistem yang sebelumnya tidak punya dukungan fadvise.
Anda mengendalikannya dengan variabel server io_method. Defaultnya adalah worker, di mana proses worker khusus menjalankan I/O asinkron. Di Linux Anda bisa beralih ke io_uring untuk I/O asinkron native kernel, asalkan server dibangun dengan dukungan liburing. Ada juga sync, yang menjalankan I/O yang memenuhi syarat secara sinkron dan pada dasarnya menonaktifkan fitur ini. Dua pengaturan pendamping, io_combine_limit dan io_max_combine_limit, mengatur bagaimana pembacaan blok yang berdekatan digabungkan menjadi satu permintaan yang lebih besar, dan view sistem baru pg_aios memungkinkan Anda memantau operasi yang sedang berjalan.
# postgresql.conf — enable asynchronous I/O (PostgreSQL 18)
io_method = worker # default; also: io_uring (Linux, --with-liburing) or sync
io_combine_limit = 128kB # batch adjacent reads into one larger I/O
effective_io_concurrency = 16
-- Inspect in-flight asynchronous I/O
SELECT * FROM pg_aios;Jangan langsung beralih ke io_uring di produksi hanya karena terdengar lebih cepat. Metode ini butuh build yang dikompilasi dengan liburing dan hanya untuk Linux, sementara metode default worker sudah memberikan sebagian besar manfaat pada penyimpanan cloud pada umumnya. Ukur beban kerja Anda sendiri dengan pg_aios dan waktu kueri nyata sebelum mengubah io_method — nilai yang tepat bergantung pada kernel, penyimpanan, dan konkurensi Anda, bukan pada judul sebuah blog.
Kunci primer UUIDv4 acak memang praktis tetapi merugikan B-tree: setiap insert mendarat di titik acak dalam indeks, menyebarkan penulisan dan merusak lokalitas cache pada tabel besar. UUIDv7 mengatasinya dengan memberi awalan berupa timestamp, sehingga kunci yang baru dibuat tersusun kira-kira mengikuti urutan penyisipan — Anda mendapat keunikan UUID dengan sifat ramah indeks seperti sequence. Sebelum versi 18 Anda perlu ekstensi atau kode di sisi klien untuk membuatnya. PostgreSQL 18 menjadikan uuidv7() sebagai fungsi bawaan, dan menambahkan alias uuidv4() agar Anda bisa eksplisit memilih versi yang diinginkan.
-- Time-ordered primary keys, no extension required
CREATE TABLE orders (
id uuid PRIMARY KEY DEFAULT uuidv7(),
customer_id bigint NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
SELECT uuidv7(); -- timestamp-ordered, temporally sortable
SELECT uuidv4(); -- explicit random UUID (new alias in PG18)Nilai yang dikembalikan uuidv7() dapat diurutkan secara temporal, persis yang Anda inginkan untuk kunci primer default pada tabel dengan banyak insert: halaman indeks terisi dari kiri ke kanan, page split berkurang, dan range scan atas baris yang baru dibuat tetap rapat. Untuk sistem terdistribusi, ini juga mempertahankan sifat UUID yang mudah digabungkan tanpa write amplification yang mengganggu UUIDv4 saat dipakai sebagai kunci clustering.
PostgreSQL sudah punya kolom generated STORED sejak versi 12 — dihitung saat penulisan dan disimpan ke disk. Versi 18 menambahkan kolom generated virtual yang menghitung nilainya saat pembacaan, tanpa menyimpan apa pun, dan menjadikan VIRTUAL sebagai default ketika Anda menulis GENERATED ALWAYS AS tanpa kata kunci. Untuk nilai turunan yang jarang Anda filter atau indeks, itu gratis: tidak ada byte tambahan di disk, tidak ada biaya saat penulisan, dan ekspresinya dievaluasi hanya saat kolom benar-benar dipilih.
CREATE TABLE invoices (
id uuid PRIMARY KEY DEFAULT uuidv7(),
net numeric(12,2) NOT NULL,
tax_rate numeric(4,3) NOT NULL,
-- VIRTUAL is now the default: computed on read, stored nowhere
gross numeric(12,2) GENERATED ALWAYS AS (net * (1 + tax_rate)) VIRTUAL,
-- opt back into on-disk materialization when you need to index it
gross_kept numeric(12,2) GENERATED ALWAYS AS (net * (1 + tax_rate)) STORED
);Karena default berubah menjadi VIRTUAL, kolom generated yang Anda kira sudah termaterialisasi kini bisa dihitung ulang pada setiap pembacaan — dan Anda tidak bisa membangun indeks biasa langsung di atas kolom virtual. Jika sebuah kolom generated sering difilter, di-join, atau diindeks, deklarasikan STORED secara eksplisit. Tinjau setiap skrip migrasi yang menghilangkan kata kunci itu dan bergantung pada perilaku stored lama sebelum memutakhirkan skema produksi.
Indeks B-tree multikolom klasik pada kolom seperti (tenant_id, created_at) tidak berguna bagi kueri yang hanya memfilter created_at, karena Postgres tidak bisa mencari tanpa nilai untuk kolom terdepan. PostgreSQL 18 menambahkan skip scan, yang memungkinkan planner memakai indeks B-tree multikolom bahkan ketika tidak ada batasan kesetaraan pada kolom pertama atau kolom awal, selama ada batasan berguna pada kolom berikutnya. Secara praktis ia menelusuri nilai-nilai kolom terdepan yang berbeda dan melompati indeks untuk masing-masing — mengubah indeks yang sebelumnya diabaikan menjadi berguna dan mengurangi full-table scan.
PostgreSQL 18 menambahkan metode autentikasi oauth ke pg_hba.conf, memungkinkan klien melakukan autentikasi dengan alur OAuth 2.0 yang divalidasi melalui pustaka ekstensi. Ini membawa variabel server oauth_validator_libraries untuk memuat modul validasi token, opsi OAuth libpq baru di sisi klien, dan flag build --with-libcurl untuk pustaka yang diperlukan. Bagi tim yang sudah standar pada satu penyedia identitas, ini berarti akses basis data akhirnya bisa bergantung pada infrastruktur token yang sama dengan sisa tumpukan, bukan pada ranah kata sandi terpisah.
| Aspek | PostgreSQL 17 | PostgreSQL 18 |
|---|---|---|
| Pembacaan disk | Sinkron, satu blok pada satu waktu | Asinkron via io_method, scan hingga 3x lebih cepat |
| UUID berurut waktu | Butuh ekstensi atau kode klien | Fungsi bawaan uuidv7() |
| Kolom generated | Hanya STORED, selalu di disk | VIRTUAL sebagai default, STORED opsional |
| Indeks multikolom, tanpa kesetaraan terdepan | Indeks dilewati, sering full scan | Skip scan memakai indeks di lebih banyak kasus |
| Login OAuth | Tidak didukung secara native | Metode oauth di pg_hba.conf |
Rencana upgrade saya sengaja membosankan: uji di replika dulu, biarkan io_method pada default worker dan ukur waktu kueri nyata sebelum menyentuhnya, serta audit setiap kolom generated agar tidak ada yang diam-diam beralih dari stored ke virtual. Setelah itu saya memakai uuidv7() pada tabel baru dengan banyak insert dan membiarkan skip scan diam-diam menyelamatkan kueri yang dulu diabaikan planner. PostgreSQL 18 adalah rilis langka di mana kemenangan terbesar — I/O asinkron — tidak butuh biaya untuk diadopsi selain upgrade itu sendiri.
Sumber