Collision UUID có đáng sợ không? Checklist an toàn khi dùng làm ID production

(Cập nhật: 19 tháng 7, 2026 ) UUID collision độ an toàn primary key database UNIQUE
Kết luận

Collision UUID v4 do ngẫu nhiên thuần túy gần như không phải rủi ro vận hành ở quy mô sản phẩm thông thường — không gian 122 bit quá lớn. Điều bạn nên sợ là trùng giả: cắt ngắn ID, RNG yếu, seed test lộ production, import dump hai lần. Giữ UNIQUE, không truncate khóa, dùng CSPRNG. Sinh và soi format trên công cụ UUID Kawa (chạy trên trình duyệt). Chi tiết birthday paradox xem thêm bài UUID v4.

Bài UUID v4 trùng được không? đi sâu toán học. Bài này là góc ngắn vận hành: khi nào đáng lo, checklist trước khi merge schema, và cách triage ticket “trùng UUID” trong team outsourcing / startup Việt mà không hoảng vì Reddit.

Ai cần checklist này

  • Backend vừa chọn UUID làm primary key và bị hỏi “rồi trùng thì sao?”
  • DevOps / DBA nhận alert UNIQUE violation lần đầu
  • Fresher thấy comment “UUID vẫn trùng được” và muốn tiêu chí quyết định rõ

”An toàn” nghĩa là gì với UUID

Ba lớp khác nhau — đừng gộp một câu:

  1. Toán học: không gian hữu hạn → xác suất > 0
  2. Thực dụng: với CSPRNG, collision ngẫu nhiên không phải failure mode bạn sẽ gặp trước disk full / bug app
  3. Vận hành: UNIQUE + không truncate + generator đúng → đây là lớp bạn kiểm soát được

Team thường tranh luận lớp 1 trong standup, rồi ship thiếu lớp 3.

An toàn thực dụng ≠ "xác suất = 0"
An toàn vận hành   = UNIQUE + CSPRNG + không cắt ID

Bảng quy mô: khi nào mới “ý thức” collision

Số liệu minh họa theo birthday paradox (xấp xỉ). Mục tiêu: cảm giác đơn vị, không phải chứng minh formal.

Quy mô sinh UUID vs mức độ lo
Số UUID đã sinh Xác suất collision (xấp xỉ) Ý nghĩa thực tế
10⁶ (1 triệu) ≈ 0 (10⁻¹⁵ bậc) Prototype / DB nhỏ — đừng lo trùng ngẫu nhiên
10⁹ (1 tỷ) Vẫn cực thấp SaaS lớn vẫn dưới ngưỡng 'đáng sợ'
~2³¹ ≈ 2 tỷ Vẫn rất thấp với 122 bit Hay nhầm với không gian 32-bit int
~2⁶¹ Vùng birthday bắt đầu đáng kể Xa ngoài hầu hết sản phẩm

Trái đất ~8×10⁹ người × 10⁸ UUID/người vẫn không đưa bạn gần ngưỡng 2⁶¹. Nếu hệ thống của bạn chưa gần con số đó, ưu tiên fix truncate và UNIQUE trước khi redesign sang serial “cho chắc”.

Công thức cảm tính (n ≪ 2⁶¹):

P ≈ n² / (2 · 2¹²²)

Khi n tăng gấp 10, P tăng ~100 lần — nhưng từ nền cực nhỏ.


Case study: “trùng UUID” mà không phải collision

Case A — Truncate làm URL đẹp

Product muốn /orders/a1b2c3d4. Dev lấy uuid.slice(0, 8). Sau vài tháng, hai đơn hàng đụng prefix. Ticket ghi “UUID collision”. Root cause: không gian 32 bit hex, không phải v4 đầy đủ.

Fix: giữ UUID đủ 36 ký tự trong DB; slug ngắn map qua bảng order_public_id.

Case B — Seed test cố định

Fixture luôn dùng:

