UUID v4 vs v7 — Perbedaan dan Kapan Menggunakan Masing-masing
UUID v4 telah menjadi pilihan default untuk kunci primer terdistribusi selama lebih dari satu dekade. Hasilkan nilai 128-bit acak, format sebagai delapan grup heks, selesai. Tidak diperlukan koordinasi, probabilitas kolisi praktis nol, bekerja di mana-mana. Jadi mengapa UUID v7 — distandarisasi di RFC 9562 pada 2024 — menyebar dengan cepat di seluruh sistem produksi pada 2026?
Jawaban singkatnya: kinerja basis data. UUID v4 merusak lokalitas indeks B-tree. UUID v7 memperbaikinya sambil menjaga segala hal lain yang disukai pengembang tentang UUID. Artikel ini menjelaskan apa sebenarnya setiap versi itu, mengapa perbedaannya penting dalam skala, kapan memilih masing-masing, dan cara bermigrasi tanpa downtime.
Perlu menghasilkan UUID sekarang? Cobalah generator UUID Toova, yang mendukung baik v4 maupun v7 secara massal. Untuk pengenal acak lainnya, generator string acak dan generator password mencakup token yang lebih pendek.
UUID v4 — Keacakan Murni
UUID versi 4 menggunakan generator angka pseudoacak yang aman secara kriptografis (CSPRNG) untuk mengisi 122 dari 128 bit. Enam bit yang tersisa tetap: empat bit meng-encode versi (0100) dan dua bit meng-encode varian (10). Hasilnya terlihat seperti ini:
f47ac10b-58cc-4372-a567-0e02b2c3d479
^^^^
version = 4 (acak) Keacakan adalah fiturnya. Dua sistem independen dapat menghasilkan UUID tanpa koordinasi dan tidak pernah berkolisi — probabilitas kolisi dalam himpunan 2,71 kuadriliun UUID v4 adalah sekitar 50%, yang berarti untuk aplikasi praktis apa pun risikonya dapat diabaikan. Anda tidak memerlukan server ID terpusat, urutan basis data, atau lock terdistribusi.
Cara v4 dihasilkan
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4();
// => 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
Library uuid (JavaScript), uuid.uuid4() Python, google/uuid Go, dan setiap runtime bahasa utama mendelegasikan ke OS CSPRNG — /dev/urandom di Linux, CryptGenRandom di Windows. Biaya pembangkitan secara efektif nol.
Masalahnya: penyisipan acak membunuh indeks B-tree
Sebuah indeks B-tree menjaga data terurut. Ketika Anda menyisipkan baris baru, basis data menemukan tempat di mana kunci baru cocok dalam urutan terurut dan menempatkannya di sana. Jika setiap kunci baru acak, ia mendarat di posisi acak di indeks — yang berarti setiap penyisipan perlu memuat halaman yang berbeda dari disk ke buffer pool. Pada volume rendah ini tidak terlihat. Pada volume tinggi (jutaan baris, laju INSERT tinggi), ia menciptakan pola yang disebut fragmentasi indeks: indeks terisi dengan halaman setengah kosong karena setiap penyisipan mengenai lokasi yang berbeda, dan buffer pool terus-menerus berputar karena working set panas mencakup seluruh indeks alih-alih slice baru-baru yang dapat diprediksi.
Gejala di produksi: latensi INSERT meningkat, overhead autovacuum naik (PostgreSQL), tekanan checkpoint tumbuh, dan kinerja baca untuk record baru-baru ini menurun karena "baru-baru ini" tidak lagi memetakan ke lokalitas apa pun di indeks. Ini bukan hipotetis — ini adalah titik sakit yang terdokumentasi dengan baik pada tabel PostgreSQL dan MySQL besar dengan kunci primer UUID v4.
UUID v7 — Keacakan Terurut Waktu
UUID v7 dirancang secara eksplisit untuk memecahkan masalah fragmentasi B-tree. Ia meng-encode Unix millisecond timestamp 48-bit di bit yang paling signifikan, diikuti oleh bit versi, 12 bit acak, bit varian, dan 62 bit acak lainnya. Total keacakan: 74 bit — masih jauh lebih banyak dari yang diperlukan untuk mencegah kolisi.
018f4e6b-a23c-7d45-9abc-0e02b2c3d479
^^^^^^^^^^^^^^
Unix timestamp 48-bit (presisi ms)
^
version = 7 Karena timestamp menempati bit tinggi, UUID yang dihasilkan kemudian mengurutkan setelah UUID yang dihasilkan lebih awal. Penyisipan selalu ditambahkan ke tepi kanan indeks. Basis data hanya perlu menjaga halaman indeks terbaru tetap panas di buffer pool, bukan seluruh indeks. Pada laju INSERT tinggi, ini saja dapat memotong latensi tulis sebesar 30–60% dan mengurangi I/O dengan urutan besarnya pada tabel dengan puluhan juta baris.
Cara v7 dihasilkan
import { v7 as uuidv7 } from 'uuid';
const id = uuidv7();
// => '018f4e6b-a23c-7d45-9abc-0e02b2c3d479'
Library uuid yang sama menambahkan dukungan v7 di versi 10. Standar library Python menambahkannya di 3.14. PostgreSQL 17 menyertakan uuidv7() sebagai fungsi bawaan. Jika Anda menggunakan stack yang lebih lama, beberapa library kecil menyediakan pembangkitan v7 tanpa dependensi.
Monotonisitas sub-milidetik
Apa yang terjadi ketika dua UUID v7 dihasilkan dalam milidetik yang sama? RFC 9562 memungkinkan implementasi untuk menaikkan counter monotonik di bit acak untuk menjamin pengurutan dalam milidetik yang sama. Library uuid melakukan ini secara default. Hasilnya: bahkan jika 10.000 ID dihasilkan dalam satu milidetik, mereka masih mengurutkan dengan benar.
Masalah Fragmentasi Indeks B-Tree Secara Terperinci
Untuk memahami mengapa ini penting, pertimbangkan apa yang terjadi pada 10.000 penyisipan per detik dengan kunci primer UUID v4 pada tabel dengan 100 juta baris yang ada:
- Setiap penyisipan menghasilkan kunci 128-bit acak yang jatuh di posisi acak di antara 100 juta entri yang ada.
- Basis data harus memuat halaman B-tree spesifik yang berisi posisi tersebut ke buffer pool.
- Dengan 100 juta baris dan halaman 8 KB, indeks mencakup sekitar 100.000 halaman. Setiap detik, 10.000 halaman berbeda diperlukan — jauh lebih banyak daripada yang dapat ditahan oleh tipikal 8 GB shared_buffers hanya untuk indeks ini.
- Setiap cache miss menghasilkan baca disk. Pada 10.000 penyisipan/detik, ini dapat menghasilkan ribuan baca disk acak per detik, menjenuhkan bahkan SSD pada tabel besar.
Dengan UUID v7, semua 10.000 penyisipan per detik mendarat di halaman daun paling kanan (atau segelintir kecil halaman baru-baru ini). Buffer pool hanya perlu menjaga beberapa halaman tersebut tetap panas. Tingkat hit cache untuk tulisan mendekati 100%. Amplifikasi tulis turun secara dramatis.
Manfaat yang sama berlaku untuk pemindaian rentang: WHERE created_at BETWEEN x AND y pada tabel v4 memerlukan pemindaian indeks penuh atau indeks timestamp terpisah. Pada tabel v7, kunci primer itu sendiri adalah indeks timestamp — query dapat mencari langsung ke rentang yang benar.
Cara Menggunakan UUID v7 di PostgreSQL
-- Bekerja secara native dengan tipe UUID di PostgreSQL 16+
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- v4 (acak)
-- Beralih ke UUIDv7 via ekstensi atau pembangkitan sisi aplikasi
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Dengan UUIDv7: timestamp sudah disematkan, jadi
-- kolom created_at terpisah ini sering kali redundan.
PostgreSQL 17 mengirim uuidv7() secara native. Untuk PostgreSQL 14–16, ekstensi pg_uuidv7 menyediakan fungsi yang sama. Tipe data UUID menyimpan baik v4 maupun v7 secara identik — 16 byte, tanpa overhead. Satu-satunya perbedaan adalah pola bit yang menentukan urutan sortir.
Satu konsekuensi yang berguna: karena timestamp disematkan, banyak tabel tidak lagi memerlukan kolom created_at terpisah untuk pengurutan atau tampilan. Anda dapat mengekstrak timestamp dari UUID v7 dengan satu panggilan fungsi. Ini mengurangi kompleksitas skema dan menghilangkan satu tulisan per penyisipan.
UUID v4 vs v7 — Trade-Off
| Sifat | UUID v4 | UUID v7 |
|---|---|---|
| Bit keacakan | 122 | 74 |
| Urutan sortir | Acak | Kronologis |
| Kinerja INSERT B-tree | Buruk (fragmentasi) | Sangat baik (berurutan) |
| Kebocoran timestamp | Tidak ada | Ya (presisi ms) |
| Standar RFC | RFC 4122 (2005), RFC 9562 (2024) | RFC 9562 (2024) |
| Dukungan library | Universal | Tumbuh pesat (2024–2026) |
| Created_at tersemat | Tidak | Ya |
Panduan Migrasi — v4 ke v7
Migrasi tabel yang ada dari kunci primer UUID v4 ke v7 adalah operasi multi-langkah. Batasan kunci: Anda tidak dapat mengubah nilai kunci primer yang dirujuk oleh foreign key tanpa memperbarui semua tabel yang merujuk juga. Rencanakan jendela pemeliharaan atau gunakan pendekatan dual-write.
-- 1. Tambahkan kolom baru
ALTER TABLE orders ADD COLUMN id_v7 UUID;
-- 2. Backfill baris yang ada (pertahankan created_at asli sebagai
-- sumber timestamp; gunakan fungsi library UUIDv7 di aplikasi Anda)
UPDATE orders SET id_v7 = generate_uuidv7(created_at);
-- 3. Verifikasi tidak ada NULL yang tersisa
SELECT COUNT(*) FROM orders WHERE id_v7 IS NULL;
-- 4. Tukar kolom (memerlukan jendela pemeliharaan singkat)
ALTER TABLE orders ALTER COLUMN id_v7 SET NOT NULL;
ALTER TABLE orders ALTER COLUMN id_v7 SET DEFAULT generate_uuidv7(now());
ALTER TABLE orders RENAME COLUMN id TO id_v4_old;
ALTER TABLE orders RENAME COLUMN id_v7 TO id;
ALTER TABLE orders ADD PRIMARY KEY (id); Untuk migrasi zero-downtime pada tabel traffic tinggi, pendekatan yang direkomendasikan adalah:
- Tambahkan kolom
id_v7baru dengan default server yang menghasilkan UUID v7 ke depan. - Backfill baris lama dalam batch selama periode traffic rendah, menggunakan timestamp
created_atyang ada sebagai seed untuk porsi timestamp v7. - Perbarui semua kolom foreign key di tabel yang merujuk untuk menunjuk ke
id_v7. - Ganti nama kolom dan drop kendala kunci primer lama selama jendela pemeliharaan singkat.
Langkah backfill adalah yang terpanjang. Pada 10.000 baris per batch dengan jeda 50 ms antar batch, tabel 100-juta baris membutuhkan sekitar 8 jam. Mulailah lebih awal.
Dampak Dunia Nyata
Beberapa tim engineering telah menerbitkan benchmark yang membandingkan UUID v4 dan v7 pada dataset ukuran produksi. Temuan yang konsisten:
- Throughput INSERT: peningkatan 2–5x pada tabel dengan 50M+ baris saat beralih dari v4 ke v7, dengan keuntungan meningkat seiring ukuran tabel tumbuh.
- Latensi tulis p99: turun dari ratusan milidetik (v4, di bawah beban) menjadi milidetik satu digit (v7) pada perangkat keras yang sama.
- Ukuran indeks: indeks B-tree pada kolom v7 adalah 15–30% lebih kecil daripada indeks v4 yang setara pada data yang sama karena fragmentasi meninggalkan lebih sedikit halaman setengah kosong.
- Efisiensi buffer pool: tingkat hit shared buffers untuk indeks kunci primer berjalan dari ~40% (v4, tabel besar) menjadi ~99% (v7), karena hanya halaman baru-baru ini yang perlu tetap panas.
Keuntungan dapat diabaikan di bawah sekitar 1 juta baris. Jika tabel Anda tetap kecil, tetap gunakan v4 untuk kesederhanaan. Di atas 10 juta baris dengan laju INSERT yang signifikan, v7 adalah default yang lebih baik.
Kapan Menggunakan Masing-masing
Gunakan UUID v7 ketika:
- Anda merancang skema baru dan tabel akan tumbuh besar (10M+ baris).
- Tabel memiliki laju INSERT tinggi — event, log, order, pesan, notifikasi.
- Anda ingin kunci primer yang juga berfungsi sebagai timestamp pembuatan (menghilangkan kolom).
- Anda menggunakan PostgreSQL 17, MySQL 8.0+, atau ORM modern yang mendukung pembangkitan v7.
- Pengurutan berdasarkan ID secara semantis setara dengan pengurutan berdasarkan waktu pembuatan — yang akan terjadi untuk sebagian besar tabel append-heavy.
Gunakan UUID v4 ketika:
- Pengenal diekspos kepada pengguna dan tidak boleh mengungkap waktu pembuatan (kode undangan, share link, handle billing).
- Tabel kecil dan stabil — tidak ada manfaat kinerja yang membenarkan biaya migrasi.
- Anda menghasilkan ID dalam konteks di mana timestamp pembuatan sensitif (record pengguna pribadi dalam produk yang bersaing pada optik pertumbuhan).
- Anda memerlukan sesuatu sebagai kredensial atau token sekali pakai — gunakan generator rahasia khusus, bukan versi UUID mana pun.
Ringkasan
UUID v4 adalah acak, privat, dan didukung secara universal. UUID v7 adalah terurut waktu, ramah basis data, dan kini standar di library dan basis data utama. Untuk skema baru dengan tabel besar atau tumbuh cepat, v7 adalah default yang lebih baik. Untuk ID yang diekspos kepada pengguna di mana kebocoran timestamp menjadi perhatian, v4 tetap menjadi pilihan yang tepat.
Hasilkan kedua versi secara instan dengan generator UUID Toova — tanpa akun diperlukan. Untuk token yang lebih pendek, generator string acak menghasilkan ID alfanumerik pada panjang berapa pun.