CodeGym /课程 /SQL SELF /唯一标识符的生成

唯一标识符的生成

SQL SELF
第 16 级, 课程 0
可用

当电脑刚开始疯狂联网的时候,出现了一个很简单但超级棘手的问题:怎么保证两个在世界不同角落的设备不会生成一样的标识符?

想象一下:两个服务器,一个在东京,一个在柏林,各自独立地给对象生成ID。如果它们碰巧生成了相同的ID——数据就会混乱、被覆盖,系统直接崩了。

所以在90年代,分布式系统和网络协议飞速发展的时候,Microsoft和Open Software Foundation(OSF)内部就诞生了GUID的想法——Globally Unique Identifier,也就是全局唯一标识符。

GUID(在ISO标准里叫UUID,Universally Unique Identifier)其实就是一个128位的数字,比如长这样:

550e8400-e29b-41d4-a716-446655440000

这就像数字指纹一样:又长又乱,碰撞(两个标识符一样)的概率几乎为零。

它的生成方式包括:

  • 时间戳,
  • 随机数,
  • 设备的MAC地址(老版本用),
  • 甚至还有加密hash函数。

为啥这很重要?因为GUID让我们可以不用中央服务器、不用协调,也不用锁和等待,就能生成唯一标识符。这对下面这些场景简直救命:

  • 分布式数据库,
  • 网络协议,
  • 文档流转系统,
  • 当然还有现代API。

PostgreSQL里的UUID

GUID/UUID现在到处都用——从PostgreSQL到云服务。它们就像现代互联网的无形基石:没有它们,世界根本没法这么容易地连起来。

其实它就是很长的随机数,16字节,用16进制写出来。说白了就是很长的整数。

这种数据类型特别适合你想在整个系统里保证唯一性的时候用,不用依赖什么中心化的ID生成器。分布式系统或者数据在不同服务器上生成的时候尤其有用。

为啥不用INTEGER就行?

你可能会想,既然可以用自增数字当ID,干嘛还要用UUID?咱们来分析下:

全局唯一性:如果你的数据库跑在多个服务器上,INTEGER的唯一性就很难保证。用UUID就没这烦恼。

安全性和数据隐藏:UUID比顺序号难猜多了。这样别人就不容易通过可预测的ID(比如/users/1、/users/2)推测你的数据。

分布式系统:如果数据在系统不同地方生成,自增INTEGER就不行了,除非你搞很复杂的同步。

UUID还有个好处——它是标准,很多编程语言和存储系统都支持。

用UUID的优点

主要优点:

  • 唯一性:哪怕数据在不同服务器或系统生成,ID也保证唯一。
  • 灵活性:可以用作主键、外键,或者别的用途。
  • 可扩展性:分布式数据库场景下特别方便。

不过UUID也有缺点:

  • 体积:UUID占内存16字节,比INTEGER(4字节)大多了。
  • 可读性:比起数字,UUID看着难记多了。

PostgreSQL里生成UUID

内置函数gen_random_uuid()

PostgreSQL从13版开始自带gen_random_uuid()函数,可以直接生成随机UUID。这个函数会返回一个唯一标识符,可以直接当UUID类型字段的值用。

例子:

SELECT gen_random_uuid();

结果:

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

确保安装了pgcrypto扩展(PSQL 1-12用)

在老版本PostgreSQL(13之前),gen_random_uuid()要装了pgcrypto扩展才有。如果公司非让你用老版本,可以用这条命令救场:

CREATE EXTENSION IF NOT EXISTS "pgcrypto";

这样你就能用随机UUID生成了。

把UUID当外键用

UUID很适合用来建立表之间的关系。比如你有个用户表:

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表

我们来建个orders表,user_id当外键,指向users表的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表的id字段关联,这样就能把用户和他们的订单连起来。

用JOIN查数据

来看看users和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;

结果:

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的常见用法

用户和订单的标识符:分布式系统里,数据可能来自不同地方。

API里的标记:UUID在REST API里经常用来标识实体。

全局数据同步:比如数据从不同服务器收集过来。

常见错误和注意点

手动生成UUID:最好用内置函数,比如gen_random_uuid(),这样不容易出错。

滥用:别在只用自增INTEGER就够的地方用UUID。比如本地表,永远不会扩展的那种。

体积:UUID占空间大,尤其建索引时可能影响查询性能。

2
任务
SQL SELF, 第 16 级, 课程 0
已锁定
生成 UUID
生成 UUID
2
任务
SQL SELF, 第 16 级, 课程 0
已锁定
在关联表中绑定UUID
在关联表中绑定UUID
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION