UUID di Microservices & CockroachDB: Antara Skalabilitas dan Performa
5 min read

UUID di Microservices & CockroachDB: Antara Skalabilitas dan Performa

Di dunia microservices modern, UUID sering dipilih sebagai primary key (PK) hampir tanpa pikir panjang. Alasannya terdengar masuk akal: UUID unik secara global, aman untuk distributed system, dan menghindari koordinasi antar service. Namun ketika sistem mulai scale, banyak tim baru menyadari satu hal pahit:

Query join jadi lambat. Sangat lambat.

Bahkan bisa puluhan hingga ratusan kali lebih lambat dibanding integer ID.

Artikel ini membahas secara mendalam:

  • Apa itu UUID dan kenapa populer sebagai PK
  • Kenapa ukurannya besar tapi tetap dipilih
  • Apa yang sebenarnya salah ketika performa drop
  • Dampaknya di database distributed seperti CockroachDB
  • Best practice yang benar-benar dipakai di sistem skala besar

Apa Itu UUID?

UUID (Universally Unique Identifier) adalah identifier 128-bit (16 byte) yang dirancang agar unik tanpa koordinasi pusat.

Contoh UUID:

550e8400-e29b-41d4-a716-446655440000

Varian UUID yang umum

  • UUID v4: random
  • UUID v1: time + MAC address (jarang dipakai sekarang)
  • UUID v7: time-ordered (standar baru, sangat relevan untuk database)

UUID biasanya direpresentasikan sebagai string, tapi di database disimpan sebagai binary 16 byte.


Varian UUID dan Identifier Serupa

Tidak semua UUID diciptakan sama. Banyak masalah performa yang dibahas di artikel ini bukan karena UUID itu sendiri, melainkan karena varian UUID yang dipilih.

Berikut varian UUID dan identifier populer yang sering digunakan di sistem modern, beserta ringkasan karakteristiknya.

UUID v4 (Random UUID)

Karakteristik:

  • 100% random
  • Tidak memiliki urutan waktu
  • Paling umum dipakai secara default

Kelebihan:

  • Sangat mudah digunakan
  • Tidak bocor informasi waktu atau urutan
  • Aman untuk exposure publik

Kekurangan (krusial):

  • Sangat buruk untuk index locality
  • Menyebabkan fragmentasi index
  • Join mahal, terutama di database besar dan distributed

Ringkasan:

Cocok untuk identifier eksternal, buruk sebagai primary key join-heavy.

UUID v1 (Time-based UUID)

Karakteristik:

  • Berbasis timestamp + node identifier
  • Bersifat time-ordered

Kelebihan:

  • Index lebih teratur dibanding v4
  • Insert lebih sequential

Kekurangan:

  • Berpotensi membocorkan informasi waktu dan node
  • Sudah jarang direkomendasikan

Ringkasan:

Lebih baik dari v4 untuk database, tapi kurang ideal untuk sistem modern.

UUID v7 (Time-ordered UUID)

Karakteristik:

  • Standar UUID terbaru
  • Timestamp di bagian awal
  • Tetap unik secara global

Kelebihan:

  • Sangat baik untuk index locality
  • Insert hampir sequential
  • Cocok untuk distributed SQL (CockroachDB, Spanner)

Kekurangan:

  • Belum tersedia native di semua database lama

Ringkasan:

Pilihan UUID terbaik saat ini untuk primary key database.

ULID (Universally Lexicographically Sortable Identifier)

Karakteristik:

  • 128-bit (setara UUID)
  • Lexicographically sortable
  • Biasanya direpresentasikan sebagai string base32

Kelebihan:

  • Time-ordered
  • Mudah dibaca manusia
  • Sangat baik untuk index

Kekurangan:

  • Bukan standar UUID resmi
  • Perlu konsistensi implementasi

Ringkasan:

Alternatif UUID v7 yang sangat populer di microservices.

KSUID (K-Sortable Unique Identifier)

Karakteristik:

  • 160-bit
  • Timestamp + random payload
  • Dirancang oleh Segment

Kelebihan:

  • Sangat baik untuk sistem event dan log
  • Sorting alami berdasarkan waktu

Kekurangan:

  • Lebih besar dari UUID
  • Index lebih berat

Ringkasan:

Cocok untuk event stream, kurang ideal sebagai PK relasional.

Ringkasan Cepat Perbandingan

IdentifierSizeOrderedCocok untuk PKCocok untuk Join
UUID v4128-bit⚠️
UUID v1128-bit⚠️⚠️
UUID v7128-bit
ULID128-bit
KSUID160-bit⚠️⚠️

Kenapa UUID Banyak Digunakan Sebagai Primary Key?

