LINUX.ORG.RU

Простой вопрос по mysql

 


1

1

Как атомарно инкриментировать столбец записи и вернуть полученное значение (в один запрос):

Логика такая:

UPDATE tbl_counter SET a=a+1 WHERE record_id=1;
SELECT a FROM tbl_counter WHERE record_id=1;

Желательно без сторов.

★★★

Ответ на: комментарий от ziemin

Кстати. Я попробовал типа SELECT a=a+1, @tmp=a, но выскочила ошибка. А вот вариант из гугла:

SET @update_id := 0;
UPDATE some_table SET row = 'value', id = (SELECT @update_id := id)
WHERE some_other_row = 'blah' LIMIT 1; 
SELECT @update_id;

отсюда

ziemin ★★
()

На правах извращения:

update counter,(select sleep(a+1) from counter) x set a=a+1 where id=1;

Во времени исполнения запроса будет закодирован ответ

ihanick
()
Ответ на: комментарий от ziemin

Задача вообщем-то проста, за один запрос без локов, сторов, etc и инкрементировать и вернуть значение

visual ★★★
() автор топика

SELECT по стандарту SQL не может изменять данные. UPDATE по стандарту SQL не может возвращать значений. Иначе был бы хаос. Поэтому решай свою задачу двумя запросам и это будет нормальное решение, работающее с любыми СУБД.

Alve ★★★★★
()
Ответ на: комментарий от visual

ну и что? два клиента выбирают одно и тоже, оба они выполняют update, счётчик увеличится на 2. Как бы всё верно.

Alve ★★★★★
()
Ответ на: комментарий от visual

несколько параллельных клиентов выбирают одно и тоже

ничего из БД не будет взято «параллельно». Запросы выполняются один за другим, каждый запрос - отдельная транзакция. Так что атомарность в твоей задаче имхо не нужна.

Alve ★★★★★
()
Ответ на: комментарий от Alve

да нет, неверно, у каждого должен быть отличный от другого номер

visual ★★★
() автор топика
Ответ на: комментарий от visual

Вторым комментом я вариант предложил. Запрос один (который update) а переменные живут в сессии. Та что вот решение.

Если не понял, то:

-- начало сессии
set @tmp = 0;
-- ... тут ты своими делами занимаешься ...
-- ВОТ ОН: ОДИН ЗАПРОС!
UPDATE tbl_counter SET a=(SELECT @tmp = a + 1) WHERE record_id=1;
-- ... тут ты можешь ещё делами позаниматься (только @tmp не трогай!)
-- ОПА
SELECT @tmp;
ziemin ★★
()
Ответ на: комментарий от Alve

ничего из БД не будет взято «параллельно». Запросы выполняются один за другим, каждый запрос - отдельная транзакция.

То есть вы хотите сказать что очередь выполнения запросов у mysql одна на всех клиентов ? Мол пока select от клиент1 не выполнился, клиент2 подождет ?

TEX ★★★
()
Ответ на: комментарий от TEX

Вполне может быть такой вариант: СУБДшный планировщик запросов вполне может иметь несколько потоков, чтобы принимать несколько запросов по сети вроде как одновременно, да. Т.е. несколько SELECT'ов с точки зрения юзера может быть выполненны якобы параллельно. Но! Во-первых, update точно будет выполнен атомано, один. Во-вторых, СУБД это набор файлов, грубо говоря - база ОДНА. И накопитель, на котором она лежат, всё равно прочтёт инфу для одного клиента, а потом для второго. И отдаст только сначала одну инфу, потом другую. Собственно, так всё «многозадачность» и работает.

Alve ★★★★★
()

в мускуле не знаю, но в db2 есть специальные виды таблиц, OLD TABLE, NEW TABLE, FINAL TABLE.

SELECT a FROM NEW TABLE
(UPDATE tbl_counter SET a=a+1 WHERE record_id=1)
WHERE record_id=1

может и в этом вашем мускуле что-нить подобное

vvviperrr ★★★★★
()

мускул - нинужен! очевидно же! а серьёзно - странная постановка задачи, зачем такое нужно - придумать не могу?

mumpster ★★★★★
()
Ответ на: комментарий от ziemin

И query в два раза становится медленней, чем если сделать это двумя запросами. Уже обсасывали тысячу раз, что запросы в запросе тормозят безбожно.

ihappy
()

postgresql

UPDATE tbl_counter SET a=a+1 WHERE record_id=1 RETURNING a;

