RDBM 和 SQL 数据库中主键、自增和 UUID 的最佳实践

Posted

技术标签:

【中文标题】RDBM 和 SQL 数据库中主键、自增和 UUID 的最佳实践【英文标题】:Best practices on primary key, auto-increment, and UUID in RDBMs and SQL databases 【发布时间】:2019-02-24 03:11:59 【问题描述】:

我们正在为用户实体设计一个表格。唯一重要的要求是用户实体(例如他们的个人资料)应该有一个永久 URL。网上有很多关于 int/long vs UUID 的内容。但我仍然不清楚。

    考虑到配置文件包含私人信息这一事实,在 URL 中嵌入可预测的 ID 并不是一个好主意。我说的对吗? 为了满足第一个要求,我可以将主键作为 UUID 并将其嵌入到 URL 中。但是有两个问题。我是否应该担心以 UUID 作为主键的性能损失?索引,插入,选择,加入?

话虽如此,以下哪一项更好(相对于上述)?

CREATE TABLE users(
  pk UUID NOT NULL,
  .....
  PRIMARY KEY(pk)
);

CREATE TABLE users(
  pk INT NOT NULL AUTO_INCREMENT,
  id UUID NOT NULL,
  .....
  PRIMARY KEY(pk),
  UNIQUE(id)
);

【问题讨论】:

【参考方案1】:

UUID 用作pk:第一个问题是,UUID 占用9x 存储空间而不是int。第二个问题是,如果您需要更频繁地按pk 排序,甚至不要考虑UUID。 pk 的 UUID 不会影响 where 条件或除 sort 之外的其他条件的时间复杂度。

int 用作pk:很容易猜到。蛮力攻击者会喜欢这个。这是唯一的问题,也是最大的问题。

int 用作pk,但同时保留UUID:如果UUID 不是pk,则按UUID 搜索会增加时间复杂度。虽然,所有的关系都将由int 维护,但是,当您使用 UUID 进行搜索时,需要一些时间。由于关系在int,因此9x 存储问题在这里得到解决。因此是最好的。

【讨论】:

【参考方案2】:

这个问题非常基于意见,所以这是我的问题。

我的看法是使用第二个,一个独立于 PK 的 UUID。问题是:

PK 是独一无二的,不会向公众公开。 UUID 是唯一的,可能会向公众公开。

如果由于任何原因 UUID 被泄露,您需要更改它。更改 PK 可能会很昂贵并且有很多副作用。如果 UUID 与 PK 是分开的,那么它的更改(尽管不是微不足道的)影响要小得多。

【讨论】:

数字 ID 或 UUID 也不应该是秘密的。安全性不应基于无法猜测的 ID,无论如何都应检查访问权限。 @ymajoros 并非所有资源都是完全私有的。一个例子是“任何有链接的人”可以访问的东西。谷歌通过 Docs 和 Sheets 之类的东西来做到这一点。在这种情况下,自动递增的 ID 应该保密,以防止 fusking 式攻击以发现文档。在这种情况下,UUID 会很棒,因为没有真正的模式可以猜测,因此查找它们很耗时。因此,它们提供了可接受的保护层,同时保持易于访问。安全性不仅仅是开/关。安全性始终是妥协级别,包括各种风险和可用性权衡。 @JoelMellon 网址不是安全性所在。使其安全的是谷歌身份验证+您提供的任何访问权限的组合。当然,如果你公开文件,你就会明白,但没有人强迫你这样做。使用增量 ID 不会降低安全性,但会使公共文档是公开的这一事实更加清晰。 我认为@JoelMellon 想说的是,出于某种原因,您可能不希望外部用户以某种方式确定您系统中的交易记录数量,因为它们可以通过以下方式公开访问排序的数字 ID。很好,它们是公开的,但没有人能确定你拥有多少资源。【参考方案3】:

我看到一篇很好的文章,它解释了使用 UUID 作为主键的利弊。最后,它建议对 PK 使用增量整数,对外界使用 UUID。永远不要将你的 PK 暴露在外面。

在多种不同情况下使用的一种解决方案对我有用 简而言之,两者都使用。 (请注意:不是一个好的解决方案 - 请参阅 请注意对下面原始帖子的回复)。在内部,让 数据库以小型、高效、数字的方式管理数据关系 顺序键,无论是 int 还是 bigint。然后添加一个填充的列 使用 UUID(可能作为插入触发器)。范围内 数据库本身,可以使用通常的 PK 和 FK。

但是当对数据的引用需要暴露给 外部世界,即使“外部”意味着另一个内部系统,他们 必须仅依赖于 UUID。这样,如果你真的需要改变 您的内部主键,您可以确定它的范围仅限于一个 数据库。 (注意:正如 Chris 所观察到的,这完全是错误的)

我们在另一家公司对客户数据使用了这种策略,只是为了避免 “可猜测”的问题。 (注意:避免与阻止不同,请参阅 下面)。

在另一种情况下,我们会生成一个文本“slug”(例如,在 像这样的博客文章),这将使 URL 更人性化 友好的。如果我们有重复,我们只需附加一个散列值。

即使作为“辅助主键”,在 字符串形式错误:使用内置数据库机制作为值 我希望存储为 8 字节整数。

使用整数,因为它们很有效。使用数据库 除了任何外部引用之外,还实现了 UUID 混淆。

https://tomharrisonjr.com/uuid-or-guid-as-primary-keys-be-careful-7b2aa3dcb439

【讨论】:

【参考方案4】:

这实际上是一个选择问题,从我的角度来看,这个问题可以提出基于意见的答案。我总是做的,即使它是多余的,我在自动增量列上创建主键(我称之为技术键)以使其在数据库中保持一致,允许“主键”在设计阶段出现问题时更改,并且还允许在任何其他表中的外键约束指向键的情况下消耗更少的空间,并且我使候选键唯一且不为空。

技术密钥是您通常不会向最终用户展示的东西,除非您决定这样做。对于您仅出于任何目的(例如修改日期、创建日期、版本、更改记录的用户等)而保留在数据库级别的其他技术列,这可能是相同的。

在这种情况下,我会选择您的第二个选项,但稍作修改:

CREATE TABLE users(
  pk INT NOT NULL AUTO_INCREMENT,
  id UUID NOT NULL,
  .....
  PRIMARY KEY(pk),
  UNIQUE(id)
);

【讨论】:

@Kamil,当有关系时,应该使用 auto-inc 作为 FK 吗?但这是否意味着简单查询会有额外的连接?例如 1 对 m 的 customer-payments 关系,意味着获得 customerKey 的付款,我们将使用 auto-inc where customerKey = key from req 加入客户的付款,而不是仅仅查询 customerKey = key 的支付表来自请求。【参考方案5】:

不要把它作为你的数据库主键:这会在你想改变你的数据库技术的未来引起问题。如果您增加用户数量,您的竞争对手就会知道您拥有多少用户以及您添加新用户的速度。

【讨论】:

以上是关于RDBM 和 SQL 数据库中主键、自增和 UUID 的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

mybatismybatis中insert 主键自增和不自增的插入情况mysql

(解释文)My SQL中主键为0和主键自排约束的关系

2017年2月21日 Oracle数据库,怎么设置表中主键的自增

Java程序连接Oracle数据库时怎么设置主键自增

mysql insert插入时实现如果数据表中主键重复则更新,没有重复则插入的四种方法

SQL中主键的使用