|
|
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Здравствуйте. Есть такая задача: пополнение баланса пользователя. По запросу на пополнение баланса выполняется простой алгоритм: Ищем в таблице балансов запись, которая принадлежит пользователю. Если у пользователя есть баланс, то такая запись присутствует, если баланса нет - записи тоже нет. Если приходит одновременно несколько запросов на пополнение баланса одному юзеру, у которого нет записи (нулевой баланс), то случается deadlock. Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. id - не является автоинкрементом, на самом деле это USER_ID Пробовал INSERT ... ON ON DUPLICATE KEY UPDATE - также не решает проблемы. Как решить проблему? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 12:38:10 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000, Дай определение таблицы balances. В частности, интересно какие там индексы и какой движок используется. (storage engine). ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:16:45 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000 Код: sql 1. Решение напрашивается само собой - сразу создавать для нового пользователя запись с нулевым балансом. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:18:02 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
MasterZiv, Код: sql 1. 2. 3. 4. 5. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:37:35 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
tanglirРешение напрашивается само собой - сразу создавать для нового пользователя запись с нулевым балансом. Не хотелось бы... юзеров будет очень много, поэтому решили вынести баланс в отдельную таблицу, чтобы работа с балансом была максимально быстрой ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:40:11 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000MasterZiv, Код: sql 1. 2. 3. 4. 5. Так просто ? Два поля ? Не верю... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:41:32 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000tanglirРешение напрашивается само собой - сразу создавать для нового пользователя запись с нулевым балансом. Не хотелось бы... юзеров будет очень много, поэтому решили вынести баланс в отдельную таблицу, чтобы работа с балансом была максимально быстрой Это дурость. Не в смысле вынесения, а в смысле надежды, что станет быстрее. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:42:24 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
MasterZivТак просто ? Два поля ? Не верю...Вытащили 2 поля из основной таблицы юзеров в отдельную. Видимо, во многих полях в исходной таблице был нулл/0, и надеялись, что за счёт отсутствия их в новой таблице работа ускорится. Но MasterZivЭто дурость. Не в смысле вынесения, а в смысле надежды, что станет быстрее. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:45:07 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
тут еще интереснее стало... даже если приходят запрос на пополнение баланса от разных юзеров. все равно вываливается deadlock Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. Мне кажется, это связано с блокировкой следующего ключа, но пока я плаваю в этой теме... В эту сторону копать? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:48:56 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
MasterZiv, ну это просто для того чтоб легче примеры читались. ситуация воспроизводится при такой структуре. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:50:49 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
tanglirсразу создавать для нового пользователя запись с нулевым балансом.+1 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:51:29 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Давайте рассмотрим эту проблему не с точки зрения архитектуры, а с точки зрения решения самой проблемы. Структура таблицы есть. Воспроизвести проблему на локале - дело 1 минуты, достаточно открыть 2 терминала. Deadlock есть. Как от него избавится? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:55:31 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Ну или ужесточите уровень изоляции. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 13:58:20 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Akina, вот кстати копаю в этом направлении )) только не ужесточить, а наоборот понизить помогло. Код: sql 1. тогда такой запрос работает как надо. Код: sql 1. но это еще только первые вести с полей... возможно будут побочные эффекты ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 14:05:53 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
А был какой? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 14:51:50 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000тут еще интереснее стало... даже если приходят запрос на пополнение баланса от разных юзеров. все равно вываливается deadlock Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. Мне кажется, это связано с блокировкой следующего ключа, но пока я плаваю в этой теме... В эту сторону копать? Странно, такого вроде бы быть не должно... А точно между ними дедлок ? попробуй только одну сессию пустить ... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:10:51 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
AkinaНу или ужесточите уровень изоляции. Куда уж жёсче ... Некуда ужесточать... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:11:25 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Можно попробовать не делать SELECT... FOR UPDATE вообще, делать UPDATE, что-то про UPDATE-илось -- ок, хорошо, нет -- вставлять. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:13:06 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
AkinaА был какой? был по дефолту REPEATABLE-READ ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:17:13 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
MasterZivпопробуй только одну сессию пустить ... по отдельности отрабатывают нормально ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:18:39 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
MasterZivМожно попробовать не делать SELECT... FOR UPDATE вообще, делать UPDATE, что-то про UPDATE-илось -- ок, хорошо, нет -- вставлять. Без FOR UPDATE нельзя. Это редкий случай - когда пришел запрос, а баланса у юзера нету. Чаще происходит ситуации, когда у юзера есть баланс. Если в начале транзакции будет просто SELECT ... WHERE id=1, а в конце UPDATE ... SET balance = balance+10 WHERE id=1 То при паралельных запросах один из них потеряется ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:25:48 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
insert on duplicate key update ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:26:16 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
ScareCrowinsert on duplicate key update а еще можно прочесть все сообщения в теме. уже обсуждалось.... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:28:25 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
ScareCrowinsert on duplicate key update На самом деле это пока наилучший вариант, но требуется понизить уровень изоляции. Это пока смущает... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:31:50 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
MasterZivСтранно, такого вроде бы быть не должно... А точно между ними дедлок ?я ради интереса на пустой таблице (создал по ddl ТСа) попробовал - воспроизводится на пустой! таблице что оно там ухитряется залочить?.. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.12.2013, 15:40:19 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Вобщем сделал понижение уровня изоляции транзакции до READ COMMITTED с использованием INSERT ON DUPLICATE KEY UPDATE. И вот как чувствовал, что что-то не то... На локале все потестил, все нормально, со всех ракурсов. На бой вынес и повалились ошибки Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engi ne limited to row-based logging. InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED. Так что проблема все еще актуальна... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 13:44:01 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000, как-то странно у вас. Огласите полный набор действий от начала и до конца транзакции, которые приводят к блокировкам в случае insert ... on duplicate... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 16:33:13 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Arhat109Огласите полный набор действий от начала и до конца транзакции, которые приводят к блокировкам в случае insert ... on duplicate... Я так проверял Oleg8000 Код: sql 1. 2. 3. 4. 5. Потом на пустой таблице Oleg8000 Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. результат аналогичен - дедлок. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 16:53:49 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 17:14:50 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
просто отсортируй инсерты по ID сначала 5 потом 10 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 17:16:40 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
http://dev.mysql.com/doc/refman/5.5/en/innodb-record-level-locks.html http://dba.stackexchange.com/a/28289 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 17:18:37 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
tanglir, А теперь попробуйте мне внятно объяснить зачем ПЕРЕД insert делаете блокировку через select for update? Ссылки, привели верные. Ежели табличка пуста, то первая сессия своим селектом сразу же захватывает gap-ом всю табличку, и следующая сессия своим селектом делает тоже самое, что и приводит к нормальному дедлоку. Я понимаю, когда делают для последующего update - лочить конкретный набор строк, дабы по ним не пролетел ещё кто-то. А вот для инсерта, да ещё и с дубликатным апдейтом - нифига не понял. Потому что: 1. Если инсерт втыкает новую запись - он это сделает. 2. Если НЕ втыкает, то пройдет апдейт ровно одной записи. Насколько понимаю, втыкаем по одной записи... (впрочем он тут и для апдейта нафиг не нужен). ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 20:07:22 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Arhat109, select for update нужен для того чтобы заблочить запись, если таковая имеется, что чаще всего и случается. а вот если ее нет, тогда нужно добавить новую строку ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 20:15:39 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000, (*facepalm*)... это понятно. А сможете внятно объяснить неоходимость именно предварительного лока в вашем конкретном случае? :) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 20:20:07 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Arhat109, там интересен совсем другой вопрос: каким способом второй селект для апдейта ваще проходит? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 20:25:23 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Arhat109, Селектом в начале транзакции, мы ищем баланс юзера. Если баланс юзера есть, значит в таблице есть запись. Мы ее блочим, для того, чтобы проделав некоторые операции с балансом (например, сохранить значение баланса до и после операции. т.е. обычный лог), в конце транзакции увеличить этот самый баланс. Ежели у юзера нет баланса, значит нет записи. Тогда добавляем запись, опять же логгируя значения баланса до и после. Пока вижу 2 пути: 1) после первого select for update, если записи нет, то заканчивать транзакцию, сбрасывая тем самым gap-ы. А дальше извращаться с insert on duplicate key update (пока вижу проблему с логгированием) 2) для юзера сразу заводить запись с нулевым балансом. Это, кажется более правильный путь, но смущают размеры таблицы. Юзеров реально до хрена, политика партии допускает наличие у пользователя неограниченного кол-ва аккаунтов. Из этой таблицы делаются ежеминутные выборки с целью списания абонентской платы. Сейчас объем таблицы под 10 млн. записей + ежесуточный прирост в 50-100 тыс. Поэтому и было решено вести балансы только активных юзеров. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 21:01:49 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Arhat109Arhat109, там интересен совсем другой вопрос: каким способом второй селект для апдейта ваще проходит? Вы можете попробовать самостоятельно. tanglir чуть выше описал полную последовательность операций, которая приводит к дедлоку. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 21:06:53 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
ScareCrowпросто отсортируй инсерты по ID сначала 5 потом 10 К сожалению, это невозможно, т.к. это фактически идентификаторы юзеров. Да и если честно, я не представляю, как это можно сделать в разных потоках. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 21:09:14 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000, Дык и попробовал. Тоже лочится. Вот и не понимаю "почто так". Пока объяснение тока одно: гапы - разные. Тока не понимаю "в чём"? Таблица-то пустая! ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 21:10:52 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000, ну, во-первых: "операции с балансом". Если это "лог", то выборка значения "до" - осмыслена, а вот смысла в логировании "между" чтением и сохранением - собственно для чего нужна блокировка - уже нет. Какая нафиг разница, когда физически запишется лог? У вас есть исходное значение на момент начала транзакции, есть конечное... оттого, что вы лог чирканете "задним числом" (после изменения значения) - плохо? А если "заранее"? А если отдельно вставлять в лог предварительно пустую запись "заранее", а потом одним, многотабличным апдейтом одновременно менять баланс и запись лога? Никак? Я к тому, что если в процессе транзакции нет логики, изменяющей поведение транзакции, то она скорее всего избыточна и вполне достаточно "встроенных" блокировок на каждый запрос. Как-то так. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 21:21:30 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Arhat109ну, во-первых: "операции с балансом". Если это "лог", то выборка значения "до" - осмыслена, а вот смысла в логировании "между" чтением и сохранением - собственно для чего нужна блокировка - уже нет. Какая нафиг разница, когда физически запишется лог? У вас есть исходное значение на момент начала транзакции, есть конечное... оттого, что вы лог чирканете "задним числом" (после изменения значения) - плохо? А если "заранее"? Если мы читаем значение баланса в начале транзакции, и не будем блокировать запись, то к моменту записи в лог это значение может быть уже неактуально, т.к. другая транзакция также в этот же момент времени прочитала значение баланса "до" и изменила баланс. Поэтому, без блокировки записи баланса, в логе запросто могут быть такие записи, при 2-х одновременных запросах user_id баланс до баланс после1 0 101 0 10 А в таблице баланса будет верное значение 20 Arhat109А если отдельно вставлять в лог предварительно пустую запись "заранее", а потом одним, многотабличным апдейтом одновременно менять баланс и запись лога? Никак? Надо подумать, пока плохо понял смысл... голова уже плохо соображает ) Arhat109Я к тому, что если в процессе транзакции нет логики, изменяющей поведение транзакции, то она скорее всего избыточна и вполне достаточно "встроенных" блокировок на каждый запрос. Как-то так. Логгирование - не единственная операция в транзакции. Есть и другие, которые как раз меняют логику, в зависимости от вида совершенного платежа. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.12.2013, 21:48:37 |
|
||
|
Deadlock при добавлении строки
|
|||
|---|---|---|---|
|
#18+
Oleg8000, В таком случае, надо смотреть почему первый select for update не блокирует запись для второго. Скорее всего это связано с разными gap, при отсутствии нужной записи. Тогда ваша мысля про "сначала проверять и если отсутствует - разблокировать", скорее всего верна. Но, в целом, явно наблюдается "косяк", имхо. Если табличка пуста, то gap с 0 по 5 должен блокировать gap с 0 по 10 - всяко. Впрочем и наоборот тоже. То есть данная последовательность (у меня воспроизводится тоже, MySQL 5.1.47) - симптом неверного перехлеста gap при оценке построчной блокировки. Надо заглянуть в код, давно не лазил. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 27.12.2013, 10:45:17 |
|
||
|
|

start [/forum/topic.php?all=1&fid=47&tid=1835481]: |
0ms |
get settings: |
6ms |
get forum list: |
9ms |
check forum access: |
2ms |
check topic access: |
2ms |
track hit: |
24ms |
get topic data: |
7ms |
get forum data: |
2ms |
get page messages: |
40ms |
get tp. blocked users: |
1ms |
| others: | 198ms |
| total: | 291ms |

| 0 / 0 |
