[译文] PostgreSQL 自动生成的主键的 UUID、序列或标识列?

news/2024/7/9 23:06:10 标签: postgresql, 数据库, sql

有时客户会问我关于自动生成主键的最佳选择。在本文中,我将探索这些选项并给出建议。

为什么要自动生成主键?

每个表都需要一个主键。在关系数据库中,能够识别单个表行很重要。如果您想知道为什么,请在 Internet 上搜索数以千计的问题,寻求帮助以从表中删除重复条目。

建议您选择一个主键,它不仅是唯一的,而且在表行的生命周期内永远不会改变。这是因为外键约束通常引用主键,更改在别处引用的主键会导致麻烦或不必要的工作。

现在,有时一个表有一个自然的主键,例如一个国家公民的社会安全号码。但通常情况下,没有这样的属性,您必须生成一个人工主键。有些人甚至争辩说,即使有自然主键,也应该使用人工主键,但我不会参与那场“圣战”。

PostgreSQL 自动生成主键的技巧

有两种基本技术:

用序列生成密钥

一个序列是一个数据库对象,其唯一目的在生活中产生的唯一编号。它使用它递增的内部计数器来执行此操作。

序列针对并发访问进行了高度优化,它们永远不会两次发出相同的数字。尽管如此,从许多并发 SQL 语句访问一个序列可能会成为一个瓶颈,因此有一个CACHE选项可以让序列一次向数据库会话分发多个值。

序列不遵循正常的事务规则:如果事务回滚,序列不会重置其计数器。这是良好性能所必需的,它不会构成问题。如果您正在寻找一种生成无间隙数字序列的方法,则序列不是正确的选择,您将不得不求助于效率较低且更复杂的技术。

要从序列中获取下一个值,您可以使用如下nextval函数:

SELECT nextval(‘sequence_name’);
有关操作序列的其他函数,请参阅文档。

生成UUID小号