anthill
()
Ответ на: комментарий от Alve

И накопитель, на котором она лежат, всё равно прочтёт инфу для одного клиента, а потом для второго.

Вы забываете про кеш, как FS так и самой СУБД

И отдаст только сначала одну инфу, потом другую.

Означает ли это что второй запрос не начнет получать данные до тех пор пока первый не получить свои полностью ? Нет не означает.

Собственно, так всё «многозадачность» и работает.

Если утрировать, то процессор тоже всего лишь быстро быстро переключается между потоками. Но железные SMP системы даже с этим не согласятся.

TEX ★★★
()
Ответ на: комментарий от visual

за один запрос без локов

SELECT FOR UPDATE? Второй клиент встанет до коммита первой транзакции.

то что нужно

С учетом того, что SELECT FOR UPDATE - это лок, то ситуация выглядит как (MIN_INT / -1)

GateKeeper ★★
()
Ответ на: комментарий от TEX

если уровень изоляции транзакций по-умолчанию (repeatable read), то каждая транзация должна работать не зависимо от другой.

Как следствие, если у нас есть две транзакции А и Б

А, модифицирует строчку но ещё не прошёл коммит.

Б, хочет модифицировать ту же самую строчку, но должна ждать пока транзакция A не откатится или не завершится.

Т.о. получается что запросы, модифицирующие одну и туже строчку будут выполняться в «одном» потоке.

Чтение всегда будет снапшота на момент начала транзакции и своих незакоммиченных данных.

Т.е. есть:
a=1
два клиента пытаются читать делать update одной и той же строки
клиент А раньше получил блокировку (мог стартануть позже, нет проблемы), клиент Б ждёт блокировку строки.

Клиент A после update и до commit считает a=2
Клиент Б получит результат select только после того как транзакция клиента А закончится и получит a=3.

ihanick
()
Ответ на: комментарий от ihanick

Т.о. получается что запросы, модифицирующие одну и туже строчку будут выполняться в «одном» потоке.

Оппонент писал про селекты

Т.е. несколько SELECT'ов с точки зрения юзера может быть выполненны якобы параллельно

Вот такое «Якобы»

TEX ★★★
()
Ответ на: комментарий от ihanick

Чтение всегда будет снапшота на момент начала транзакции и своих незакоммиченных данных.

В разных СУБД по разному, в зависимости от степени изоляции. В случае mysql, если мне не изменяет память, даже для разных движков по разному.

TEX ★★★
()
Ответ на: комментарий от TEX

в 99.9% случаев используют либо InnoDB либо MyISAM. В myisam запросы будут выполнены один за другим т.к. в myisam любой DML запрос получает блокировку всей таблицы. Т.к. это не транзакционный движок, то никакой степени изоляции там нет, но если мы делаем одним запросом с подзапросом, то весь этот запрос будет исполнен под блокировкой таблицы.

В случае с innodb read-commited блокировки записи индекса всё равно будут, но только на момент выполнения DML запроса, если требуется считывать правильное значение после выполнения модифицирующего запроса, то тогда и используют SELECT .. FOR UPDATE.

В любом случае на любой базе данных, обеспечивающей атомарность операции в момент a=a+1 должна быть серилизация, т.к. данные должны быть в конечном итоге на диске а разными могут быть в кеше процессора, в памяти и на том же диске.

В случае спецальных средств для хранения счётчиков можно реализовать атомарное увеличение на единицу но и оно всё равно для одной и той же записи может производиться только на одном ядре процессора, но в этом случае есть проблемы с консистентностью записей на диск

Вывод, в вашей любимой РСУБД обновление одной и той же записи в разных потоках происходит с блокировкой. Разница только во времени этих блокировок и дополнительно заблокированных ресурсах.

ihanick
()
Ответ на: комментарий от TEX

Оппонент писал про селекты

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

ihanick
()
Ответ на: комментарий от visual

autoincrement

В смысле, «выдать»? O_o Вообще-то в мускуле искони были autoincrement столбцы в таблицах, по крайней мере, с 3 версии точно имеются... Собственно, они решают примерно те же задачи, что и sequence в обычных базах. А чтобы _гарантированно_ поиметь монотонно возрастающий - надо как тут советовали как минимум lock table делать либо при жёсткой необходимости монотонности вообще специальными средствами эту ситуацию обрабатывать (например, внешней программой в случае мускула т.к. SP нету). Либо я опять чего-то недопонял.:-)

mumpster ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.