00000000-0000-4000-8000-000000000001

Staging restore dump + chạy seed lại → UNIQUE violation. Không phải birthday paradox.

Case C — Client mint + retry

Mobile tạo một UUID, POST timeout, retry cùng body → server thấy “trùng”. Đây là idempotency, không phải hai lần sinh độc lập trùng nhau.


Checklist trước khi ship (in nhanh vào PR)

  • Generator: crypto.randomUUID() / gen_random_uuid() / thư viện RFC — không Math.random()
  • Cột id lưu đủ 128 bit (uuid type hoặc char 36) — không varchar(8)
  • UNIQUE (hoặc PK) trên cột đó
  • Retry / idempotency key tách khỏi “sinh UUID mới mỗi lần”
  • Migration / seed không hard-code cùng một UUID cho nhiều môi trường dùng chung
  • Sinh 3–5 mẫu trên UUID generator, nhóm 3 bắt đầu bằng 4

🆔 Tạo UUID ngay tại đây

Số lượng
Kết quả

UNIQUE conflict ≠ collision toán học

Hiện tượngNguyên nhân thường gặpHành động
INSERT fail UNIQUERetry cùng id, dump trùngIdempotent upsert hoặc bỏ qua
Hai bản ghi “gần giống”Truncate / hash ngắnĐổi thiết kế public id
Cùng UUID trên 2 serviceCopy fixture / clock lệch với v1Audit generator; v4 không phụ thuộc đồng hồ
Sợ trước khi shipComment forumĐọc bảng quy mô + checklist trên

Đặt UNIQUE không mâu thuẫn với “UUID an toàn”. Constraint là lưới an toàn cho lỗi người, giống seatbelt dù xác suất tai nạn thấp.


Khi nào nên đổi thiết kế (không vì sợ trùng)

Đổi sang UUID v7 / ULID / serial khi:

  • Write-heavy + index B-tree bị phân mảnh rõ (đo được)
  • Cần sort theo thời gian tạo trên id
  • Compliance yêu cầu ID đoán được thứ tự (hiếm)

Không đổi chỉ vì “có người bảo UUID vẫn trùng”. Đó là lớp toán học bạn đã chấp nhận khi chọn v4 — và lớp đó rất rộng.


Quy trình triage 5 phút khi có ticket trùng

  1. In full id — đủ 36 ký tự có hyphen không?
  2. So hai sự kiện: cùng request hay hai generator độc lập?
  3. Kiểm tra git: có slice, substr, hash MD5 lấy 8 ký tự không?
  4. Kiểm tra seed/migration chạy lại trên DB đã có data chưa?
  5. Chỉ khi 1–4 sạch generator CSPRNG: escalate như sự kiện cực hiếm (gần như không bao giờ tới bước này)

Giữ runbook này trong Notion team — rẻ hơn tranh luận xác suất mỗi sprint.


Tạo mẫu để review schema

Mở tạo UUID online, bấm sinh vài mã, dán vào PR description kèm câu: “PK dùng uuid v4 đủ độ dài + UNIQUE”. Reviewer thấy format chuẩn nhanh hơn đọc RFC.

Công cụ chạy local trên trình duyệt — phù hợp khi bạn đang ngồi quán cà phê / VPN công ty và chỉ cần vài id test, không cần mở Node REPL.


FAQ ngắn trong body

Có cần check tồn tại trước INSERT không?
Với PK/UNIQUE, INSERT + bắt lỗi conflict thường đủ. Prefetch “SELECT xem đã có chưa” dễ race nếu không transaction đúng.

GUID Windows khác UUID?
Cùng họ 128 bit; format string thường giống 8-4-4-4-12. Quan trọng là version/variant và nguồn ngẫu nhiên.

Nhiều pod Kubernetes tự sinh có sao không?
Đúng mục đích của v4: không cần central ID service. Lo CSPRNG của runtime, không lo “pod A và pod B trùng không gian”.


Liên kết liên quan