powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Конкурентный доступ к таблице
23 сообщений из 23, страница 1 из 1
Конкурентный доступ к таблице
    #35342606
ChameLe0n
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Имеем таблицу, например с пин-кодами... Имеем веб-сайт откуда это все продается... При этом у каждого посетителя есть возможность приобрести срезу несколько пин-кодов одного типа и номинала.
Вопрос: как обеспечить конкурентный доступ к пинам без блокировки таблицы. Очевидно что один пин не может быть дважды. Также если человек заказал 5 пинов, и в таблице есть достаточно пинов ему должно быть отдано 5 пинов.
Что имеем? SELECT FOR UPDATE + UPDATE +COMMIT -> приходит первый клиент. выполняет SELECT FOR UPDATE. Приходит второй - повисает на первой же залоченой записи пока второй не выполнит COMMIT. В случае с PG проблема усугубляется тем, что после разблокировки записи PG не перезапускает сканирование таблицы, как это делает ORACLE. Таким образом вместо 5 пинов может отдаться например 3. Плюс, доступ всеравно получается последовательный.

Кто решал подобные задачи? Как правильно решить данную проблему.
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35342686
Фотография pamir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Почему же повисает. Есть такие волшебные слова, как NOWAIT. Правда, вам они вряд ли понравятся. Тогда клиент не повисает, но генерит ошибку, которую можно поймать. Хотя, почему же не поможет. Как вариант - открываете курсор, бежите по таблице и пытаетесь залочить (по одной) нужное число записей. Те, что валятся на ошибке "уже залочено" просто пропускаете.
Это первый вариант. Второй - делайте механизм резервирования. Толком не скажу, зачем это может понадобиться вам. Ну, например, для того, чтобы второму клиенту можно было показать, что на момент, когда он начал выбирать, всего в базе 100 пинов, но 20 из них уже за кем-то зарезервировано.
В этом случае, пины, выделяемые пользователю резервируются (т.е. в записи есть флаг - резерв/свободен и время резервирования). Пользователь може ещё долго наслаждаться дизайном вашей веб странички, а потом подтвердить или отказаться от покупки. При этом должно быть оговорено время, на которое резервируется пин. По истечению этого времени специальная процедура снимает флаг резерва.
Замороченно, но всё зависит от потребностей.
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35342863
ChameLe0n
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
авторПочему же повисает. Есть такие волшебные слова, как NOWAIT. Правда, вам они вряд ли понравятся. Тогда клиент не повисает, но генерит ошибку, которую можно поймать. Хотя, почему же не поможет. Как вариант - открываете курсор, бежите по таблице и пытаетесь залочить (по одной) нужное число записей. Те, что валятся на ошибке "уже залочено" просто пропускаете.
Это понятно

авторЭто первый вариант. Второй - делайте механизм резервирования. Толком не скажу, зачем это может понадобиться вам. Ну, например, для того, чтобы второму клиенту можно было показать, что на момент, когда он начал выбирать, всего в базе 100 пинов, но 20 из них уже за кем-то зарезервировано.
В этом случае, пины, выделяемые пользователю резервируются (т.е. в записи есть флаг - резерв/свободен и время резервирования). Пользователь може ещё долго наслаждаться дизайном вашей веб странички, а потом подтвердить или отказаться от покупки. При этом должно быть оговорено время, на которое резервируется пин. По истечению этого времени специальная процедура снимает флаг резерва.
Замороченно, но всё зависит от потребностей.
А как конкурентно зарезервировать тогда? :) . Если же резервировать каким то образом заранее, до того как прийдет клиент. То может сложится ситуация когда пины в базе есть а вот для конкретного клиента их нет.

