21 điểm bởi GN⁺ 2024-07-06 | 4 bình luận | Chia sẻ qua WhatsApp
  • UUID thường được dùng làm khóa chính của bảng cơ sở dữ liệu
    • Dễ tạo, dễ chia sẻ giữa các hệ thống phân tán và đảm bảo tính duy nhất
    • Khi xét đến kích thước của UUID, có thể sẽ băn khoăn liệu đây có phải lựa chọn đúng hay không, nhưng nhiều khi chúng ta không có quyền quyết định
  • Bài viết này không tập trung vào câu hỏi “UUID có phải là định dạng phù hợp cho khóa hay không”, mà giải thích cách sử dụng UUID làm khóa chính một cách hiệu quả trong PostgreSQL

Sử dụng PostgreSQL và UUID làm khóa chính

  • UUID là gì?
    • UUID thường được dùng làm khóa chính của bảng cơ sở dữ liệu
    • Có thể dễ dàng chia sẻ giữa các hệ thống phân tán và đảm bảo tính duy nhất
    • Do kích thước của UUID, có thể nảy sinh nghi ngờ về mức độ phù hợp, nhưng trong nhiều trường hợp không có lựa chọn nào khác

Kiểu dữ liệu UUID trong PostgreSQL

  • Lưu UUID dưới dạng chuỗi

    • PostgreSQL cung cấp kiểu dữ liệu text để lưu chuỗi
    • Tuy nhiên, kiểu text không phù hợp để lưu UUID
    • PostgreSQL cung cấp kiểu dữ liệu chuyên dụng uuid dành cho UUID
    • Kiểu uuid là kiểu dữ liệu 128 bit, cần 16 byte để lưu một giá trị
    • Kiểu text phát sinh thêm overhead 1 hoặc 4 byte
  • Kết quả thử nghiệm

    • Tạo hai bảng để so sánh: một dùng kiểu text, bảng còn lại dùng kiểu uuid
    • Sau khi chèn 10.000.000 hàng, so sánh kích thước bảng và kích thước chỉ mục
    • Bảng dùng kiểu text lớn hơn 54%, còn kích thước chỉ mục lớn hơn 85%

UUID và chỉ mục B-Tree

  • Chỉ mục B-Tree và UUID

    • UUID ngẫu nhiên không phù hợp với chỉ mục B-Tree
    • Chỉ mục B-Tree hoạt động tốt với các giá trị có thứ tự
    • UUID.randomUUID() của Java trả về UUID v4, đây là giá trị giả ngẫu nhiên
    • UUID v7 tạo ra các giá trị được sắp xếp theo thời gian nên phù hợp với chỉ mục B-Tree
  • Sử dụng UUID v7

    • Để dùng UUID v7 trong Java, cần thư viện java-uuid-generator
    • Việc tạo UUID v7 có thể giúp cải thiện hiệu năng chèn

Ảnh hưởng của UUID v7 đến hiệu năng INSERT

  • Thử nghiệm
    • Tạo một bảng dùng UUID v7 và chèn 10.000 hàng 10 lần để đo hiệu năng
    • Kết quả có phần ngẫu nhiên, nhưng việc chèn UUID v7 nhanh hơn khoảng 2 lần

Đọc thêm

  • PostgreSQL 17 có thể sẽ hỗ trợ native cho UUID v7
  • Thông tin về định dạng UUID v7
  • Ảnh hưởng của UUID đến hiệu năng khi dùng làm khóa chính trong cơ sở dữ liệu

Tóm tắt

  • Vấn đề độ dài của UUID

    • Dù đã được tối ưu, UUID vẫn không phải là kiểu tối ưu nhất để làm khóa chính
    • Nếu có quyền lựa chọn, hãy cân nhắc các phương án khác như TSID
  • Sự cần thiết của tối ưu hóa

    • Nếu dự kiến có tập dữ liệu lớn hoặc lưu lượng truy cập cao, cần cân nhắc tối ưu hóa
    • Việc thay đổi khóa chính là một công việc khó, nên điều quan trọng là thiết lập đúng ngay từ đầu
  • Lưu ý

    • Tác giả không phải là chuyên gia PostgreSQL, chỉ đang chia sẻ những gì mình học được
    • Nếu thấy hữu ích, mong nhận được phản hồi qua bình luận hoặc Twitter

Tổng hợp của GN⁺

  • Bài viết này đề cập đến cách sử dụng UUID làm khóa chính hiệu quả trong PostgreSQL
  • Qua thử nghiệm, bài viết cho thấy dùng UUID v7 có thể cải thiện hiệu năng chèn
  • Nếu dự kiến có tập dữ liệu lớn hoặc lưu lượng truy cập cao thì cần tối ưu hóa
  • Các lựa chọn khác như TSID cũng đáng để cân nhắc

4 bình luận

 
savvykang 2024-07-09

Có phải là đòi hỏi quá đáng nếu mong muốn dùng mã hóa base62 cho uuid thay vì định dạng chuẩn (hex + dấu gạch nối) không?

 
qurare 2024-07-08

uuidv7 là vô địch

uuidv8+ là "thần"

 
bbulbum 2024-07-08

Rào cản lớn nhất là... nó không thân thiện với con người.. Tôi vẫn thấy cần điều này ở khá nhiều chỗ..

 
GN⁺ 2024-07-06
Ý kiến Hacker News
  • Khuyến nghị dùng bigserial làm khóa chính thân thiện với B-tree, và cân nhắc UUID được mã hóa dưới dạng chuỗi như một tùy chọn định vị bản ghi bên ngoài

    • Nếu người dùng không chuyên có thể cần trích dẫn, hãy cân nhắc trước các tùy chọn đơn giản như bộ định vị kiểu PNR
    • Không nên trộn các kiểu PK trong schema của cùng một dịch vụ hoặc ứng dụng
    • Khi dùng UUIDv7 làm định danh duy nhất, chỉ nên dùng cho dữ liệu có mã thời gian nội tại
    • Không dùng hashids; nó không có chất lượng mật mã và cũng không quen thuộc với người dùng phổ thông
    • Khi mã hóa, không dùng base64 hoặc bảng chữ cái có chứa dấu gạch nối
  • Khi thiết kế schema cơ sở dữ liệu, hãy ghi nhớ nguyên tắc tách biệt mối quan tâm và sự đồng điệu cơ học

  • ID ngẫu nhiên có kiểu của Stripe thực ra không ngẫu nhiên

    • Chúng chứa metadata, timestamp nhúng, shard và khóa tham chiếu, thông tin phiên bản, v.v.
    • Cá nhân tôi thích bộ định vị bigserial+HMAC mã hóa AES và mã hóa bằng base58 hơn
  • Trong Postgres, UUID ngẫu nhiên không phải vấn đề lớn

    • UUID (16 byte) lớn hơn serial (4 byte) hoặc bigserial (8 byte), nhưng ở cấp độ toàn bảng thì không phải vấn đề đáng kể
  • Trước khi cân nhắc serial so với UUID ngẫu nhiên so với UUID có thứ tự trong Postgres, còn rất nhiều thứ khác đáng lo hơn

  • Gần đây tôi đã chọn ULID làm PK cho Postgres và nhận được rất nhiều trợ giúp từ bài viết này: https://brandur.org/nanoglyphs/026-ids

  • Lý do tôi thích ULID là vì nó tương thích với kiểu UUID, và có timestamp tích hợp nên nếu sắp xếp theo ID thì cũng sẽ ra thứ tự theo timestamp

  • Sẽ hay hơn nếu phần so sánh cũng bao gồm int64, để có thể đối chiếu overhead của UUID với cách tiếp cận truyền thống

  • Hiệu năng chèn là một cách không tốt để đánh giá hiệu năng

    • Hiệu năng B-Tree tốt hơn khi chèn, nhưng tôi tự hỏi nó sẽ ra sao trong các giao dịch quy mô lớn
  • Trong SQLite, UUID4 được ưa chuộng vì ít khả năng xảy ra va chạm page cache hơn trong thời gian khóa giao dịch

    • Điều này cũng có thể áp dụng tương tự cho các hệ thống Postgres
  • Tôi thích khóa chính số nguyên tự tăng

    • Dễ hiểu và dễ sắp xếp
    • Trong các dự án xử lý batch quy mô lớn, có thể lưu khóa chính cuối cùng và lấy mọi thứ lớn hơn nó
  • Benchmark thời gian chèn của UUIDv7 có bao gồm thời gian tạo UUID

    • Tôi chỉ muốn thấy riêng chi phí cập nhật chỉ mục
  • PostgreSQL 17 khó có khả năng bao gồm hỗ trợ UUIDv7

    • Gần đây committer của công việc này đã bị gỡ khỏi dự án, và phiên bản 17 đã ở trạng thái đóng băng tính năng
  • Tôi đã bắt đầu dùng python-ulid, và ULID vượt trội hơn UUID

  • Liên kết tiêu chuẩn UUID v7 đã cũ, nên hãy tham khảo RFC 9562: https://datatracker.ietf.org/doc/html/rfc9562