Về việc sử dụng PostgreSQL và UUID làm khóa chính
(maciejwalkowiak.com)- 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
textkhông phù hợp để lưu UUID - PostgreSQL cung cấp kiểu dữ liệu chuyên dụng
uuiddành cho UUID - Kiểu
uuidlà kiểu dữ liệu 128 bit, cần 16 byte để lưu một giá trị - Kiểu
textphát sinh thêm overhead 1 hoặc 4 byte
- PostgreSQL cung cấp kiểu dữ liệu
-
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ểuuuid - 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
textlớn hơn 54%, còn kích thước chỉ mục lớn hơn 85%
- Tạo hai bảng để so sánh: một dùng kiểu
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
- Để dùng UUID v7 trong Java, cần thư việ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
Có phải là đòi hỏi quá đáng nếu mong muốn dùng mã hóa base62 cho
uuidthay vì định dạng chuẩn (hex + dấu gạch nối) không?uuidv7 là vô địch
uuidv8+ là "thần"
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ỗ..
Ý kiến Hacker News
Khuyến nghị dùng
bigseriallà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àihashids; 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ôngKhi 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
bigserial+HMACmã hóa AES và mã hóa bằng base58 hơnTrong Postgres, UUID ngẫu nhiên không phải vấn đề lớn
serial(4 byte) hoặcbigserial(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
serialso 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ơnGầ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ốngHiệu năng chèn là một cách không tốt để đánh giá hiệu năng
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
Tôi thích khóa chính số nguyên tự tăng
Benchmark thời gian chèn của UUIDv7 có bao gồm thời gian tạo UUID
PostgreSQL 17 khó có khả năng bao gồm hỗ trợ UUIDv7
Tôi đã bắt đầu dùng
python-ulid, và ULID vượt trội hơn UUIDLiê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