Вариант с блокировкой таблице рабочий.. Но очевидно, что пока работают 100 клиентов - это будет нормально.. если их будет 1000 то все встанет колом и все будут ждать поочереди своих пинов...
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35342982
Фотография pamir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Боюсь, что вы предполагаете использовать оптимистическую стратегию блокировок, тогда как в вашем случае (да и в Вебе вообще) нужно использовать пессимистичскую - т.е. свести время нахождения записей в блокированном виде к минимуму.
Итак, пришёл пользователь. Сказал, что хочет 5 пинов. Мы их не залочили. Мы их зарезервировали (т.е. быстренько нашли пять нелоченых записей, залочили, обновили у них флаг "резерв" и закоммитились. Ну и запомнили - кому их зарезервировали). Всё. Конкуренция только если этот короткий промежуток времени кто-то ещё будет резервировать записи.
Если же сделать так, что пользователь заказал 5 пинов. Мы их залочили и ждём, когда он соизволит подтвердить свой заказ, то всё может встать колом и при 100 пользователях ОДНОВРЕМЕННО заказывающих пины.

Да, как вариант первого примера, но без резерва - мы запомнили, что пользователю нужно 5 пинов (но ничего не резервировали), он подтвердил и тогда мы побежали лочить записи, обновлять из как вам нужно - ставить пометку "продано", и тут же коммититься. Опять же это делается одномоментно (т.е. исключительно за время, требуемое для лочки, изменения и коммита), но не за время обдумывания пользователем каких-то своих домашних проблем.
В этом случае, если количество пинов ограничено, может случиться так, что пользователь заказав 5 штук так долго думал, прежде чем нажать кнопку подтверждения, что их уже раскупили. Он будет очень удивлён. В этом случае резервирование поможет.
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35342988
Фотография pamir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ChameLe0nВариант с блокировкой таблице рабочий..Не надо блокировать всю таблицу. Надо блокировать только нужные записи.
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35343092
ChameLe0n
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
авторКонкуренция только если этот короткий промежуток времени кто-то ещё будет резервировать записи.

Считайте что конкуренция постоянная. Предположим есть только один типа карточки. И 100 клиентов одновременно жмут кнопку - купить 10 пинов. Предположим что время обработки каждого запроса(10 пинов) - 1 с. Как мне отдать все данные клиентам за ~1с. А не за 100 с.

авторон подтвердил и тогда мы побежали лочить записи, обновлять из как вам нужно - ставить пометку "продано", и тут же коммититься. Опять же это делается одномоментно
Не понял Вашего хода мыслей. Я пример привел с "побежали лочить записи" в первом посте.
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35343384
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
насколько я понял, проблема в том что для "select ... for update" нет способа сказать "игнорируй уже заблокированные на данный момент строки", а перебирать записи постоянно перезапуская транзакцию - не хочется.

тогда такая идея - добавить в таблицу пинов колонку g:
Код: plaintext
alter table pins add g int not null default (random() *  100000 )::int %  7 ;
которая сгруппирует пины в блоки (в данном примере - в семь блоков).

при выборке - выбирать из блока "от балды" типа
Код: plaintext
select pin from pins where g = (random() *  100000 )::int %  7  and not sold limit  1  for update;
тогда с некоторой вероятностью ( незнаю какой :) ) такие одновременные выборки выполнятся одновременно, так как попадут в разные группы (но часть конечно попадёт в одну группу и будет заблокирована). так как все запросы будут идти из одного интерфейса - можно даже наверное придумать способ что бы для каждого следующего пользователя номер группы возрастал, допустим тот же serial типа порядковый номер, который выдаётся новому пользователю при начале сессии... ааа стоп ! :) можно же сразу сделать:
Код: plaintext
select pin from pins where g = nextval('users_requests_counter_seq') %  7  and not sold limit  1  for update;


