#database #postgresql #concurrency #unique #file-locking
#База данных #postgresql #параллелизм #уникальный #блокировка файла
Вопрос:
У меня есть список уникальных номеров в файле. Всякий раз, когда пользователь моих сайтов делает запрос, мне нужно получить значение из этого списка, чтобы ни один другой пользователь не получил такое же значение. Может быть много одновременных запросов, и, следовательно, основная проблема заключается в том, чтобы гарантировать, что никакие два пользователя не получат одинаковое значение. Я не так обеспокоен производительностью в том смысле, что пользователь не ожидает никакого ответа после выполнения запроса, и поэтому все может происходить в фоновом режиме. Из того, что я прочитал, это может быть реализовано с использованием блокировок файлов, или я мог бы хранить данные в БД и использовать блокировки БД. Я просто хочу получить представление о том, как лучше всего это сделать. Я использую postresql в качестве своей базы данных, и мне было интересно, можно ли это сделать, используя последовательности, в которых я сохраняю текущий номер строки в последовательности, чтобы моя программа знала, какую строку читать из БД. Но опять же, я не уверен, как предотвратить чтение одной и той же строки несколькими процессами, прежде чем у любого из них появится возможность ее обновить.
Ответ №1:
Базы данных на самом деле не делают много вещей, но то, что они делают, они делают очень хорошо. В частности, они обрабатывают одновременный доступ к данным. Вы можете использовать это в своих интересах:
- Создайте таблицу:
create table special_number (id serial, num varchar(100) not null);
- Обеспечение уникальности:
create unique index special_number_num on special_number(num);
- Загружайте свои номера в таблицу с помощью
COPY
, позволяя id автоматически подсчитывать от 1 - Создайте последовательность:
create sequence num_seq;
- Используйте тот факт, что
nextval
функция postgres гарантированно безопасна для одновременного выбора номера из списка :select num from special_number where id = (select nextval('num_seq'));
. Это вернетсяnull
, если у вас закончатся номера.
Этот подход на 100% безопасен и прост — внутренние преимущества postgres как базы данных выполняют всю тяжелую работу.
Вот SQL, извлеченный из приведенного выше:
create table special_number (id serial, num varchar(100) not null);
create unique index special_number_num on special_number(num);
copy special_number(num) from '/tmp/data.txt'; -- 1 special number per file line
create sequence num_seq;
select num from special_number where id = (select nextval('num_seq'));
Этот SQL был протестирован на postgres и работает должным образом.
Комментарии:
1. Большое спасибо за ваш ответ, у меня было только одно сомнение по этому поводу. Для чего нужен идентификатор владельца для таблицы special_number.
2. @Bohemian кроме того, не правда ли, что последовательности не всегда возвращают числа в последовательном порядке, что приведет к тому, что некоторые идентификаторы в специальной таблице чисел никогда не будут пользователем. Каково решение в таких ситуациях или как гарантировать, что функция sequences nextval всегда возвращает идентификатор в последовательности.
3. @Bohemian Еще одна проблема, с которой я сталкиваюсь с текущим кодом, заключается в том, что
select num from special_number where id = nextval('num_seq')
он возвращает все специальные числа в таблице. Я пытался использовать Limit 1, но проблемы с этим заключаются в том, что последовательность обновляется до len (специальная таблица чисел), поскольку nextval вызывается один раз для каждой записи. Как я мог бы решить эту проблему, используя ваш подход4. @Bohemian Еще одна проблема, с которой я сталкиваюсь с текущим кодом, заключается в том, что выберите num из special_number, где id = nextval(‘num_seq’) возвращает все специальные номера в таблице. Я пытался использовать Limit 1, но проблемы с этим заключаются в том, что последовательность обновляется до len (специальная таблица чисел), поскольку nextval вызывается один раз для каждой записи. Я преодолел эту проблему, используя
select num from special_number where id = (Select nextval('num_seq'));
вместо этого. В любом случае спасибо5. @RiteshKadmawala — пожалуйста, ознакомьтесь с обновленным ответом, в котором есть ответы на все ваши комментарии. вы правы в выборе каждой строки: измените SQL
... where id = (select nextval('num_seq'))
на исправление — см. Ответ.