MySQL INSERT в многопользовательской игре SELECT Deadlock

0
  • Версия MySQL: 5.7
  • Двигатель хранения: InnoDB
  • Чтобы поставить вас в ситуацию, это карточная игра (в nodejs). В этом случае 9-игровая настольная игра. И каждый игрок может играть в 4 настольных играх одновременно.

В последнее время я сталкиваюсь с повторяющимися Deadlock, например, с 1 по 5 в минуту на одноранговых пользователей 1K. Нижеприведенный запрос называется средним значением 2 раза в минуту и для каждого пользователя.

  • Пользователь хочет играть.
  • Сервер ALWAYS находит 1-е пустое место (место) в 1-й доступной настольной игре.

Я не знаю, как это решить, поскольку этот запрос построен для обработки возможных условий гонки, когда различные игроки 2+ (или один и тот же игрок 2+ раз) выполняют запрос Insert.

Конечно, я могу управлять условиями гонки уникальными ключами, но неуправляем на пике пользователей 100+, которые ищут свободное место. Это приведет к отказу 91+ путем дублирования ключа.


LATEST DETECTED DEADLOCK
------------------------

*** (1) TRANSACTION:
TRANSACTION 100539324, ACTIVE 0 sec inserting
mysql tables in use 5, locked 5
LOCK WAIT 23 lock struct(s), heap size 3520, 111 row lock(s), undo log entries 1
MySQL thread id 6782, OS thread handle 2460, query id 138188765 localhost 127.0.0.1 root Creating sort index
INSERT INTO app_tables_players (user_id, game_id, seat_number, username, state, folded) SELECT 597, a.id, b.id AS seat_number, 'some_username', 'temp', false FROM (SELECT id FROM app_tables_games WHERE table_id = 6 AND seats_total > seats_taken AND id NOT IN (SELECT game_id FROM app_users_state WHERE user_id = 597)) AS a, 'app_temp_players_9' AS b WHERE b.id NOT IN (SELECT seat_number FROM app_tables_players WHERE game_id = a.id) ORDER BY a.id ASC LIMIT 1

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 75 page no 51 n bits 1120 index seat_number_game_id of table 'nodejs'.'app_tables_players' trx id 100539324 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 964 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008d8f6; asc     ;;
 2: len 4; hex 006b437b; asc  kC{;;

*** (2) TRANSACTION:
TRANSACTION 100539325, ACTIVE 0 sec setting auto-inc lock, thread declared inside InnoDB 4743
mysql tables in use 5, locked 5
22 lock struct(s), heap size 3520, 151 row lock(s)
MySQL thread id 6702, OS thread handle 11428, query id 138188764 localhost 127.0.0.1 root Creating sort index
INSERT INTO app_tables_players (user_id, game_id, seat_number, username, state, folded) SELECT 613, a.id, b.id AS seat_number, 'some_username2', 'tmp', false FROM (SELECT id FROM app_tables_games WHERE table_id = 14 AND seats_total > seats_taken AND id NOT IN (SELECT game_id FROM app_users_state WHERE user_id = 613)) AS a, 'app_temp_players_9' AS b WHERE b.id NOT IN (SELECT seat_number FROM app_tables_players WHERE game_id = a.id) ORDER BY a.id ASC LIMIT 1

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 75 page no 51 n bits 1120 index seat_number_game_id of table 'nodejs'.'app_tables_players' trx id 100539325 lock mode S locks gap before rec
Record lock, heap no 18 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 06; asc  ;;
 1: len 4; hex 0008d6e4; asc     ;;
 2: len 4; hex 006b5a47; asc  kZG;;

Record lock, heap no 97 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008d97e; asc    ~;;
 2: len 4; hex 006b5d1e; asc  k] ;;

Record lock, heap no 160 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 05; asc  ;;
 1: len 4; hex 0008d93e; asc    >;;
 2: len 4; hex 006b4cb3; asc  kL ;;

Record lock, heap no 181 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008d92f; asc    /;;
 2: len 4; hex 006b5c0b; asc  k\ ;;

Record lock, heap no 267 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008d9b2; asc     ;;
 2: len 4; hex 006b5b9a; asc  k[ ;;

Record lock, heap no 272 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 05; asc  ;;
 1: len 4; hex 0008d999; asc     ;;
 2: len 4; hex 006b5d43; asc  k]C;;

Record lock, heap no 307 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008da33; asc    3;;
 2: len 4; hex 006b6182; asc  ka ;;

Record lock, heap no 346 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008da38; asc    8;;
 2: len 4; hex 006b5fbd; asc  k_ ;;

Record lock, heap no 530 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 05; asc  ;;
 1: len 4; hex 0008d9b2; asc     ;;
 2: len 4; hex 006b544e; asc  kTN;;

Record lock, heap no 556 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 05; asc  ;;
 1: len 4; hex 0008d97e; asc    ~;;
 2: len 4; hex 006b62f3; asc  kb ;;

