10 poin oleh GN⁺ 2025-12-16 | 1 komentar | Bagikan ke WhatsApp
  • UUID v4 memiliki tingkat keacakan yang tinggi sehingga memicu inefisiensi indeks dan I/O berlebihan, dan jika digunakan sebagai kunci utama di PostgreSQL dapat menurunkan performa
  • Karena penyisipan berlangsung secara acak, page split dan fragmentasi indeks lebih sering terjadi, serta menyebabkan ukuran log WAL membesar dan latensi tulis meningkat
  • UUID berukuran 16 byte, dua kali lebih besar daripada bigint, yang berujung pada penurunan cache hit ratio dan pemborosan memori
  • Sering disalahpahami sebagai pengenal yang aman, tetapi menurut RFC 4122, UUID bukan sarana keamanan untuk mencegah tebakan
  • Untuk database baru, disarankan menggunakan kunci berbasis sequence bertipe integer, dan bila tak terhindarkan, gunakan UUID v7 yang berurutan berdasarkan waktu

Masalah performa UUID v4

  • Database PostgreSQL yang menggunakan kunci utama UUID v4 selama 10 tahun terakhir secara konsisten menunjukkan penurunan performa dan I/O berlebihan
    • UUID v4 menghasilkan 122 bit acak sehingga indeks tidak bisa diurutkan
    • Saat penyisipan, data tidak disimpan ke halaman yang berurutan sehingga terjadi akses acak, dan saat update maupun delete juga diperlukan penelusuran yang tidak efisien
  • Indeks B-Tree mengasumsikan data terurut, tetapi UUID v4 tidak memiliki keterurutan sehingga efisiensi insert rendah
    • Setiap insert ditulis ke halaman acak sehingga page split di tengah sering terjadi
    • Akibatnya muncul latensi tulis dan peningkatan WAL

Struktur UUID dan alternatifnya

  • UUID adalah pengenal berukuran 128 bit (16 byte), dan di PostgreSQL disimpan sebagai tipe binary uuid
  • UUID v4 berbasis bit acak, sedangkan UUID v7 menyertakan timestamp pada 48 bit pertama sehingga efisiensi indeks lebih baik
  • PostgreSQL 18 (direncanakan pada 2025) akan mendukung UUID v7 secara bawaan
  • UUID v7 dapat diurutkan secara kronologis sehingga kepadatan halaman dan efisiensi cache meningkat

Alasan memilih UUID dan keterbatasannya

  • UUID digunakan saat dibutuhkan pembuatan pengenal tanpa benturan di lingkungan multi-klien atau microservices
    • Contoh: membuat ID secara bersamaan di beberapa instance database
    Iklan
  • Namun, RFC 4122 menyatakan “jangan menganggap UUID sulit ditebak”, sehingga tidak cocok sebagai pengenal keamanan
  • Probabilitas benturan mencapai 50% setelah menghasilkan 2.71×10¹⁸ UUID; dalam praktiknya peluang benturan rendah, tetapi biaya performanya tinggi

Inefisiensi ruang dan I/O pada UUID

  • UUID memakan ruang dua kali lipat dari bigint (8 byte) dan empat kali lipat dari int (4 byte)
    • Pada tabel besar, hal ini menyebabkan kebutuhan penyimpanan meningkat serta waktu backup dan restore bertambah
  • Hasil eksperimen kepadatan halaman indeks
    • indeks integer: 97.64%
    • indeks UUID v4: 79.06%
    • indeks UUID v7: 90.09%
  • Dalam pengujian Cybertec, pencarian pada indeks UUID v4 membutuhkan tambahan 8.5 juta akses halaman, dengan I/O naik 31229%
    • Dalam kondisi yang sama, indeks bigint membutuhkan 27,332 akses buffer, sedangkan UUID v4 membutuhkan 8,562,960 akses buffer
Iklan

Dampak pada cache dan memori

  • Karena distribusinya acak, UUID memiliki buffer cache hit ratio yang rendah
    • Lebih banyak halaman harus dimuat ke cache, dan halaman yang dibutuhkan lebih sering mengalami eviction
  • Penurunan efisiensi cache menimbulkan latensi query dan kenaikan penggunaan memori
  • Untuk menjaga performa, disarankan melakukan rekonstruksi indeks berkala (REINDEX CONCURRENTLY) atau menggunakan pg_repack

Cara mengurangi dampak performa

  • Menambah memori: disarankan menyediakan RAM 4 kali ukuran database (contoh: DB 25GB → memori 128GB)
  • Menyesuaikan work_mem: performa dapat meningkat dengan mengalokasikan lebih banyak memori untuk operasi sort
  • Di lingkungan Rails, gunakan pengaturan implicit_order_column agar memakai kolom yang bisa diurutkan seperti created_at, bukan UUID
  • Dengan perintah CLUSTER, tabel dapat ditata ulang berdasarkan kolom yang bisa diurutkan, tetapi memerlukan exclusive lock

