我们正在构建一个新的 REST API,并且有一些关于主键和幂等键的决定。我们的 API 有很多用户,用户只能查找属于他们的对象。请求经过身份验证,因此我们知道userId
与给定请求相关联。
关注的属性:
userId
用户的标识。idempotenyKey
在对 API 的幂等请求中使用的幂等密钥。每个用户唯一。
假设有一个Cars
带有端点的假设表GET .../cars/{id}
,我们将以此为基础进行讨论。
选项
选项 1 - 使用 userId 和 idempotencyKey 的组合作为主键
CREATE TABLE Cars (
userId VARCHAR(255) NOT NULL,
idempotencyKey VARCHAR(255) NOT NULL,
description VARCHAR(255),
PRIMARY KEY (userId, idempotencyKey),
INDEX user_index (userId)
);
项目查找:
GET .../cars/{idempotencyKey}
想法:
- 查找中使用的 id 是可以猜测的。
- 由于对象是经过身份验证的,这似乎不会造成直接的安全问题,但这确实意味着如果 API 中有一个端点允许我们的用户查找某些内容,我们需要假设查找是可猜测的。
选项 2 - 使用 userId + idempotencyKey 的哈希作为主键
CREATE TABLE Cars (
id VARCHAR(255) NOT NULL,
userId VARCHAR(255) NOT NULL,
idempotencyKey VARCHAR(255) NOT NULL,
description VARCHAR(255),
PRIMARY KEY (id),
INDEX user_index (userId)
);
插入时我们会计算id = hash(userId + idempotencyKey)
.
项目查找:
GET .../cars/{id}
想法:
- 主键是不可猜测的。
- 这进一步防止了针对
ids
. - 不再是复合主键。
- 与选项 3 不同,不需要对 (userId, idempotencyKey) 的唯一约束。
选项 3 - 生成 uuid 主键
CREATE TABLE Cars (
id VARCHAR(255) NOT NULL,
userId VARCHAR(255) NOT NULL,
idempotencyKey VARCHAR(255) NOT NULL,
description VARCHAR(255),
PRIMARY KEY (id),
INDEX user_index (userId),
CONSTRAINT unique_by_user UNIQUE (userId, idempotencyKey)
);
插入时我们会设置id = UUID.randomUUID()
.
项目查找:
GET .../cars/{id}
想法:
- id 是完全不可猜测的,并且与任何业务数据无关。
- 需要额外的索引。
选项 4 - 从未向用户公开的自动递增主键
CREATE TABLE Cars (
id INT NOT NULL AUTO_INCREMENT,
userId VARCHAR(255) NOT NULL,
idempotencyKey VARCHAR(255) NOT NULL,
description VARCHAR(255),
PRIMARY KEY (id),
INDEX user_index (userId),
CONSTRAINT unique_by_user UNIQUE (userId, idempotencyKey)
);
有几种方法可以进行查找。查找可以完全基于 idempotencyKey,即:
GET .../cars/{idempotencyKey}
或者,我们可以自动生成一个 referenceId uuid,它将被索引并用于查找。
想法:
- 主键永远不会暴露给用户,这是一种非常常见的做法。
- 如果业务逻辑发生变化,则允许更大的灵活性(尽管对于像 userId 和 idempotencyKey 这样的基本概念,我看不出它们会如何变化)。
- 主键是 INT,因此更小。这允许更小的索引和可以说更高的性能。
- 再次需要一个额外的索引。
寻求有关采取何种方法的建议。围绕性能和规模的考虑非常重要。
谢谢!