Record lock, heap no 629 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008d999; asc     ;;
 2: len 4; hex 006b5f41; asc  k_A;;

Record lock, heap no 804 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 05; asc  ;;
 1: len 4; hex 0008da33; asc    3;;
 2: len 4; hex 006b60bc; asc  k' ;;

Record lock, heap no 906 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008d93e; asc    >;;
 2: len 4; hex 006b4988; asc  kI ;;

Record lock, heap no 962 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008d928; asc    (;;
 2: len 4; hex 006b5b03; asc  k[ ;;

Record lock, heap no 964 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 1; hex 04; asc  ;;
 1: len 4; hex 0008d8f6; asc     ;;
 2: len 4; hex 006b437b; asc  kC{;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
TABLE LOCK table 'nodejs'.'app_tables_players' trx id 100539325 lock mode AUTO-INC waiting
*** WE ROLL BACK TRANSACTION (2)

Запрос, вызывающий проблему:

INSERT INTO app_tables_players
    (user_id, 
    game_id, 
    seat_number) 
SELECT
    597, 
    a.id, 
    b.id AS seat_number
FROM 
    (SELECT id 
    FROM
        app_tables_games 
    WHERE
        table_id = 14
        AND seats_total > seats_taken 
        AND id NOT IN (
            SELECT game_id 
            FROM   app_users_state 
            WHERE  user_id = 597
        )
    ) AS a, 
    app_temp_players_9 AS b 
WHERE
    b.id NOT IN (
        SELECT seat_number 
        FROM   app_tables_players 
        WHERE  game_id = a.id
    ) 
ORDER BY a.id ASC 
LIMIT 1;

  • Объяснить запрос:

id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra

1, INSERT, app_tables_players,, ALL,, ,, ,, ,

1, PRIMARY, app_tables_games,, ref, table_id,table_id_seats_taken_seats_total, table_id_seats_taken_seats_total, 4, const, 24, 33.33, Using where; Using index; Using temporary; Using filesort

1, PRIMARY, b,, index,, id, 1,, 9, 100.00, Using where; Using index; Using join buffer (Block Nested Loop)

4, DEPENDENT SUBQUERY, app_tables_players,, index_subquery, seat_number_game_id,game_id_user_id,game_id,seat_number,game_id_state, seat_number_game_id, 6, func,func, 2, 100.00, Using where; Using index

3, SUBQUERY, app_users_state,, ref, game_id_user_id,user_id,game_id, user_id, 4, const, 4, 100.00,

ПРИМЕЧАНИЕ. Иногда "Тупик" встречается с запросом выше и:

DELETE FROM app_users_states WHERE game_id = some_id AND user_id = some_id
Теги:
database
deadlock
database-deadlocks

1 ответ

0
Лучший ответ

После еще нескольких исследований о всех тупиках, встречающихся в этом приложении, я увидел, что существует 2-3 разных типа или ситуаций.

Одним из них был lock mode AUTO-INC waiting

Таким образом, первая транзакция блокирует столбец id автоинкремента и ожидает, что индекс "X" и вторая транзакция имеют блокировку индекса "X" и ожидают блокировку AUTO-INC.

Так как мне нужен столбец id чтобы получить lastInsertId и использовать его позже в SELECT, было бы ДОЛЖНО иметь этот столбец автоинкремента. Чтобы решить, что я создал известное значение UUID в своем приложении и сохранен как двоичный (16) вместо autoincrement int id.

Вторая и проблема была связана с INSERT на SELECT в другой или той же таблице в том же запросе. Как (пример);

INSERT INTO tableA some_colX (SELECT some_colY FROM tableA WHERE some_colZ = 1 LIMIT 1)

Этот SELECT использовал lock mode S locks gap before rec но INSERT требует lock_mode X locks gap before rec insert intention waiting. В моем INSERT я делал SELECT в трех разных таблицах, а в другой части приложения были некоторые другие UPDATE или другие INSERT s, поэтому множество тупиков повсюду.

Чтобы решить часть этого, я изменил (некоторые из) SELECT внутри INSERT добавляя FOR UPDATE начиная с этого, устанавливает IX lock вместо S lock, предотвращая, чтобы другой запрос получил сначала X lock in this record/gap

Позже я понял, что в моей заявке был еще один тупик. Что ты думаешь? Другой другой (и последний) INSERT запрос SELECT в других совершенно разных таблицах.

От 1 до 5 тупиков в минуту до 1 тупика за 10 минут и более.

Итак, как мое личное мнение (прецедент), не используйте INSERT и SELECT в том же запросе. Использовать процедуры, транзакции... И проверить, используют ли SELECT надлежащие индексы.

Извините за мой английский :)

Удачи.

ОБНОВИТЬ:

Chganged 1 Index в основной таблице, участвующей в INSERT и 1 Index на одной из таблиц SELECT. Результат: к настоящему времени 0 тупиков за 2 часа

Ещё вопросы

Сообщество Overcoder
Наверх
Меню