Мы создаем новый REST API и должны принять несколько решений относительно первичного ключа и idempotency_key.В нашем API много пользователей, и пользователям разрешено только искать принадлежащие им объекты.Запросы аутентифицируются, поэтому мы знаем userId
, связанный с данным запросом.
Свойства, о которых идет речь:
userId
Идентификатор пользователя. idempotenyKey
Ключ идемпотентности, используемый в идемпотентных запросах к API. Уникальный для пользователя.
Предположим, есть гипотетическая таблица Cars
с конечной точкой GET .../cars/{id}
, на которой мы будем основывать наше обсуждение.
Параметры
Вариант 1 - использовать составной идентификатор пользователя и 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}
Мысли:
- Идентификатор, используемый в поисках, является предположительным.
- Поскольку объекты проходят проверку подлинности, это, по-видимому, не представляет собой непосредственную проблему безопасности, но это означает, что если в 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}
Мысли:
- Идентификатор абсолютно неуверен и не привязан к каким-либо бизнес-данным.
- Требуетсядополнительный индекс.
Вариант 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}
В качестве альтернативы, мы можем автоматически сгенерировать ссылочный идентификатор UUID, который будет проиндексирован и использован для поиска.
Мысли:
- Первичный ключ никогда не предоставляется пользователю, что является довольно распространенной практикой.
- Обеспечивает большую гибкость при изменении бизнес-логики (хотя для фундаментальных концепций, таких как userId и idempotencyKey, я не вижу, как они изменились бы).
- Первичный ключ - INT и, таким образом,меньше.Это учитывает меньшие индексы и возможно большую производительность.
- Снова требуется дополнительный индекс.
Нужен совет, какой подход выбрать.Очень важны соображения относительно производительности и масштаба.
Спасибо!