UUID(通用唯一标识符)是一个 128 位的数字,它是用有效保证唯一性的算法生成的。有几种标准化算法。在 PostgreSQL 中,有许多生成UUID的函数:

  • 该uuid-ossp扩展提供了生成UUID的功能。请注意,由于名称中有连字符,您必须引用扩展名 ( CREATE EXTENSION “uuid-ossp”😉。
  • 从 PostgreSQL v13 开始,您可以使用核心函数gen_random_uuid()生成版本 4(随机)UUID。

请注意,您应该始终uuid对UUID使用 PostgreSQL 数据类型。不要尝试将它们转换为字符串,否则numeric会浪费空间并降低性能。

定义自动生成的主键

有四种方法可以定义具有自动生成值的列:

使用DEFAULT条款

您可以将此方法与序列和UUID 结合使用。这里有些例子:

CREATE TABLE has_integer_pkey ( id bigint DEFAULT nextval('integer_id_seq') PRIMARY KEY, ... );

CREATE TABLE has_uuid_pkey ( id uuid DEFAULT gen_random_uuid() PRIMARY KEY, ... );

登录后复制

DEFAULT只要INSERT语句没有显式插入该列,PostgreSQL 就会使用该值。

使用serial和bigserial伪类型

此方法是定义序列和设置DEFAULT子句的快捷方式,如上所述。使用此方法,您可以按如下方式定义表:

CREATE TABLE uses_serial ( id bigserial PRIMARY KEY, ... );

这相当于以下内容:`

sql">CREATE TABLE uses_serial (  
id bigint PRIMARY KEY,);

CREATE SEQUENCE uses\_serial\_id_seq  
OWNED BY uses_serial.id;

ALTER TABLE uses_serial ALTER id  
SET DEFAULT nextval(‘uses\_serial\_id\_seq’);CREATE TABLE uses\_serial (  
id bigint PRIMARY KEY,);

CREATE SEQUENCE uses\_serial\_id_seq  
OWNED BY uses_serial.id;

ALTER TABLE uses_serial ALTER id  
SET DEFAULT nextval(‘uses\_serial\_id_seq’);

“ OWNED BY”子句在列和序列之间添加了依赖关系,因此删除列会自动删除序列。

使用serial将创建一个integer列,而bigserial将创建一个bigint列。

使用标识列

这是使用序列的另一种方式,因为 PostgreSQL 在“幕后”使用序列来实现标识列。

sql">CREATE TABLE uses_identity (
   id bigint GENERATED ALWAYS AS IDENTITY
             PRIMARY KEY,
   ...
);

还有“ GENERATED BY DEFAULT AS IDENTITY”,它是相同的,只是如果您尝试显式插入列的值(与DEFAULT子句非常相似),则不会收到错误消息。更多内容请看下文!

您可以为标识列指定序列选项:

CREATE TABLE uses_identity ( id bigint GENERATED ALWAYS AS IDENTITY (MINVALUE 0 START WITH 0 CACHE 20) PRIMARY KEY, ... );

登录后复制

使用BEFORE INSERT触发器

这与DEFAULT值类似,但它允许您使用生成的值无条件地覆盖用户插入的值。触发器的最大缺点是性能影响。

我应该使用integer( serial) 还是bigint( bigserial) 作为自动生成的主键?

您应该始终使用bigint.

确实,一个integer占用四个字节,而一个bigint需要八个。但:

  • 如果你有一个小表,哪里integer就足够了,四个浪费的字节不会有太大关系。此外,并非您设计的每张桌子都会很小!
  • 如果您有一个大表,您可能会超过 的最大值integer,即 2147483647。请注意,如果您的表包含的行数少于此数量,也可能会发生这种情况:您可能会删除行,并且某些序列值可能会被事务“丢失”被回滚。现在,它是相当复杂的主键列从改变integer到bigint一个大表中的活动数据库中,而不会引起过多的停机时间,所以你应该保存自己痛苦。

使用bigint,您肯定永远不会超过 9223372036854775807 的最大值:即使每秒插入 10000 行而没有任何停顿,您还有将近 3000 万年才能达到极限。

我应该bigserial为我的自动生成的主键使用或 标识列吗?

应该使用标识列,除非必须支持旧的 PostgreSQL 版本。

身份列是在 PostgreSQL v11 中引入的,它们有两个优点bigserial:

它们符合 SQL 标准,同时bigserial是专有的 PostgreSQL 语法。这将使您的代码更具可移植性。
如果您使用GENERATED ALWAYS AS IDENTITY,如果您尝试通过显式插入数字来覆盖生成的值,您将收到一条错误消息。这避免了手动输入的值会与稍后生成的值发生冲突的常见问题,从而导致令人惊讶的应用程序错误。
因此,除非必须支持 PostgreSQL v10 或更低版本,否则没有理由使用bigserial.

我应该使用bigint或uuid自动生成的主键吗?

我的建议是使用序列,除非您使用数据库分片或有其他原因以“分散”方式(在单个数据库之外)生成主键。

真正的差异

优点bigint很明显:

bigint仅使用 8 个字节,而uuid使用 16个字节
从序列中获取值比计算UUID更便宜
使用序列的一个缺点是它是单个数据库中的单个对象。因此,如果您使用分片(将数据分布在多个数据库中),则不能使用序列。在这种情况下,UUID是一个明显的选择。(您可以使用定义为INCREMENT大于 1 和不同START值的序列,但是当您添加额外的分片时,这可能会导致问题。)

当然,如果您的主键不是由数据库自动生成的,而是在分布在多个应用程序服务器上的应用程序中创建的,您也会更喜欢UUID。

虚差

有人认为UUID更好,因为它们将写入分布在主键索引的不同页面上。这应该减少争用并导致更平衡或更少碎片化的索引。第一个是正确的,但这实际上可能是一个缺点,因为它需要缓存整个索引以获得良好的性能。第二个肯定是错误的,因为 B 树索引总是平衡的。此外,PostgreSQL v11 中的更改确保单调增加的值将比随机插入更有效地填充索引(但随后的删除当然会导致碎片)。简而言之,任何这样的优势要么是微不足道的,要么是不存在的,而且它们被以下事实所平衡uuid 使用两倍的存储空间,这将使索引更大,从而导致更多写入并占用更多缓存。

基准bigint与uuid

我的同事 Kaarel不久前进行了一次小型性能测试,发现这uuid比bigint更大的联接要慢。

我决定对这两个表运行一个小型的仅插入基准测试:

`CREATE UNLOGGED TABLE test_bigint (
id bigint GENERATED ALWAYS AS IDENTITY (CACHE 200) PRIMARY KEY
);

CREATE UNLOGGED TABLE test_uuid ( id uuid DEFAULT gen_random_uuid() PRIMARY KEY );

我在我的笔记本电脑(SSD,8 核)上使用pgbench自定义脚本执行了基准测试,该脚本有 6 个并发客户端重复运行 1000 个准备INSERT语句的事务五分钟:

INSERT INTO test_bigint /* or test_uuid */ DEFAULT VALUES;

性能对比bigint与uuid自动生成的主键

column1bigintuuid
每秒插入10709074947
每行索引增长30.5 字节41.7 字节

使用bigint明显胜出,但差异并不大。

结论

序列生成的数字和UUID都可用作自动生成的主键。

除非需要在单个数据库之外生成主键,否则请使用标识列,并确保所有主键列的类型为bigint。

原文地址:
https://www.modb.pro/db/98437
https://www.cybertec-sql>postgresql.com/en/uuid-serial-or-identity-columns-for-sql>postgresql-auto-generated-primary-keys/


http://www.niftyadmin.cn/n/351538.html

相关文章

《微服务实战》 第十六章 Spring cloud stream应用

前言 https://github.com/spring-cloud/spring-cloud-stream-binder-rabbit 官方定义Spring Cloud Stream是一个构建消息驱动微服务的框架。应用程序通过inputs或者outputs来与Spring Cloud Stream中binder对象交互。通过我们配置来binding(绑定),而Spring Cloud Stream的bin…

Ubutun安装Anconda3

一、下载Anconda 方法一:官网下载 https://www.anaconda.com/download(比较费时) 可以点击右键复制地址 使用Wget下载 wget https://repo.anaconda.com/archive/Anaconda3-2023.03-1-Linux-x86_64.sh方法 2:清华源 在清华大…

如何爬取股票数据API实时L2交易数据接口?

无论选择哪个接口,都需要注册并获得API密钥才能开始。API密钥一旦获得,就可以使用相应的库获得实时的股票数据接口。 获取API密钥 大多数API需要注册并获得API密钥才能使用。您需要在API提供商的网站上注册并申请API密钥。注册和申请过程中可能需要提供姓…

企业如何将采购成本最小化?

从人员成本到运输和手续费,采购成本涵盖了广泛的费用,这些费用可能会迅速增加。这就是为什么要有一个明确的采购流程,鼓励竞争性招标,并使供应商轻松与你合作。但是,降低采购成本的最有效方法也许是通过实施一个采购软…

Docker安装Yearning3.1.4/禅道18.4【亲测可用】

一、安装Yearning 1、建议先安装go 2、在mysql下新建yearning数据库: 进入mysql:docker exec -it 91ada9def680 /bin/bash 账号密码登录:mysql -uroot -p create database if not exists yearning default character set utf8mb4 collat…

Unity之使用Photon Server + PUN2 开发局域网多人游戏

一.前言 Photon Engine是一款跨平台的实时多人游戏引擎,它提供了可靠的基础设施和工具,使开发者能够轻松地构建和部署多人游戏。Photon Engine支持多种平台,包括PC、移动设备和Web,同时还提供了多种语言的SDK,如C++、C#、Java、JavaScript等,使得开发者可以使用自己熟悉…

Java程序设计入门教程--类的行为

类的成员方法是Java描述类对象行为的途径。成员方法的定义应包含两部分内容:方法声明和方法体。 方法定义常用的格式如下: [public/protected/private][static][final/abstract] returnType methodName([param List]) [throw…

基于OpenCV和PyQt5的跳远成果展示程序

基于OpenCV和PyQt5的跳远成果展示程序 近年来,体育运动越来越受到人们的关注,其中跳远是一项备受瞩目的运动项目。为了更好地展示运动员的跳远成果,本文将介绍一种基于OpenCV和PyQt5的跳远成果展示程序实现方法。 本文的跳远成果展示程序主…