UUID vs Serial PostgreSQL: Panduan Memilih Primary Key

Pemilihan tipe data untuk primary key di PostgreSQL antara UUID (Universally Unique Identifier) dan Serial (auto-incrementing integer) berdampak langsung pada performa basis data, efisiensi penyimpanan, dan skalabilitas arsitektur sistem. Serial (atau BIGSERIAL) menggunakan angka berurutan yang sangat cepat dan efisien, namun mudah ditebak dan sulit digabungkan saat melakukan data merging dari berbagai sumber terdistribusi. Di sisi lain, UUID (khususnya versi 4 atau v7) menjamin keunikan global tanpa koordinasi pusat, ideal untuk sistem terdistribusi, namun memiliki ukuran penyimpanan yang lebih besar dan potensi fragmentasi indeks (index bloat) pada operasi tulis berkecepatan tinggi.

Matriks Perbandingan Utama: UUID vs Serial

Parameter Serial / BIGSERIAL UUID (v4 / v7)
Ukuran Storage 4 bytes (Integer) / 8 bytes (BigInt) 16 bytes
Kecepatan Insert Sangat Cepat (berurutan di B-Tree) Lebih Lambat (random I/O untuk UUID v4, baik untuk v7)
Keamanan / Prediktabilitas Mudah ditebak (inkremental: 1, 2, 3...) Sangat aman, acak, tidak bisa ditebak
Sistem Terdistribusi Sulit (butuh koordinasi central sequence) Sangat Mudah (bisa generate di client/aplikasi)
Kinerja JOIN & Index Optimal (ukuran kecil, sedikit page reads) Kurang optimal pada skala masif (memakan RAM lebih banyak)

Implementasi Teknis di PostgreSQL

Untuk menggunakan Serial atau UUID pada tabel PostgreSQL, berikut adalah sintaks pembuatan tabel dan cara memasukkan datanya.

Menggunakan BIGSERIAL

CREATE TABLE users_serial (
    id BIGSERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL
);

-- Insert data otomatis
INSERT INTO users_serial (username) VALUES ('johndoe');

Menggunakan UUID (Ekstensi pgcrypto / gen_random_uuid)

Mulai PostgreSQL 13 ke atas, fungsi gen_random_uuid() sudah tersedia secara bawaan tanpa perlu mengaktifkan ekstensi tambahan.

CREATE TABLE users_uuid (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username VARCHAR(50) NOT NULL
);

-- Insert data
INSERT INTO users_uuid (username) VALUES ('janedoe');

Analisis Performa dan Masalah Index Bloat

Perbedaan terbesar antara UUID dan Serial terletak pada bagaimana PostgreSQL menyimpannya di dalam indeks B-Tree. Serial menghasilkan nilai yang selalu bertambah (monotonik naik). Hal ini membuat penambahan baris baru selalu diletakkan di ujung kanan halaman indeks (right-growing tree), yang sangat efisien dalam penggunaan memori dan cache hit ratio.

Sebaliknya, UUID versi 4 bersifat sepenuhnya acak. Ketika baris baru dimasukkan, posisinya di dalam indeks B-Tree bisa berada di mana saja. Akibatnya terjadi pemecahan halaman (page splits), peningkatan ukuran indeks secara drastis (index bloat), dan penurunan performa baca-tulis ketika ukuran tabel melebihi kapasitas RAM server. Solusi modern untuk mengatasi masalah ini adalah menggunakan UUID versi 7 yang berbasis waktu (time-ordered), sehingga tetap unik secara global namun mempertahankan sifat sekuensial seperti Serial.

Kapan Harus Memilih Serial?

  • Aplikasi monolitik standar dengan satu basis data terpusat.
  • Tabel dengan volume transaksi tulis (INSERT) yang sangat tinggi per detiknya.
  • Keterbatasan ruang penyimpanan (storage) atau bandwidth memori menjadi prioritas utama.
  • Kemudahan pembacaan URL internal (meskipun praktik keamanan tetap menyarankan obfuscation ID).

Kapan Harus Memilih UUID?

  • Arsitektur mikro layanan (microservices) atau sistem terdistribusi di mana beberapa node melakukan insert secara independen.
  • Kebutuhan keamanan untuk mencegah pengguna menebak jumlah transaksi atau ID entitas lain (misal: /orders/1029 vs /orders/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11).
  • Sinkronisasi data offline-to-online atau multi-master replication di mana konflik primary key harus dihindari tanpa koordinasi pusat.