users SET balance=(SELECT SUM(amount) FROM transactions WHERE user_id=?)
Я же верно понимаю, что тут 2 запроса и если кто-то удалил параллельно из таблицы транзакций после того как в этом запросе посчиталась сумма, то я сохраню невалидный баланс.
Кроме блокировок можно это решить? Я знаю, что если мы будем запускать перерасчет после каждой операции изменения в таблицы transactions, то получим eventual consistency и баланс так или иначе будет верный. Мне бы понять можно ли просто запросом sql такой кейс решить
Это будет в рамках одной транзакции, поэтому вы получите валидный результат на отметку времени начала транзакции. Если вы после каждого изменения таблицы transactions гарантированно выполняете такой запрос, то и баланс будет актуальный.
А как Вы собираетесь поддерживать в валидном состоянии баланс, если только не из приложения?
Кстати, почему бы не использовать триггеры на таблице transactions, которые будут автоматически обновлять поле balance в таблице users ? В случае триггеров счётчик не должен сбиваться.
Вообще я бы через CTE соединил запросы вставки новых данных в transactions и обновление счётчика, чтобы не усложнять структуру базы.
Я ниразу их не юзал. Я разработчик который постоянно сталкивается с подобными вещами и кроме блокировок мне ничего на ум не приходит. Другие разработчики +- так же отвечают. Решил спросить у тех, кто хорошо в БД шарит
Давай без триггеров вариант с CTE посмотрим. WITH data(user_id, amount) AS ( -- входящие параметры SELECT :user_id, :amount ), insert AS ( INSERT INTO transactions SELECT * FROM data ) UPDATE users SET balance = balance + data.amount FROM data WHERE users.user_id = data.user_id Чем не нравится?
Надо обмозговать)
С триггерами по-сути тоже самое будет, просто часть с update уедет в триггер. Если задача держать поле balance в постоянном соответствии с суммой amount по таблице transactions, то вставка в транзакции и обновление счётчика должны идти в одной транзакции, по очевидным причинам )
Запросы всегда работают атомарно, а Вам тут нужно изоляция. К примеру, на уровне изоляции serializable это делается тривиально. Понятия "после / до" для параллельных транзакций не имеют прямого отношения к consistency, кстати.
В триггерах нет никакой "магии", если Вы об этом. Т.е. если процедура обновления баланса ненадёжна сама по себе, и в триггере она будет работать не лучше.
Она и так атомарная. Баланс в любом случае на какой-то момент будет валидным -- и перестанет быть валидным сразу после этого момента. Более того, время, в течение которого этот баланс был валидным -- можэт быть строго 0, что не отменяет того, что значение поля balance после коммита соответствует балансу на какой-то момент в прошлом. Вопрос как сделать, чтобы этот баланс не менялся -- это другой вопрос. И ключевое здесь -- сколько вы хотите чтобы он не менялся и зачем вам это нужно.
Если Вам важен результат этих транзакций — почитали бы Вы какой-то "фундаментальный" учебник про основные принципы ACID, честное слово. А то потом приходится (лично мне много приходилось) искать косяки в транзакциях и как-то устранять "кашу" в БД после "блокировщиков". ;(
Есть пример? А то тонна инфы в инете, а толку не так много для меня. Я тоже знаю много разработчиков, у которых есть в компании DBA, который потом всем пальчиком показывает, но у меня нет такого спеца.
Пример учебника, в смысле? Я сходу не помню... ну вот "Database system concepts", например. > у которых есть в компании DBA Заниматься такими вещами — вообще не дело DataBase Administrator. Это базовые знания, умения и ответственность разработчика баз данных, если что. ;)
> и изоляция там по умолчанию read_commited. Значит, см. вариант 2. > Меня лишь интересовало как сделать без блокировок сделать правильные цифры. Пути, показанные тут https://t.me/pgsql/346283 и тут https://t.me/pgsql/346286 (если "..." заполнить аналогично первому примеру), на первый взгляд верные. Это если считать, что других (пишущих) транзакций, затрагивающих эту таблицу, в базе не бывает (взаимодействия с ними нужно разбирать отдельно). Собственно, это ещё одна проблема RC в нетривиальных случаях — для добавления ещё одного вида транзакции в худшем случае придётся подумать, как она будет взаимодействовать с N уже существующих. > Вот первая статья Да не читайте Вы эту чушь "статьи" (почти все те, что я в когда-либо видел, содержали грубые ошибки, например) — читайте учебники и документацию.
Обсуждают сегодня