--
„Истина — это вовсе не то, что можно убедительно доказать, это то, что
делает всё проще и понятнее“ — Антуан де Сент-Экзюпери
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35343393
Фотография pamir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ёшнасколько я понял, проблема в том что для "select ... for update" нет способа сказать "игнорируй уже заблокированные на данный момент строки", а перебирать записи постоянно перезапуская транзакцию - не хочется.Зачем постоянно перезапускать транзакцию?
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35343397
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
кхм... разовью мысль - можно же ещё проще - если выбирать пины не подряд, а случайные :) вероятность того что допустим с хорошим random из 100000 свободных пинов две транзакции выберут один и тот же пин и будут заблокированы - достаточно мала :) имхо.


--
„Истина — это вовсе не то, что можно убедительно доказать, это то, что
делает всё проще и понятнее“ — Антуан де Сент-Экзюпери
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35343406
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pamir Ёшнасколько я понял, проблема в том что для "select ... for update" нет способа сказать "игнорируй уже заблокированные на данный момент строки", а перебирать записи постоянно перезапуская транзакцию - не хочется.Зачем постоянно перезапускать транзакцию?ну всмысле что "постоянно обрабатывать exception ERROR: could not obtain lock on row in relation"
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35343414
Фотография pamir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ёш pamir Ёшнасколько я понял, проблема в том что для "select ... for update" нет способа сказать "игнорируй уже заблокированные на данный момент строки", а перебирать записи постоянно перезапуская транзакцию - не хочется.Зачем постоянно перезапускать транзакцию?ну всмысле что "постоянно обрабатывать exception ERROR: could not obtain lock on row in relation"Ааа. Ну так это ж другое совсем.
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35343431
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ChameLe0n авторКонкуренция только если этот короткий промежуток времени кто-то ещё будет резервировать записи.

Считайте что конкуренция постоянная. Предположим есть только один типа карточки. И 100 клиентов одновременно жмут кнопку - купить 10 пинов. Предположим что время обработки каждого запроса(10 пинов) - 1 с. Как мне отдать все данные клиентам за ~1с. А не за 100 с.Для того чтобы улучшить конкурентный доступ к объектам (таблицам, записям и тд) можно либо уменьшить время блокировки, либо сегментировать доступ к объекту (чтобы у него был не одна блокировка, а несколько независимых).
1) В коротких транзакциях обновлять количество доступных карточек.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
BEGIN;
UPDATE pins SET qty = qty WHERE type_id = 'xxx' RETURNING qty INTO _qty;
IF _qty >=  0  THEN
  RETURN False;
ELSE
  RIASE EXCEPTION 'Товар отсутствует'; -- Делаем откат транзакции любым удобным способом (rollback'ом, к примеру)
END IF;
COMMINT;
BEGIN;
  -- Тут бизнес-логика
COMMINT;
То есть значала резервируем количество карт и быстренько закрываем тразакцию, чтобы не блокировать ресурс. После этого открываем новую транзакцию, где выполняем "медленную" бизнес-логику продажи товара.
Есть большой минус у этого варианта - надо синхронизировать две транзакции через 2PC, либо вручную.
2) ну а чтобы сделать сегментацию блокировок, надо крепко подумать :) Отчасти вариант будет похож на предложенный Ёж'ом :)
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35344304
ChameLe0n
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ёшкхм... разовью мысль - можно же ещё проще - если выбирать пины не подряд, а случайные :) вероятность того что допустим с хорошим random из 100000 свободных пинов две транзакции выберут один и тот же пин и будут заблокированы - достаточно мала :) имхо.

Имеет право на жизнь. Только насколько я понимаю запрос вида

Код: plaintext
1.
2.
3.
4.
5.
6.
SELECT
   *
FROM
   pins
ORDER BY
   random()
LIMIT  10 

