这个周末,尼克博士与我们分享了一篇写得很好的文章,讨论了自动递增(串行)密钥的缺点和一种替代方法。在讨论本文时,出现了一个问题:如何在应用程序中使用 UUID 键,以及在 PostgreSQL 中如何使用 UUID 键。PostgreSQL 开箱即用定义了一个 UUID数据类型,这是一个很好的开始。然后我们有三个选项来生成 UUID,

  1. 在应用程序代码中
  2. 在数据库中使用 uuid-ossp 扩展
  3. 在数据库中使用 pgcrypto 扩展

在我们的应用程序中生成它们会很好地工作,这是一种很好的方法,除非您希望数据库为您自动处理这些创建,而这通常是我们的观点所在。

只有当您确实需要它提供的功能时才使用uid-ossp,如果您只需要生成和索引它们,那么您就不需要 uid-ossp。(更新:见下文)。要在数据库中生成 UUID,一个简单的起点是使用 pgcrypto 扩展中的 gen_random_uuid() 函数。

那么我们如何使用这个函数呢?首先我们需要在数据库中创建扩展名,

CREATE EXTENSION pgcrypto;
CREATE EXTENSION "uuid-ossp";

这是加载预编译共享库代码的方式,这些代码将功能添加到 PostgreSQL 数据库中。

特别注意,必须为希望在其中使用扩展的每个数据库创建(加载)一个扩展。当它被加载到数据库服务器的运行实例中,重启服务才能生效。

还要注意,如果您已经将数据库从一个服务器转储并恢复到另一个正在运行的实例,那么根据转储/恢复的方法,您可能需要在恢复之后将其加载到这个新实例中。

使用下面的 SQL 语句,就可以生成一个 UUID

SELECT gen_random_uuid();
SELECT uuid_generate_v4();
SELECT uuid_generate_v1mc();

返回 UUID 数据类型

让我们用 UUID 主键创建一个表,看看我们如何使用gen_random_uuid()函数来为我们填充标识,

CREATE SCHEMA IF NOT EXISTS snw;
CREATE TABLE snw.contacts(
   id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   name TEXT,
   email TEXT
);

现在我们可以在新建的 Stark & Wayne 联系人表中添加条目,

INSERT INTO snw.contacts (name,email) VALUES
('Dr Nic Williams','drnic'),
('Brian Mattal','brian'),
('Wayne E. Seguin','wayneeseguin'),
('Long Nguyen','long'),
('Bill Chapman','bill'),
('Chris Weibel','chris'),
('Jeremey Budnack','jrbudnack'),
('Ruben Koster','rkoster'),
('Jamie Van Dyke','jamie'),
('Quintessence Anx','qanx'),
('McGowan','mcg'),
('高,秀娇 (XJ)','xj'),
('Geoff Franks','geoff'),
('Van Nguyen','vnguyen'),
('John Longanecker','jlonganecker')
;
INSERT 0 15

现在我们来看看我们的表格

SELECT * FROM snw.contacts;
                  id                  |       name       |    email
--------------------------------------+------------------+---------------
 0d60a85e-0b90-4482-a14c-108aea2557aa | Dr Nic Williams  | drnic
 39240e9f-ae09-4e95-9fd0-a712035c8ad7 | Brian Mattal     | brian
 9e4de779-d6a0-44bc-a531-20cdb97178d2 | Wayne E. Seguin  | wayneeseguin
 66a45c1b-19af-4ab5-8747-1b0e2d79339d | Long Nguyen      | long
 bc8250bb-f7eb-4adc-925c-2af315cc4a55 | Bill Chapman     | bill
 200393bc-8aaa-45a8-9093-80c4792348cd | Chris Weibel     | chris
 cd881764-bea1-4249-b86d-f8fb8182eec1 | Jeremey Budnack  | jrbudnack
 970972dd-dce8-4c65-a85b-63735ada0fc9 | Ruben Koster     | rkoster
 1c225a3a-2c70-4d95-b87f-f086cbd20366 | Jamie Van Dyke   | jamie
 9f0bb16e-fc25-47f3-b60a-635b6224225a | Quintessence Anx | qanx
 9788c636-936e-4dd6-b9d5-f340329142bd | McGowan          | mcg
 3d8b664f-ef5f-4587-a45c-f2991a1fc029 | 高,秀娇 (XJ)    | xj
 f618f5a8-380e-44fb-9b4e-b3286f29dcc8 | Geoff Franks     | geoff
 35cb41dd-3edb-4483-a1fe-fc315243d2f8 | Van Nguyen       | vnguyen
 74b37cd4-75aa-4871-b17b-a5160428e589 | John Longanecker | jlonganecker
(15 rows)

我们看到每一行都有一个 UUID id字段,它是自动为我们生成的主键。

讨论

@drewblas(谢谢Drew!)指出,使用 pgcrypto 中的 gen_random_uuid() 对表在磁盘上的键空间碎片有负面影响。Drew告诉我们:

Random产生非常片段的插入,这会破坏表。使用 uuid_generate_v1mc() [代替]…键是seq,因为它们是基于时间的。所以所有插入都指向同一个数据页,没有随机 io。

这是有道理的,由于随机概率分布的键,它应该是碎片。但是,这种碎片对数据库系统本身的效率不是很好。为了获得较低的使用 UUID 的好处主键用于分裂也许指出了最好使用 uuid_generate_v1mc() 下面从 uuid-ossp 扩展,因为它使用一个基于时间的 seq 算法中可以读到 postgresql文档

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