URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 7559
[ Назад ]

Исходное сообщение
"PostgreSQL - блокировка таблиц"

Отправлено parad , 25-Авг-08 18:06 
Hello, world!

Имеется таблица, упрощенный вид которой выглядит следующим образом:
    serial  _id       - ID объекта
    boolean _status   - Статус объекта (true - используется, false - не используется)
    *       _object   - Данные объекта.

При подключении нового пользователя ему, на время работы, должен выделятся один из объектов. Сейчас это реализовано как 'SELECT _id ... WHERE _status=FALSE LIMIT 1;' -> 'UPDATE ... SET _status=TRUE WHERE _id=$i;'. Это бажная схема - она будет некорректно работать при одновременных запросах. Возможно ли как-то в один акт получить _id незанятого объекта и заблокировать его, так, чтобы конкурирующие запросы не мешали друг-другу? ПС. PostgreSQL 8.3.3.


Содержание

Сообщения в этом обсуждении
"PostgreSQL - блокировка таблиц"
Отправлено chip , 25-Авг-08 18:24 
UPDATE ... SET _status=TRUE WHERE _id=(SELECT _id ... WHERE _status=FALSE LIMIT 1);

>[оверквотинг удален]
>
>Имеется таблица, упрощенный вид которой выглядит следующим образом:
>    serial  _id      
> - ID объекта
>    boolean _status   - Статус объекта (true
>- используется, false - не используется)
>    *       _object
>  - Данные объекта.
>
>При подключении нового пользователя ему, на время работы, должен выделятся один из объектов. Сейчас это реализовано как 'SELECT _id ... WHERE _status=FALSE LIMIT 1;' -> 'UPDATE ... SET _status=TRUE WHERE _id=$i;'. Это бажная схема - она будет некорректно работать при одновременных запросах. Возможно ли как-то в один акт получить _id незанятого объекта и заблокировать его, так, чтобы конкурирующие запросы не мешали друг-другу? ПС. PostgreSQL 8.3.3.


"PostgreSQL - блокировка таблиц"
Отправлено parad , 25-Авг-08 18:48 
А _id заблокированного объекта теперь как получить?

"PostgreSQL - блокировка таблиц"
Отправлено Аноним , 25-Авг-08 22:52 
>UPDATE ... SET _status=TRUE WHERE _id=(SELECT _id ... WHERE _status=FALSE LIMIT 1);

Это та же самая бажная схема. Если 2 запроса начнут выполнятся одновременно, они выдадут 1 объект 2 раза.

Правильный вариант блокировать таблицу целиком или только строки выборки SELECT FOR UPDATE.

UPDATE RETURNING - позволяет и обновить и получить результат обновления.


"PostgreSQL - блокировка таблиц"
Отправлено parad , 25-Авг-08 23:27 
Почитал про "SELECT FOR UPDATE" - не до конца разрбрался:
    1) Нужно ли select и update делать в одной транзакцией? Можно привести пример (смотрел в гугле - все ссылки ведут на проблемы с savepoint при выполнении select for update - примеров пользования нет)?
    2) Предположим, что заблокированная страка ожидает апдейта, - будет ли она выбираться при параллельном селекте?

"PostgreSQL - блокировка таблиц"
Отправлено parad , 25-Авг-08 23:55 
Всё, разобрался экспериментальным путем - делать нужно в одной транзакции и таблица блокируется полностью до завершения транзакции. Блокировка таблицы - как-то не кашерно и может стать узким местом. Черт, неужели нет способа, чтобы не блокировать таблицу полностью?

"PostgreSQL - блокировка таблиц"
Отправлено Аноним , 26-Авг-08 00:12 
>таблица
>блокируется полностью до завершения транзакции.

Не правда, блокируются только строки из условия where до конца транзакции. если используется select for update. Хотя возможно блокировка целой таблицы будет лучше ))


>Блокировка таблицы - как-то не кашерно
>и может стать узким местом. Черт, неужели нет способа, чтобы не
>блокировать таблицу полностью?

Вот именно, ты сериализуешь запросы, убивая параллельльность. Это и есть способ решения твоей проблемы с параллельностью ;)



"PostgreSQL - блокировка таблиц"
Отправлено parad , 26-Авг-08 00:45 
>
>Не правда, блокируются только строки из условия where до конца транзакции. если
>используется select for update. Хотя возможно блокировка целой таблицы будет лучше
>))
>

Я проверял - открыл две консоли - в одной набрал:
begin;
select t1 from test where t2=false for update;

Потом во второй повторил ввод - на чем консоль повисла, и отвисала только по завершению транзакции в первой консоли.

> Вот именно, ты сериализуешь запросы, убивая параллельльность. Это и есть способ решения твоей проблемы с параллельностью ;)

Гы :) ничего не понял, можно поподробней! :)


"PostgreSQL - блокировка таблиц"
Отправлено Аноним , 26-Авг-08 15:31 
>Я проверял - открыл две консоли - в одной набрал:
>begin;
>select t1 from test where t2=false for update;
>
>Потом во второй повторил ввод - на чем консоль повисла, и отвисала...

блокируются только строки из условия where до конца транзакции. Ты блокировал одни и те же строки (одно условие where). Так и должно быть.

>> Вот именно, ты сериализуешь запросы, убивая параллельльность. Это и есть способ решения твоей проблемы с параллельностью ;)
>
>Гы :) ничего не понял, можно поподробней! :)

Ты хочешь параллельно (конкурентно) раздавать id? Это не возможно. Блокирока, гаранирующая последовательное выполнение выдачи id будет иначе будет выдача одного id двум. Можно сделать её явной, можно неявной.

минимум блокировок (ожидания) -  это открыть курсор со свободными id (не все, хватит 100 штук вполне) пытатся занять (update) их по одному. В большинстве случаев это будет первый полученный (fetch) id, редко второй и совсем редко дальнейшие.


"PostgreSQL - блокировка таблиц"
Отправлено angra , 26-Авг-08 00:28 
Например сначала делаем select но без limit
дальше для каждого id пытаемся сделать UPDATE ... SET _status=TRUE WHERE _id=$i and _status=FALSE
Если обновление успешно, то именно этот id используем в дальнейшем.

"PostgreSQL - блокировка таблиц"
Отправлено parad , 26-Авг-08 00:52 
;) действительно классно, но есть одно - база ~ 340Гб. К сожалению раньше не встречались задачи выходившие за пределы select/update/insert, а уважение к постгре большое. :) Поэтому хочется расковырять эту тему и найти родное (как надо) решение для постгри.

"PostgreSQL - блокировка таблиц"
Отправлено Аноним , 26-Авг-08 15:13 
>;) действительно классно, но есть одно - база ~ 340Гб.

Это не важно.

Можно сделать так.
UPDATE ... SET _status=TRUE WHERE _status=FALSE and _id= (select _id from ... limit 1)

Но этот запрос может не сработать и тогда его надо повторять пока не сработает и пока есть свободные _id ;)

А можно открыть курсор и по одному перебирать fetch пока не получится сменить status.


"PostgreSQL - блокировка таблиц"
Отправлено parad , 26-Авг-08 15:28 
А как-же предыдущий ответ?:
>UPDATE ... SET _status=TRUE WHERE _id=(SELECT _id ... WHERE _status=FALSE LIMIT 1);

Это та же самая бажная схема. Если 2 запроса начнут выполнятся одновременно, они выдадут 1 объект 2 раза.


"PostgreSQL - блокировка таблиц"
Отправлено Аноним , 26-Авг-08 15:34 
>А как-же предыдущий ответ?:
>Это та же самая бажная схема. Если 2 запроса начнут выполнятся одновременно,
>они выдадут 1 объект 2 раза.

where _id=(select...)  !!!AND!!! _status=False


Если её уже заблокировали, второе условие не даст это сделать ещё раз. Будет 0 rows updated.


"PostgreSQL - блокировка таблиц"
Отправлено parad , 26-Авг-08 16:38 
В итоге получил:
UPDATE test SET t2=true WHERE t2=false AND t1=(SELECT t1 FROM test WHERE t2=false LIMIT 1) RETURNING t1;


Все правильно? ))


"PostgreSQL - блокировка таблиц"
Отправлено Аноним , 27-Авг-08 01:34 
>В итоге получил:
>UPDATE test SET t2=true WHERE t2=false AND t1=(SELECT t1 FROM test WHERE
>t2=false LIMIT 1) RETURNING t1;
>
>
>Все правильно? ))

Если обработаешь вариант когда этот запрос ничего тебе не вернёт - то правильно.


"PostgreSQL - блокировка таблиц"
Отправлено parad , 27-Авг-08 15:16 
Почему не вернет?: '... RETURNING t1;'

"PostgreSQL - блокировка таблиц"
Отправлено Аноним , 27-Авг-08 20:35 
>Почему не вернет?

Потому что сначала работает select, а потом update. И между ними статус может поменятся.


"PostgreSQL - блокировка таблиц"
Отправлено Vladimir , 25-Авг-08 18:41 
BEGIN
LOCK TABLE ... IN ACCESS EXCLUSIVE MODE
SELECT _id ... WHERE _status=FALSE LIMIT 1
UPDATE ... SET _status=TRUE WHERE _id=$i
COMMIT