CodeGym /Các khóa học /SQL SELF /Sinh mã định danh duy nhất

Sinh mã định danh duy nhất

SQL SELF
Mức độ, Bài học
Có sẵn

Khi máy tính bắt đầu kết nối mạng nhiều hơn, xuất hiện một vấn đề đơn giản nhưng cực kỳ đau đầu: làm sao đảm bảo hai thiết bị khác nhau ở hai nơi khác nhau trên thế giới không tạo ra cùng một mã định danh?

Hãy tưởng tượng: hai server, một ở Tokyo, một ở Berlin, tự tạo mã định danh cho các object. Nếu chúng vô tình tạo ra cùng một ID — dữ liệu có thể bị lẫn lộn, ghi đè lên nhau, và hệ thống có thể sập luôn.

Vậy nên vào những năm 1990, thời kỳ bùng nổ của hệ thống phân tán và các giao thức mạng, trong lòng Microsoft và Open Software Foundation (OSF) đã nảy ra ý tưởng về GUID — Globally Unique Identifier, hay còn gọi là mã định danh toàn cầu duy nhất.

GUID (còn trong chuẩn ISO gọi là UUID, Universally Unique Identifier) — là một số 128-bit, trông kiểu như này nè:

550e8400-e29b-41d4-a716-446655440000

Nó giống như dấu vân tay số: dài và ngẫu nhiên đến mức xác suất trùng lặp (hai mã định danh giống nhau) gần như bằng 0.

Nó được tạo ra bằng cách dùng:

  • thời gian,
  • số ngẫu nhiên,
  • MAC address của thiết bị (ở phiên bản cũ),
  • và thậm chí cả hàm băm mật mã.

Tại sao cái này lại quan trọng? Vì GUID cho phép tạo mã định danh duy nhất mà không cần server trung tâm, không cần phối hợp, không cần lock hay delay. Nó thực sự cứu cánh cho:

  • cơ sở dữ liệu phân tán,
  • giao thức mạng,
  • hệ thống quản lý tài liệu,
  • và tất nhiên là các API hiện đại.

UUID trong PostgreSQL

GUID/UUID được dùng khắp nơi — từ PostgreSQL đến các dịch vụ cloud. Chúng như những “thợ xây” vô hình của internet hiện đại: không có chúng thì việc kết nối thế giới sẽ không dễ dàng như bây giờ đâu.

Thực ra nó chỉ là một số ngẫu nhiên rất dài gồm 16 byte, được ghi ở dạng hệ 16. Đơn giản là một số nguyên rất dài thôi.

Kiểu dữ liệu này cực kỳ hợp lý nếu bạn cần đảm bảo giá trị duy nhất trên toàn hệ thống, không phụ thuộc vào bộ sinh mã định danh tập trung. Đặc biệt hữu ích trong hệ thống phân tán hoặc khi dữ liệu được tạo ở nhiều server khác nhau.

Tại sao không dùng mỗi INTEGER?

Nghe có vẻ, tại sao phải dùng UUID trong khi chỉ cần số tự tăng làm mã định danh? Cùng phân tích nhé:

Tính duy nhất toàn cầu: Nếu database của bạn chạy trên nhiều server, đảm bảo duy nhất cho INTEGER sẽ rất khó. Với UUID thì giải quyết ngon lành.

Bảo mật và ẩn thông tin: UUID khó đoán hơn số thứ tự. Điều này giảm nguy cơ lộ thông tin qua mã định danh dễ đoán (kiểu như /users/1, /users/2).

Hệ thống phân tán: Nếu dữ liệu được tạo ở nhiều nơi trong hệ thống, số tự tăng INTEGER không hợp nếu không có đồng bộ phức tạp.

Một điểm cộng nữa của UUID — nó là chuẩn, được hỗ trợ ở nhiều ngôn ngữ lập trình và hệ thống lưu trữ dữ liệu.

Ưu điểm khi dùng UUID

Những điểm mạnh chính:

  • Duy nhất: đảm bảo mã định danh là duy nhất, kể cả khi dữ liệu được tạo ở các server/hệ thống khác nhau.
  • Linh hoạt: dùng làm primary key, foreign key hay các mục đích khác đều được.
  • Khả năng mở rộng: rất tiện khi làm việc với database phân tán.

