Возможно за счёт durability?
Сейчас клиенты приходят в много потоков и число запросов обновляется в таблице. Со всем развесистым деревом локов.
Вряд ли получится что-то лучшее, чем READ COMMITTED + unlogged table. Но от одной инструкции процессора (если Вы это имели в виду) это всё очень далеко, разумеется.
Да, я что-то подобное атомарным инструкциям и имел в виду. ReadCommitted тут получается не очень хорош: пришли 10 клиентов, считали из таблицы значение счётчика 99 и пошли делать полезную работу. Затем со всеми необходимыми блокировками выстраиваясь в очередь прибавили по единичке и получили в таблице 109. А положено не больше 100.
> прибавили по единичке и получили в таблице 109. Как и должно быть, нет (99 + 10 = 109)? > А положено не больше 100. Почему?!
Бизнес-требования. С точки зрения уровней изоляции всё верно. Поэтому и ищем что-то и пошустрее и поточнее.
Так а что с остальными 9 клиентами должно случиться-то в этом примере?
Если работать на уровне serializable, то все кроме первого на этапе чтения из таблицы счётчика получили бы 100 и вернули клиенту ошибку
Да. Если это то, что Вам нужно (только нужно быстрее) — Вы занимаетесь реализацией очереди, разве нет? Если так, то с них и надо "сдирать" — см. для начала http://johtopg.blogspot.com/2015/01/queues-queues-they-all-fall-down.html А вообще лучше смотреть на код готовых реализаций — есть немало неочевидных трюков, влияющих на их производительность.
Не очередь, счётчик. Это должно быть проще
Т.е. если счётчик = 15, например, то 10 задач должны использовать эту запись, и по их завершении должно стать 25? (Я не понимаю, в чём именно проблема — в ограничении максимума или предотвращении одновременной обработки?)
В ограничении максимума при приемлемой скорости работы
Хмм... т.е. Вам нужно, чтобы данная запись была в итоге обработана ровно (не больше и не меньше) 100 раз, если это вообще возможно, я правильно понял задачу?
Не больше 100. Меньше можно
Т.е., для примера, счётчик был 95, пришло 7 сессий, 3 откатились по своим внутренним причинам, а ещё одна — потому, что счётчик (с учётом откатившихся транзакций) стал = 100, и это нормальная ситуация (т.е. 95 + 3 реально отработавших → счётчик = 100)?
Ну так https://t.me/pgsql/474680, нет?
Да, интересный вариант. Но надо аккуратно разобраться с кешированием сиквенса в сессии. И по сути это же честная транзакция? Со всеми полагающимися локами?
> Но надо аккуратно разобраться с кешированием сиквенса в сессии. Ну Вы же согласились "терять" значения — казалось бы, какая уже разница? ;) А так-то по умолчанию они не кешируются. Так что значения будут теряться только при ROLLBACK и crash recovery, по идее. > И по сути это же честная транзакция? Что, nextval()? Нет, там locks не удерживаются — это как раз неатомарный счётчик.
Ну, можно сделать таблицу с жэтонами на остаток constraint, и выдавать эти жэтоны через SELECT ... FOR UPDATE SKIP LOCKED. Само изменение -- тожэ записывать в жэтоны (те жэ, или другие) и потом собирать их обратно в итоговый регистр, на который висит constraint.
На самом деле -- он пример не очень показательный привёл. Лучшэ видно -- если, допустим, значение у нас 80, constraint на <100, и 25 рабочих процэссов хотят отработать увеличение +1. В итоге, если сделать по дефолту -- процэсс обновляет регистр, на регистре висит constraint -- то все 25 процэссов будут выполняться последовательно (последние 5 при этом откажутся работать). Логически всё правильно, но отсутствие параллелизма удручает. Хотелось бы, чтобы первые 20 отработали параллельно, а остальные -- или отказались сразу или отказались после их завершэния.
Так кончилось-то вот этим, вроде: https://t.me/pgsql/474690
Обсуждают сегодня