Каждый раз будет тянуть все данные из базы. Если пинов много - будет тяжеловато. Второй момент - это не решит проблему с тем что может вернуться меньше пинов чем запрошено при наличии необходимого количества. Думаю, может перенести логику на сервер приложений. В Java5 появились всякие вкусности на тему конкуретного доступа...
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35344319
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ChameLe0nДумаю, может перенести логику на сервер приложений. В Java5 появились всякие вкусности на тему конкуретного доступа...При кластеризации возможно большие проблемы синхронизации, да и по большому проблема не решится, а лишь прибавит сложности программы. И, соответственно, затруднит поддержку.
ИМХО, это задача, которую лучше всего решать на уровне БД.
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35344326
ChameLe0n
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
авторТо есть значала резервируем количество карт и быстренько закрываем тразакцию, чтобы не блокировать ресурс. После этого открываем новую транзакцию, где выполняем "медленную" бизнес-логику продажи товара.

Вцелом медленной бизнес-логики нет. Я показал как пример, что непонятно как добится приемлемого параллелилизма в конкретном случае и
авторТо есть значала резервируем количество карт и быстренько закрываем тразакцию
может оказаться очень медленным при 1000 паралельных клиентах
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35344350
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ChameLe0n1000 конкурентных запросов на изменение одного ресурса - это очень очень очень круто!
Но если предположить такую ситуацию теоретически, то возможно, что пессимистическая блокировка всей таблицы на запись (или блока строк) - это единственное решение, так как любая любая оптимистическая блокировка чревата постоянными эксепшенами (это будет аналогично запуску 1000 приложений на однопроцессорном компьютере, процессор всё время будет занят переключением между задачами, а не выполнением этих задач. В итоге, ни одна задача не будет решена в приемлемое время).
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35348923
ChameLe0n
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
автор1000 конкурентных запросов на изменение одного ресурса - это очень очень очень круто!

Ок, погорячился... но 50 или 100 - вполне реальная цифра.. Еще идеи есть у кого?
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35350191
Konstantin~
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Имеем 50-100 килентов которые "сражаются" за ресурс, в данном случае множество еще-не-купленных пинов.

На мой взгляд возможны два решения:

1. "взять все и поделить", как предложил Еш . Массив пинов разделяется на N сегментов по какому- либо способу, где N = "concurrency level", i.e. предполагаемое кол-во одновременных клиентов.
Каждому клиенту в момент открытия сессии присваивается номер сегмента массива пинов n. Далее вы делаете ваш SELECT FOR UPDATE WHERE segment_num = ... + UPDATE +COMMIT

2. "асинхронно и очередь". т.е. классическая схема продажи чего либо order-invoice-inventory-account-transactions. При наличии не купленных пинов намерение клиента купить X пинов записывается как заказ. Клиент ждет когда его заказ выполнят и выдадут справку-счет где указанны конкретные номера купленных пинов. Выполнением заказов должен заниматся отдельный процесс который работает как по cron или как service/daemon. Такой процесс обрабатывает заказы one-by-one: "BEGIN: считывает заказ, проводит платеж, помечает Х пинов как проданные и номера записывает в иновойс ... изменяет балансы, пишет transaction лог ... етц COMMIT".

3. вариант "2 + 1", то есть вариант 2, только когда имеются несколько процессов которые занимаются исполнением заказов параллельно. Чтобы "заказо-исполнители" не дрались за ресур, его надо разбить на части, например если есть два параллельных процесса, то можно организовать чтоб один работает по четным номерам заказов и отдает пины у которых pin_number <= ( pins_total / 2 ) а другой обрабатывает нечётные заказы и отдаёт пины pin_number > ( pins_total / 2 )
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35350194
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ChameLe0n автор1000 конкурентных запросов на изменение одного ресурса - это очень очень очень круто!

Ок, погорячился... но 50 или 100 - вполне реальная цифра.. Еще идеи есть у кого?имхо быстрее чем вариант Dan Black'а с блокировкой не счётчике - не сделать
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35350422
ChameLe0n
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Konstantin~Имеем 50-100 килентов которые "сражаются" за ресурс, в данном случае множество еще-не-купленных пинов.

На мой взгляд возможны два решения:

1. "взять все и поделить", как предложил Еш . Массив пинов разделяется на N сегментов по какому- либо способу, где N = "concurrency level", i.e. предполагаемое кол-во одновременных клиентов.
Каждому клиенту в момент открытия сессии присваивается номер сегмента массива пинов n. Далее вы делаете ваш SELECT FOR UPDATE WHERE segment_num = ... + UPDATE +COMMIT

2. "асинхронно и очередь". т.е. классическая схема продажи чего либо order-invoice-inventory-account-transactions. При наличии не купленных пинов намерение клиента купить X пинов записывается как заказ. Клиент ждет когда его заказ выполнят и выдадут справку-счет где указанны конкретные номера купленных пинов. Выполнением заказов должен заниматся отдельный процесс который работает как по cron или как service/daemon. Такой процесс обрабатывает заказы one-by-one: "BEGIN: считывает заказ, проводит платеж, помечает Х пинов как проданные и номера записывает в иновойс ... изменяет балансы, пишет transaction лог ... етц COMMIT".

3. вариант "2 + 1", то есть вариант 2, только когда имеются несколько процессов которые занимаются исполнением заказов параллельно. Чтобы "заказо-исполнители" не дрались за ресур, его надо разбить на части, например если есть два параллельных процесса, то можно организовать чтоб один работает по четным номерам заказов и отдает пины у которых pin_number <= ( pins_total / 2 ) а другой обрабатывает нечётные заказы и отдаёт пины pin_number > ( pins_total / 2 )
Спасибо, принято к сведенью
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35350426
ChameLe0n
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Кстати, может в других СУБД есть красивое решение?
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35350549
Konstantin~
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
IMHO нету т.к. проблема не в бд.
...
Рейтинг: 0 / 0
Конкурентный доступ к таблице
    #35351419
Фотография Warstone
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Можно развить вариант 2: Есть таблица заказов, куда валятся заказы и есть демон, который выполняет эти заказы, вне зависимости от того, подтвердил или нет пользователь свой заказ... То есть заказ обрабатывается так:
Пользователь сказал "Хочу 5 пинов".
Эта "хотелка" записалась в таблицу предварительных заказов. Попала в карзину пользователя и т.д.
Тут информацтя попала на компьютер пользователя, где он может выбрать ещё что-то или просто нажать на кнопку выписать.

Пораллельно по крону запускается процедура, которая "выполняет" предварительные заказы: Обновляет записи в "хотелке", добавляя их пинами. А в таблицу пинов ставит флаг "зарезервированно".

Если пользователь надап "выписать", то идет проверка: выполнена-ли "хотелка". Если она выполнена, то эта "хотелка" переносится/изменяется ее статус до продано. Если она ещё не выполнена, то пользователю говорят что.. так и так... обождите 5 секунд.
Если "хотелка" выполнена частично, то это значит что пинов больше нет. О чем радостно сообщают пользователю.

Если пины "выписаны", то та-же кроновая процедура доносит эту информацию до таблицы пинов.

Аналогично работает функция "я больше не хочу 5 пинов".

Вобщем как-то так... Таки образом "хотелки" записываются в отдельную таблицу, которая не растет и зависит напрямую от кол-ва человек. Что позволяет не тормозить пользователя до тех пор когда его "хотелку" выполнят, но в то-же время "хотелку" выполняют не тогда, когда пользователь сказал "выписать", а тогда, когда он усердно изучает нтерфейс(в большинстве случаев). Таким образом после того, как пользователь нажал "выписать" и ушел с сервера его запрос все ещё может обрабатываться (доносится ценная информация о том что пин уже продан, а не зарезервирован), только вот пользователь об этом не узнает.

Минусы тут такие: Если будет много отказов (Выбрали 5 пинов, а потом отказались) то может получиться что все пины зарезервированны, хотя от них отказались.
...
Рейтинг: 0 / 0
23 сообщений из 23, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Конкурентный доступ к таблице
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]