Tuy nhiên UUID cũng có nhược điểm:

  • Kích thước: UUID chiếm nhiều bộ nhớ hơn (16 byte) so với INTEGER (4 byte).
  • Khó đọc: nhìn và nhớ cũng khó hơn nhiều.

Sinh UUID trong PostgreSQL

Hàm tích hợp gen_random_uuid()

Từ PostgreSQL version 13 đã có hàm tích hợp gen_random_uuid() để sinh UUID ngẫu nhiên. Hàm này trả về một mã định danh duy nhất, có thể dùng làm giá trị cho cột kiểu UUID.

Ví dụ:

SELECT gen_random_uuid();

Kết quả:

d17fc23b-22e5-4fcb-bf86-1b4c766d77b7

Đảm bảo extension pgcrypto đã cài (cho PSQL 1-12)

Ở các phiên bản PostgreSQL cũ (trước version 13), hàm gen_random_uuid() chỉ có sau khi cài extension pgcrypto. Nếu ở chỗ làm bắt bạn dùng bản cũ, bạn có thể cứu nguy bằng lệnh này:

CREATE EXTENSION IF NOT EXISTS "pgcrypto";

Như vậy là bạn có thể dùng hàm sinh UUID ngẫu nhiên rồi.

Dùng UUID làm foreign key

UUID rất tiện để tạo liên kết giữa các bảng. Giả sử bạn có bảng users:

id name email
d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 Alice alice@example.com
a1d3e15a-abc1-4b51-a320-2d4c859f7467 Bob bob@example.com
3c524998-5c24-4e73-836d-a4c6bb3cafcd Charlie charlie@example.com

Tạo bảng orders

Giờ mình tạo bảng orders, trong đó user_id sẽ là foreign key trỏ tới id trong bảng users.

order_id user_id order_date
1a5b7d9c-b1a2-4f8e-9e7a-0a1111111111 d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 2024-10-15 10:00:00
2b6c8e0d-c2b3-5a9f-af8b-1b2222222222 a1d3e15a-abc1-4b51-a320-2d4c859f7467 2024-10-15 10:05:00
3c7d9f1e-d3c4-6baf-bc9c-2c3333333333 3c524998-5c24-4e73-836d-a4c6bb3cafcd 2024-10-15 10:10:00
4d8eaf2f-e4d5-7cb0-cdab-3d4444444444 d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 2024-10-15 10:15:00
5e9fb030-f5e6-8dc1-debc-4e5555555555 a1d3e15a-abc1-4b51-a320-2d4c859f7467 2024-10-15 10:20:00

Cột user_id liên kết với cột id của bảng users, cho phép tạo liên hệ giữa user và order của họ.

Lấy dữ liệu với JOIN

Xem thử dữ liệu liên kết giữa bảng users và orders:

SELECT 
    u.id AS user_id, 
    u.name, 
    o.order_id, 
    o.order_date 
FROM users u
JOIN orders o ON u.id = o.user_id;

Kết quả:

user_id name order_id order_date
d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 Alice a1d3e15a-abc1-4b51-a320-2d4c859f7467 2024-10-20 12:34:56

Các trường hợp dùng UUID phổ biến

Mã định danh cho user và order: trong hệ thống phân tán, nơi dữ liệu có thể đến từ nhiều nguồn khác nhau.

Dùng cho API: UUID thường được dùng trong REST API để định danh entity.

Đồng bộ dữ liệu toàn cầu: ví dụ khi dữ liệu được thu thập từ nhiều server.

Lỗi thường gặp và lưu ý

Tự tạo UUID bằng tay: tốt nhất nên dùng hàm tích hợp như gen_random_uuid() để tránh lỗi.

Lạm dụng: đừng dùng UUID ở nơi chỉ cần INTEGER tự tăng là đủ. Ví dụ, các bảng local không bao giờ cần mở rộng.

Kích thước: UUID chiếm nhiều chỗ hơn, có thể ảnh hưởng hiệu năng query, nhất là khi index.

2
Nhiệm vụ
SQL SELF, mức độ, bài học
Đã khóa
Tạo UUID
Tạo UUID
2
Nhiệm vụ
SQL SELF, mức độ, bài học
Đã khóa
Liên kết UUID trong các bảng liên quan
Liên kết UUID trong các bảng liên quan
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION