Замена последовательности случайным числом
Я хотел бы заменить некоторые из последовательностей, которые я использую для id в моем postgresql db, с помощью моего собственного генератора id. Генератор будет производить случайное число с checkdigit в конце. Итак:
SELECT nextval('customers')
будет заменено на что-то вроде этого:
SELECT get_new_rand_id('customer')
Затем функция вернет числовое значение, такое как: [1-9][0-9]{9}
, где последняя цифра является контрольной суммой.
У меня есть проблемы:
- Как сделать элемент атомарным
- Как избежать повторного обращения к одному и тому же идентификатору дважды (это можно было бы поймать, пытаясь вставить его в столбец с уникальным ограничением, но потом его до позднего я думаю)
- Это хорошая идея?
Примечание1. Я не хочу использовать uuid, так как он должен быть передан с клиентами, а 10 цифр намного проще общаться, чем с 36 символами uuid.
Примечание2. Функция редко вызывается с помощью SELECT get_new_rand_id()
, но будет назначаться как значение по умолчанию для столбца id вместо nextval()
.
РЕДАКТИРОВАТЬ: Хорошо, хорошая дискуссия ниже! Вот несколько объяснений, почему:
После прочтения обсуждения я, безусловно, могу видеть, что мой подход - это не лучший способ решить мою проблему, но у меня нет другой хорошей идеи, как ее решить, поэтому, пожалуйста, помогите мне здесь.
- Должен ли я добавить дополнительный столбец, где я помещаю идентификатор, который я выставляю клиенту, и сохраняю его как первичный ключ?
- Как я могу генерировать идентификатор для эффективного и эффективного использования?
- Требуется ли checkdigit?
Ответы
Ответ 1
Для создания уникальных и случайных идентификаторов из сериализации использование шифров может быть хорошей идеей. Поскольку их вывод является биективным (существует взаимно однозначное сопоставление между входными и выходными значениями), вы будете не иметь никаких коллизий, в отличие от хэшей. Это означает, что ваши идентификаторы не должны быть такими же хэшами.
Большинство криптографических шифров работают с 64-битными или более крупными блоками, но в wiki PostgreSQL есть пример процедуры PL/pgSQL для "некриптографического" шифрования, которая работает с типом (32-бит) int
. Отказ от ответственности: я не пробовал использовать эту функцию самостоятельно.
Чтобы использовать его для ваших основных ключей, запустите вызов CREATE FUNCTION со страницы вики, а затем в таблицах empty выполните:
ALTER TABLE foo ALTER COLUMN foo_id SET DEFAULT pseudo_encrypt(nextval('foo_foo_id_seq')::int);
И вуаля!
pg=> insert into foo (foo_id) values(default);
pg=> insert into foo (foo_id) values(default);
pg=> insert into foo (foo_id) values(default);
pg=> select * from foo;
foo_id
------------
1241588087
1500453386
1755259484
(4 rows)
Ответ 2
Я добавил свой комментарий к вашему вопросу, а затем понял, что должен был лучше объяснить себя... Приносим извинения.
У вас может быть второй ключ, а не первичный ключ, который отображается пользователю. Этот ключ может использовать первичный как семя для хеш-функции, которую вы описываете, и быть тем, который вы используете для поиска. Эта клавиша генерируется триггером после вставки (что намного проще, чем попытка обеспечить атомарность операции) и
Это ключ, который вы разделяете со своими клиентами, никогда не ПК. Я знаю, что есть дебаты (хотя, я не могу понять, почему), если ПК должны быть невидимыми для пользовательских приложений или нет. Современные методы проектирования баз данных и мой личный опыт, похоже, предполагают, что ПК не должны быть видимыми для пользователей. Они склонны придавать им значение, и со временем это очень плохо - независимо от того, есть ли у них контрольная цифра в ключе или нет.
Ваши соединения будут выполняться с помощью ПК. Этот другой сгенерированный ключ только предполагается использовать для поиска клиентов. Это лицо, ПК - это кишки.
Надеюсь, что это поможет.
Изменить: FWIW, мало что можно сказать о "правильном" или "неправильном" в дизайне базы данных. Иногда это сводится к выбору. Я думаю, что выбор, с которым вы сталкиваетесь, будет лучше обслуживаться, оставив ПК только и создав вторичный ключ - именно это.
Ответ 3
Я думаю, вы слишком усложняете это. Почему бы не позволить базе данных делать то, что она делает лучше всего, и позволить ей заботиться об атомарности и гарантировать, что один и тот же идентификатор не используется дважды? Почему бы не использовать тип postgresql SERIAL и получить первичный ключ с автогенерированным суррогатом, как и столбец IDENTITY целого числа в SQL Server или DB2? Используйте это вместо столбца. Плюс это будет быстрее, чем ваша пользовательская функция.
Я соглашаюсь скрыть этот суррогатный первичный ключ и использовать открытый ключ (с уникальным ограничением на него) для поиска клиентов в вашем интерфейсе.
Используете ли вы последовательность, потому что вам нужен уникальный идентификатор для нескольких таблиц? Это, как правило, указывает на необходимость переосмыслить дизайн вашей таблицы, и эти несколько таблиц, возможно, должны быть объединены в один, с автогенерированным суррогатным первичным ключом.
Также см. здесь
Ответ 4
Как вы генерируете случайные и уникальные идентификаторы - полезный вопрос, но вы, кажется, делаете встречное продуктивное предположение о том, когда их генерировать!
Я хочу сказать, что вам не нужно генерировать этот идентификатор во время создания ваших строк, потому что они по существу не зависят от вставленных данных.
То, что я делаю, является предварительным генерированием случайного идентификатора для будущего использования, таким образом, я могу взять свое собственное сладкое время и абсолютно гарантировать, что они уникальны, и нет обработки, которая будет сделана во время вставки.
Например, у меня есть таблица заказов с order_id. Этот идентификатор генерируется "на лету", когда пользователь вводит порядок, постепенно 1,2,3 и т.д. Навсегда. Пользователь не должен видеть этот внутренний идентификатор.
Затем у меня есть другая таблица - random_ids с (order_id, random_id). У меня есть рутина, которая работает каждую ночь, которая предварительно загружает эту таблицу с достаточным количеством строк, чтобы покрыть заказы, которые могут быть вставлены в следующие 24 часа. (Если я когда-либо получаю 10000 заказов за один день, у меня будет проблема - но это будет хорошая проблема!)
Этот подход гарантирует уникальность и отвлекает любую обработку от транзакции вставки и в пакетную процедуру, где она не влияет на пользователя.
Ответ 5
Лучше всего, вероятно, быть какой-то формой хеш-функции, а затем добавить контрольную сумму в конец.
Ответ 6
Если вы не используете это слишком часто (у вас нет нового клиента каждую секунду, не так ли?), тогда можно просто получить случайное число, а затем попытаться вставить запись. Просто будьте готовы повторить вставку с другим номером, когда он терпит неудачу с уникальным нарушением ограничения.
Я бы использовал числа от 1000000 до 999999 (900000 возможных номеров одинаковой длины) и проверил цифру, используя алгоритм UPC или ISBN 10. 2 контрольных цифры были бы лучше, хотя они устранили бы 99% человеческих ошибок вместо 9%.