Rekomendasi kunci integer dan sequence

  • Untuk database baru, disarankan memakai kunci berbasis sequence bertipe integer
    • integer (4 byte) menyediakan sekitar 2 miliar nilai unik, sedangkan bigint (8 byte) menyediakan jauh lebih banyak
  • Sebagian besar aplikasi bisnis sudah cukup dengan integer, sementara layanan berskala besar lebih cocok menggunakan bigint
  • Sebagai alternatif yang realistis selain UUID v4, gunakan UUID v7 atau ekstensi sequential_uuids

Ringkasan

  • UUID v4 menyebabkan inefisiensi indeks, I/O tinggi, dan efisiensi cache rendah karena sifatnya yang acak
  • UUID v4 tidak dapat digunakan sebagai pengenal keamanan dan juga boros ruang
  • Kunci sequence bertipe integer lebih cocok untuk sebagian besar aplikasi
  • Jika UUID memang harus digunakan, pilih UUID v7 yang berurutan berdasarkan waktu
  • Sebaiknya hindari penggunaan gen_random_uuid() sebagai kunci utama di PostgreSQL

1 komentar

 
GN⁺ 2025-12-16
Komentar Hacker News
  • Ini adalah contoh klasik optimisasi prematur
    Memasukkan data ke dalam pengenal permanen adalah hal yang tabu dalam pengelolaan data
    Jika tanggal lahir dimasukkan ke ID seperti nomor identitas penduduk Norwegia, nanti bisa muncul imigran yang ternyata tanggal lahirnya dulu salah diketahui, atau masalah kehabisan nomor karena terlalu banyak yang lahir pada 1 Januari
    Pada era katalog kartu dulu, mencampur data dan pengenal untuk menekan biaya pencarian masih bisa dipahami, tetapi sekarang kita punya database yang kuat jadi tidak perlu sejauh itu

    • Menurut saya contoh ini sebenarnya masalah penetapan nilai default yang keliru
      Masalahnya adalah menetapkan ulang tahun yang tidak diketahui sebagai 1 Januari, bukan soal memasukkan tanggal ke dalam kunci itu sendiri
      Kalau memakai nilai non-tanggal seperti 00 atau 99, bentrok tidak akan terjadi
      Memasukkan timestamp ke UUID bukan untuk memberi makna, melainkan demi optimisasi performa
      Kunci yang meningkat seiring waktu mengurangi biaya penulisan ulang B-tree dan meningkatkan performa insert DB
    • Nomor identitas penduduk Italia juga memasukkan jenis kelamin, dan itu menimbulkan masalah setelah operasi pergantian gender
      “Jangan memasukkan data ke pengenal permanen” hanyalah prinsip umum; tergantung situasinya kita bisa saja menerima trade-off
      Misalnya, jika hash md5 dipakai sebagai UUID untuk membangun indeks, fragmentasi memang muncul tetapi masih pada tingkat yang bisa dikelola
    • UUIDv7 pada dasarnya hanya metode pembuatan dengan bias waktu (random bias), bukan benar-benar memuat informasi nyata
      Pilihan antara UUID acak vs berbasis waktu bisa menghasilkan perbedaan performa dalam hitungan detik, bukan milidetik
    • Pada DB kecil, ini memang optimisasi prematur, tetapi saat skalanya membesar justru perlu pendekatan sebaliknya
      Di DB skala besar, sharding dan distribusi itu wajib sehingga UUID bekerja lebih baik daripada auto-increment
    • Soal contoh nomor identitas Norwegia, saya penasaran apakah yang lahir pada 1 Januari benar-benar bisa sebanyak itu
      Kalau formatnya DDMMYYXXXXX, itu bisa menampung sampai 100 ribu orang, jadi saya ingin tahu apakah memang bisa menumpuk sebanyak itu
      Mungkin ini situasi khusus seperti masuknya pengungsi dalam jumlah besar pada tahun tertentu
  • UUID tidak boleh dipakai seperti token keamanan
    Menggunakannya sebagai fitur keamanan hanya karena sulit ditebak itu berbahaya
    Tujuan nilai acak bukan cuma mencegah tebakan, tetapi juga menyembunyikan hubungan antar-ID yang berurutan

  • Bergantung pada jenis DB, strategi PK bisa benar-benar berbeda
    Di Postgres, PK acak tidak efisien, tetapi di DB terdistribusi seperti Cockroach atau Spanner justru kunci yang meningkat monoton menimbulkan masalah hot shard

    • Bahkan di DB terdistribusi, kunci yang cenderung meningkat lebih baik daripada benar-benar acak
      UUIDv7 punya bit atas yang bisa diurutkan dan bit bawah yang acak, jadi bisa mendapatkan distribusi antarnode sekaligus efisiensi penyimpanan lokal
    • Ini sebaiknya dilihat sebagai perbedaan struktur DB, bukan sekadar jenis DB
      Pada DB umum yang tidak di-shard, kunci acak memicu fragmentasi B-tree
    • Di Google Cloud Bigtable, kunci berurutan dipakai dalam bentuk reverse untuk mendorong distribusi otomatis
    • Jika Postgres di-shard, PK acak justru menguntungkan
      Tetapi jika range query sering dilakukan, kunci acak menjadi merugikan
      Pada akhirnya pilihan harus disesuaikan dengan karakteristik workload
    • Untuk workload yang dominan tulis dan sangat bias waktu, di Postgres pun PK acak bisa jadi lebih baik
  • Inti tulisan ini memang tepat dalam menyoroti kekurangan UUIDv4 sebagai PK, tetapi metode obfuscation bilangan bulat yang diajukan tampak tidak cocok untuk layanan produksi
    Untuk DB kecil, UUIDv7 adalah kompromi yang masuk akal

    • Saya lebih suka UUIDv4 daripada UUIDv7
      Karena saya tidak ingin waktu pembuatannya terekspos
      Selama datanya tidak cukup besar sampai keacakan UUIDv4 menimbulkan masalah performa, v4 adalah pilihan yang lebih aman
    • Di Postgres, saya suka memakai satu sequence tunggal
      Ada sedikit kebocoran informasi, tetapi secara operasional cukup samar
    • Kalau hanya ingin menyembunyikan jumlah pengguna, cukup terapkan enkripsi (permutation) pada kunci auto-increment
      Misalnya diubah dengan AES-128 lalu dienkode dengan base64 agar terlihat seperti ID video YouTube
  • Dalam due diligence teknis saya melihat banyak perusahaan, dan kemungkinan melakukan sharding dengan cepat adalah kunci pertumbuhan perusahaan
    Jika semua tabel memakai UUID, saat sharding kita bisa melakukan ekspansi tanpa perubahan struktur
    Ini memberi keuntungan skalabilitas yang jauh lebih besar daripada sedikit kerugian ruang dan waktu

    • UUIDv7 punya keunggulan performa di Postgres juga berkat sifatnya yang meningkat monoton
    • Kami juga pernah kesulitan dalam proses sharding karena tidak punya UUID
      Pada akhirnya, karena model datanya rumit, migrasinya sendiri lebih sulit daripada ada atau tidaknya UUID
  • Aplikasi kami mengenkripsi PK integer agar terlihat seperti UUID
    Karena jika ID berurutan terekspos, orang bisa memperkirakan jumlah pelanggan atau melakukan serangan kamus
    ID terenkripsi memungkinkan upaya pemindaian langsung terdeteksi lewat kegagalan dekripsi

    • Tetapi saat kunci hilang atau diganti, bisa muncul masalah tidak bisa didekripsi
    • Saya penasaran bagaimana pengelolaan kuncinya — apakah disuntikkan lewat environment variable, ditanam di kode, memakai skema AEAD seperti AES-GCM, dan sebagainya; pengelolaan keamanan itu penting
  • Pernyataan “2 miliar sudah cukup” itu berbahaya
    Setiap DBA biasanya punya setidaknya satu kisah mimpi buruk yang berawal dari keputusan seperti itu

  • Tulisan itu mengatakan “nilai acak tidak efisien untuk diurutkan”, tetapi sebenarnya pengurutan berdasarkan urutan byte tetap memungkinkan
    Hanya saja, karena kunci acak bukan insert berurutan, penyeimbangan ulang B-tree lebih sering terjadi sehingga performa menurun

    • UUIDv4 berguna di lingkungan terdistribusi, tetapi kita harus menerima biaya ruang 128-bit dan sifat non-sekuensialnya
    • Penulis mengatakan kemudian ia menambahkan eksperimen perbandingan indeks B-tree
      PK integer membuat indeks lebih pas di memori, sedangkan UUIDv4 membutuhkan lebih banyak akses halaman sehingga latensi meningkat
    • Ada juga pendapat bahwa landasan teknisnya lemah
    • B-tree makin efisien diinsert jika kuncinya meningkat, sedangkan kunci acak kurang ramah cache
    • Semakin erat akses data dengan waktu pembuatannya, semakin menguntungkan pengurutan berdasarkan waktu dari sisi performa
  • Tulisan ini terlihat seperti optimisasi prematur yang mendahulukan solusi daripada masalahnya
    UUIDv4 sudah cukup baik untuk kebanyakan kasus
    Masalah performa sebaiknya dipikirkan saat benar-benar muncul

    • Tetapi kalau sudah mulai dengan UUIDv4, nanti hampir mustahil melakukan rekey ke int64
    • Dalam praktiknya, ketika masalah performa benar-benar muncul, sistem biasanya sudah berada di fase pertumbuhan sehingga tidak ada ruang untuk mengganti PK
  • Singkatnya, di Postgres UUIDv7 menunjukkan performa yang sedikit lebih baik daripada v4
    Di versi terbaru, dukungan UUIDv7 tanpa plugin juga sudah dimungkinkan

    • Namun inti tulisan ini adalah rekomendasi agar kalau memungkinkan tetap memakai sequence·PK integer
    • Mulai Postgres 18 ada fungsi bawaan uuidv7(), tetapi belum jelas apakah fitur tambahannya lebih banyak
    • Bagi kebanyakan pengguna, sekarang ekstensi terpisah tidak lagi diperlukan