SELECT * FOR UPDATE для Postgres

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Аватара пользователя
vadim525
Сообщения: 47
Зарегистрирован: 2011.11.06, 03:13
Откуда: СПб
Контактная информация:

SELECT * FOR UPDATE для Postgres

Сообщение vadim525 »

Всем привет, подскажите, может кто сталкивался

База данных postgres9.6.
Использую стандартный механизм транзакций
Мне нужно логировать начисления в баланс пользователя, на каждое новое начисление создаётся новая запись

Код: Выделить всё

$transaction = Yii::$app->db->beginTransaction();
try {
	// получаем текущий баланс пользователя
	$sql = 'SELECT balans FROM balance WHERE user_id=1 ORDER BY timestamp DESC LIMIT 1 for update';
	$currentBalance = Yii::$app->db->createCommand($sql )->queryScalar();
	
	$u = new Balance();
	$u->user_id = 1;
	$u->balans = $currentBalance + 100;
	$u->save();
	
	$transaction->commit();
} catch (\Exception  $e) {
	$transaction->rollBack();
}
допустим у пользователя баланс 0
если делать последовательный запрос, то начисления проходят норм, т.е
----------------
id | user_id | balance
1 | 1 | 0
2 | 1 | 100
3 | 1 | 200

но если делать параллельные запросы, допустим с начисление 100, то получается такая картина
id | user_id | balance
1 | 1 | 0
2 | 1 | 100
3 | 1 | 100

Получается что в обеих транзакция он видит текущий баланс 0. Что я делаю не так?
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: SELECT * FOR UPDATE для Postgres

Сообщение unknownby »

vadim525 писал(а): 2021.09.16, 16:10 Что я делаю не так?
Используете параллельные запросы, вместо последовательных.
Аватара пользователя
vadim525
Сообщения: 47
Зарегистрирован: 2011.11.06, 03:13
Откуда: СПб
Контактная информация:

Re: SELECT * FOR UPDATE для Postgres

Сообщение vadim525 »

эммм.., так я и пишу что при парарельных запросах и есть проблема. Или вы про какие запросы?
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: SELECT * FOR UPDATE для Postgres

Сообщение ElisDN »

vadim525 писал(а): 2021.09.16, 16:10 Получается что в обеих транзакция он видит текущий баланс 0. Что я делаю не так?
Всё делаете так. Транзакции по умолчанию так и работают.

Просто вместо хранения баланса balance сохраняйте в строках суммы зачисления amount. А баланс запрашивайте каждый раз как SELECT SUM(amount)
Аватара пользователя
vadim525
Сообщения: 47
Зарегистрирован: 2011.11.06, 03:13
Откуда: СПб
Контактная информация:

Re: SELECT * FOR UPDATE для Postgres

Сообщение vadim525 »

Привет ElisDN, спасибо за ответ.
У юзера может быть много записей таких, со временем SUM начнёт работать долго,

Да и при парарельных запросах будет таже проблема, если будут два списания на 100 монет каждое, а баланс будет всего 100 монет,
то они пройдут оба.
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: SELECT * FOR UPDATE для Postgres

Сообщение unknownby »

vadim525 писал(а): 2021.09.17, 11:05 Привет ElisDN, спасибо за ответ.
У юзера может быть много записей таких, со временем SUM начнёт работать долго,

Да и при парарельных запросах будет таже проблема, если будут два списания на 100 монет каждое, а баланс будет всего 100 монет,
то они пройдут оба.
Если вам принципиально, чтобы вся сумма где-то прописывалась отдельно, то лучше будет вначале сделать как посоветовал ElisDN, а после добавления записи обновляйте таблицу user, где будет храниться текущий счёт.
По итогу у вас получится таблица хранящая все пополнения и в таблице пользователя общая текущая сумма или сделать поля "пополнение" и "текущая сумма". Чтобы в одной строчке видеть всю информацию.
Для параллельных запросов ваш случай не очень подходит. У вас выполняются мелкие операции, которые затрачивают мало ресурсов.
"Обычно наибольший выигрыш можно получить с запросами, обрабатывающими большой объём данных, но возвращающими пользователю всего несколько строк."
Ответить