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

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

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

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

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

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

авторон подтвердил и тогда мы побежали лочить записи, обновлять из как вам нужно - ставить пометку "продано", и тут же коммититься. Опять же это делается одномоментно
Не понял Вашего хода мыслей. Я пример привел с "побежали лочить записи" в первом посте.
...
Рейтинг: 0 / 0
29.05.2008, 15:08
    #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
29.05.2008, 15:11
    #35343393
pamir
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Конкурентный доступ к таблице
Ёшнасколько я понял, проблема в том что для "select ... for update" нет способа сказать "игнорируй уже заблокированные на данный момент строки", а перебирать записи постоянно перезапуская транзакцию - не хочется.Зачем постоянно перезапускать транзакцию?
...
Рейтинг: 0 / 0
29.05.2008, 15:13
    #35343397
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Конкурентный доступ к таблице
кхм... разовью мысль - можно же ещё проще - если выбирать пины не подряд, а случайные :) вероятность того что допустим с хорошим random из 100000 свободных пинов две транзакции выберут один и тот же пин и будут заблокированы - достаточно мала :) имхо.


--
„Истина — это вовсе не то, что можно убедительно доказать, это то, что
делает всё проще и понятнее“ — Антуан де Сент-Экзюпери
...
Рейтинг: 0 / 0
29.05.2008, 15:15
    #35343406
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Конкурентный доступ к таблице
pamir Ёшнасколько я понял, проблема в том что для "select ... for update" нет способа сказать "игнорируй уже заблокированные на данный момент строки", а перебирать записи постоянно перезапуская транзакцию - не хочется.Зачем постоянно перезапускать транзакцию?ну всмысле что "постоянно обрабатывать exception ERROR: could not obtain lock on row in relation"
...
Рейтинг: 0 / 0
29.05.2008, 15:18
    #35343414
pamir
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Конкурентный доступ к таблице
Ёш pamir Ёшнасколько я понял, проблема в том что для "select ... for update" нет способа сказать "игнорируй уже заблокированные на данный момент строки", а перебирать записи постоянно перезапуская транзакцию - не хочется.Зачем постоянно перезапускать транзакцию?ну всмысле что "постоянно обрабатывать exception ERROR: could not obtain lock on row in relation"Ааа. Ну так это ж другое совсем.
...
Рейтинг: 0 / 0
29.05.2008, 15:21
    #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
29.05.2008, 19:28
    #35344304
ChameLe0n
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Конкурентный доступ к таблице
Ёшкхм... разовью мысль - можно же ещё проще - если выбирать пины не подряд, а случайные :) вероятность того что допустим с хорошим random из 100000 свободных пинов две транзакции выберут один и тот же пин и будут заблокированы - достаточно мала :) имхо.

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

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

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

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

Ок, погорячился... но 50 или 100 - вполне реальная цифра.. Еще идеи есть у кого?
...
Рейтинг: 0 / 0
03.06.2008, 00:55
    #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
03.06.2008, 00:57
    #35350194
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Конкурентный доступ к таблице
ChameLe0n автор1000 конкурентных запросов на изменение одного ресурса - это очень очень очень круто!

Ок, погорячился... но 50 или 100 - вполне реальная цифра.. Еще идеи есть у кого?имхо быстрее чем вариант Dan Black'а с блокировкой не счётчике - не сделать
...
Рейтинг: 0 / 0
03.06.2008, 09:41
    #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
03.06.2008, 09:41
    #35350426
ChameLe0n
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Конкурентный доступ к таблице
Кстати, может в других СУБД есть красивое решение?
...
Рейтинг: 0 / 0
03.06.2008, 10:37
    #35350549
Konstantin~
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Конкурентный доступ к таблице
IMHO нету т.к. проблема не в бд.
...
Рейтинг: 0 / 0
03.06.2008, 14:42
    #35351419
Warstone
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Конкурентный доступ к таблице
Можно развить вариант 2: Есть таблица заказов, куда валятся заказы и есть демон, который выполняет эти заказы, вне зависимости от того, подтвердил или нет пользователь свой заказ... То есть заказ обрабатывается так:
Пользователь сказал "Хочу 5 пинов".
Эта "хотелка" записалась в таблицу предварительных заказов. Попала в карзину пользователя и т.д.
Тут информацтя попала на компьютер пользователя, где он может выбрать ещё что-то или просто нажать на кнопку выписать.

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

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

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

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

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

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


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