UUID menjadi populer karena menjawab banyak masalah klasik di sistem terdistribusi.

1. Cocok untuk microservices

  • Tidak perlu auto-increment global
  • Service bisa generate ID sendiri
  • Aman untuk async processing dan event-driven architecture

2. Aman untuk exposure ke publik

  • Tidak mudah ditebak
  • Tidak bocor urutan data
  • Cocok untuk API publik

3. Mudah untuk merge data

  • Tidak ada bentrok ID saat import / migrasi
  • Aman untuk multi-region dan multi-cluster

Semua ini membuat UUID terlihat seperti pilihan paling rasional.


Tapi… Kenapa Ukuran Besar Tetap Dipilih?

Walaupun besar, banyak engineer menganggap:

  • “Storage murah”
  • “CPU sekarang cepat”
  • “Index pasti di-handle database”

Masalahnya: bukan cuma soal storage.

Yang terdampak adalah:

  • Struktur index
  • Cache locality
  • Network traffic (di distributed DB)
  • Join strategy

Efeknya baru terasa saat data besar dan query kompleks.


Contoh Kasus Nyata

Your microservices use UUID primary keys. When you try to join data from two services, queries are 100x slower than with integer IDs.

Contoh query:

SELECT *
FROM orders o
JOIN payments p ON o.id = p.order_id;

Dengan integer ID: cepat. Dengan UUID: tiba-tiba query time meledak.

Kenapa?


Apa yang Sebenarnya Terjadi di Balik Layar?

1. UUID itu besar dan mahal untuk dibandingkan

  • BIGINT: 8 byte
  • UUID: 16 byte

Setiap perbandingan:

  • Lebih banyak CPU instruction
  • Lebih banyak cache miss

Dalam join besar, ini terakumulasi.

2. UUID random merusak index locality

Terutama UUID v4:

  • Insert masuk ke posisi acak di B-tree
  • Index sering split
  • Fragmentasi meningkat

Akibatnya:

  • Scan index lebih mahal
  • Join makin lambat

3. Di CockroachDB, dampaknya lebih besar

CockroachDB adalah distributed SQL database:

  • Data dibagi ke banyak node
  • Index dipartisi menjadi range
  • Join bisa lintas node

UUID v4 menyebabkan:

  • Range churn
  • Banyak RPC antar node
  • Distributed hash join yang mahal

Jadi masalahnya bukan cuma CPU, tapi network + koordinasi.

UUID memperbesar Raft & replication cost

Karena CockroachDB menggunakan Raft:

  • Key lebih besar → log lebih besar
  • Replikasi lebih berat
  • Recovery lebih lambat

Ini sering tidak disadari, tapi terasa di scale.


Kesalahan Umum yang Sering Terjadi

❌ Menggunakan UUID v4 sebagai PK default

Tanpa mempertimbangkan efek index dan join.

❌ Menganggap UUID selalu “best practice”

UUID adalah tradeoff, bukan solusi universal.

❌ Join lintas service saat runtime

Ini melanggar prinsip microservices dan memperparah masalah performa.

❌ Menggunakan BIGSERIAL polos di CockroachDB

Ini bisa menyebabkan hotspot write.


Best Practice yang Direkomendasikan

✅ Gunakan UUID yang time-ordered

Di CockroachDB (dan database modern):

  • UUID v7
  • ULID
  • KSUID

Contoh:

CREATE TABLE orders (
  id UUID PRIMARY KEY DEFAULT uuid_v7(),
  ...
);

Manfaat:

  • Insert hampir sequential
  • Index lebih stabil
  • Join jauh lebih efisien

✅ Pisahkan ID eksternal dan ID internal

Pola umum di sistem besar:

orders (
  id BIGINT PRIMARY KEY,
  public_id UUID UNIQUE
)
  • public_id: untuk API & antar service
  • id: untuk join internal

Ini sering jadi kompromi terbaik.

✅ Hindari join lintas service

Gunakan:

  • Denormalized read model
  • CQRS
  • Event-driven projection
  • Data warehouse untuk analytics

Join runtime lintas domain = bom waktu performa.

✅ Desain PK sesuai database

Di CockroachDB:

  • Hindari auto-increment murni
  • Hindari UUID random
  • Pilih key yang seimbang antara write dan read

Penutup

UUID bukanlah kesalahan desain.

Kesalahannya adalah:

  • Menggunakan UUID random tanpa mempertimbangkan index
  • Menggunakannya sebagai satu-satunya join key
  • Mengabaikan sifat database distributed

Di CockroachDB dan microservices skala besar, UUID v7 atau ULID adalah pilihan realistis, bukan UUID v4.

UUID tetap relevan — asal digunakan dengan sadar.