CodeGym /課程 /SQL SELF /產生唯一識別碼

產生唯一識別碼

SQL SELF
等級 16 , 課堂 0
開放

當電腦開始大量連網時,馬上就冒出一個超級簡單但很嚴重的問題:要怎麼保證兩台在世界不同角落的裝置,不會產生一模一樣的識別碼?

想像一下:兩台 server,一台在東京,一台在柏林,各自獨立產生物件的識別碼。如果他們不小心產生一樣的 ID——資料就會搞混、被覆蓋,整個系統直接爆炸。

所以在 1990 年代,分散式系統跟網路協定大爆發的時代,Microsoft 跟 Open Software Foundation(OSF)就搞出了 GUID 這個點子——Globally Unique Identifier,也就是 全域唯一識別碼

GUID(在 ISO 標準裡叫 UUID,Universally Unique Identifier)就是一個 128 位元的數字,看起來像這樣:

550e8400-e29b-41d4-a716-446655440000

這就像數位指紋一樣:又長又亂,碰撞(兩個識別碼一樣)的機率幾乎是零

它會用到:

  • 時間,
  • 隨機數,
  • 裝置的 MAC 位址(舊版會用到),
  • 甚至還有 cryptographic hash function。

為什麼這麼重要?因為 GUID 讓你可以不用中央 server、不用協調,就能產生唯一識別碼,沒有 lock、沒有延遲。這對於:

  • 分散式資料庫,
  • 網路協定,
  • 文件管理系統,
  • 還有現代 API

來說根本救星。

UUID 在 PostgreSQL 裡

GUID/UUID 到處都用得到——從 PostgreSQL 到雲端服務。它們是現代網路的無形建築工人:沒有它們,世界根本沒辦法這麼簡單串在一起。

其實它就是超長的隨機數,16 bytes,用 16 進位寫出來。就是一個超長的整數啦。

這種資料型態超適合你想要在整個系統裡保證唯一值,而且不想靠中央產生器的時候。特別適合分散式系統,或是資料會在不同 server 產生的情境。

為什麼不用 INTEGER 就好?

你可能會想,幹嘛用 UUID,直接用 auto-increment 的數字當識別碼不就好了?來分析一下:

全域唯一性:如果你的資料庫跑在多台 server 上,要保證 INTEGER 唯一很難。用 UUID 這問題就解決了。

安全性跟資料隱藏:UUID 比連號難猜多了。這樣別人就不容易從可預測的 ID 洩漏資訊(像 /users/1, /users/2 這種)。

分散式系統:如果資料在系統不同地方產生,auto-increment INTEGER 沒有複雜同步就不行。

還有一個好處,UUID 是標準,很多程式語言跟資料庫都支援。

UUID 的好處

主要優點:

  • 唯一性:就算資料在不同 server 或系統產生,也能保證唯一。
  • 彈性:可以拿來當 primary key、foreign key 或其他用途。
  • 可擴展性:分散式資料庫超方便。

UUID 也有缺點:

  • 大小:UUID 佔記憶體比較大(16 bytes),INTEGER 只要 4 bytes。
  • 可讀性:比較難看懂、難記。

在 PostgreSQL 產生 UUID

內建函數 gen_random_uuid()

從 PostgreSQL 13 開始有內建 gen_random_uuid(),可以直接產生隨機 UUID。這個函數會回傳一個唯一識別碼,可以當 UUID 欄位的值。

範例:

SELECT gen_random_uuid();

結果:

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

確認有裝 pgcrypto extension(PSQL 1-12 用戶)

在舊版 PostgreSQL(13 以前)gen_random_uuid() 要先裝 pgcrypto extension。如果公司硬要你用舊版,可以這樣救場:

CREATE EXTENSION IF NOT EXISTS "pgcrypto";

這樣就能用隨機 UUID 產生器了。

UUID 當 foreign key 用

UUID 很適合拿來做 table 之間的關聯。假設你有個 user table:

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

建立 orders table

來建立一個 orders table,user_id 當 foreign key,指到 users table 的 id

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

user_id 欄位跟 users table 的 id 欄位連起來,這樣就能把 user 跟他的訂單關聯起來。

JOIN 查資料

來看看 usersorders table 怎麼連:

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;

結果:

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

UUID 的常見用法

user 跟訂單的識別碼:在分散式系統裡,資料可能來自不同來源。

API 標記:UUID 常常在 REST API 裡拿來當 entity 的識別。

全域資料同步:像是從不同 server 收集資料時。

常見錯誤跟注意事項

手動產生 UUID最好用內建函數像 gen_random_uuid(),才不會出錯。

用太多:不是每個地方都要用 UUID,有時候 auto-increment INTEGER 就夠了。像是只會在本地用、不會擴展的 table。

大小:UUID 佔比較多空間,特別是有 index 時,可能會影響查詢效能。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION