Виправлення “Перевищено час очікування блокування; спробуйте перезапустити транзакцію "для" застряглої "таблиці Mysql?


131

Зі скрипту я надсилав такий запит тисячі разів до моєї локальної бази даних:

update some_table set some_column = some_value

Я забув додати частину де, тому для того самого стовпця було встановлено однакове значення для всіх рядків таблиці, і це було зроблено тисячі разів, а стовпець був індексований, тому відповідний індекс був, ймовірно, оновлений занадто багато разів .

Я помітив, що щось не так, тому що це зайняло занадто багато часу, тому я вбив сценарій. Я навіть перезавантажив комп’ютер з того часу, але щось застрягло в таблиці, тому що прості запити займають дуже багато часу, і коли я намагаюся скинути відповідний індекс, це не вдається з цим повідомленням:

Lock wait timeout exceeded; try restarting transaction

Це таблиця innodb, тому застрягла транзакція, ймовірно, неявна. Як я можу виправити цю таблицю та видалити з неї застряглу транзакцію?


3
Який вихід SHOW FULL PROCESSLIST?
Вольф

Він показує лише команду SHOW FUL PROCESSLIST, нічого іншого. Це локальна база даних про розвиток. На ньому нічого не працює. У командному рядку я отримав повідомлення про помилку 'lock wait ..', коли я спробував скинути індекс звідти.
Том

У цьому випадку ви, ймовірно, створюєте два окремих з'єднання в різних транзакціях, які повинні чекати на інший.
Вольф

Після цього я не створював жодних транзакцій. Я вбив сценарій, перезавантажив машину і зайшов у командний рядок, щоб озирнутися. Ніщо інше не використовувало базу даних, крім клієнта командного рядка mysql, тому щось повинно було застрягти в таблиці.
Том

Відповіді:


143

У мене була подібна проблема і вирішено її, перевіривши потоки, які працюють. Для перегляду запущених потоків використовуйте таку команду в інтерфейсі командного рядка mysql:

SHOW PROCESSLIST;

Він також може бути надісланий з phpMyAdmin, якщо у вас немає доступу до інтерфейсу командного рядка mysql.
Тут відобразиться список потоків із відповідними ідентифікаторами та часом виконання, тож ви можете Вбивати теми, на які потрібне занадто багато часу для виконання. У phpMyAdmin у вас буде кнопка для зупинки потоків за допомогою KILL, якщо ви використовуєте інтерфейс командного рядка, просто використовуйте команду KILL, а потім ідентифікатор потоку, як у наступному прикладі:

KILL 115;

Це припинить з'єднання для відповідної нитки.


36
БРАТИ ДО ВІДОМА! Це згадував хтось на одній із багатьох потоків SO щодо цієї проблеми: Іноді процес, який заблокував таблицю, виявляється сплячим у списку процесів! Я виривав волосся, поки не вбив усіх ниток, які були відкриті у базі даних, сплячи чи ні. Це остаточно розблокувало таблицю і дозволило виконати запит оновлення. Коментолог згадав щось на кшталт "Іноді потік MySQL блокує таблицю, а потім спить, поки він чекає, що трапиться щось, не пов’язане з MySQL".
Ерік Л.

(Я додав свій власний відповідь , щоб конкретизувати цей фрагмент думки тут , на відповідне запитання)
Eric L.

Це мені допомогло. У мене було кілька спальних ниток, створених функцією управління / запиту баз даних PHPStorm.
Еджаз

Чудово! З 5 годин у мене був прихований процес, який я міг ідентифікувати та вбити.
Альдо Парадисо

2
це не рішення більше, ніж обклеювання протоки через інфіковану рану, це рішення. Ви не вирішуєте основної кореневої проблеми.
користувач2914191

55

Ви можете перевірити поточні операції за допомогою

SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`

Ваша транзакція повинна бути однією з перших, оскільки вона є найстарішою у списку. Тепер просто візьміть значення trx_mysql_thread_idі надішліть йому KILLкоманду:

KILL 1234;

Якщо ви не впевнені, яка транзакція є вашою, повторюйте перший запит дуже часто і дивіться, які транзакції зберігаються.


Можливо, вам доведеться скористатися кореневим обліковим записом, щоб запустити цей SQL, щоб побачити, яка транзакція насправді блокує інших користувачів для доступу до таблиці
tom10271

40

Перевірте стан InnoDB на наявність замків

SHOW ENGINE InnoDB STATUS;

Перевірте відкриті таблиці MySQL

SHOW OPEN TABLES WHERE In_use > 0;

Перевірте очікувані транзакції InnoDB

SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`; 

Перевірити залежність блокування - що блокує що

SELECT * FROM `information_schema`.`innodb_locks`;

Вивчивши результати вище, ви повинні мати можливість бачити, що блокує, що.

Першопричина проблеми може бути і у вашому коді - будь ласка, перевірте відповідні функції, особливо на примітки, якщо ви використовуєте JPA як Hibernate.

Наприклад, як описано тут , неправильне використання наступних приміток може спричинити блокування в базі даних:

@Transactional(propagation = Propagation.REQUIRES_NEW) 

4
Дуже дякую! Запуск SELECT * FROM information_schema.innodb_trx t JOIN information_schema.processlist p ON t.trx_mysql_thread_id = p.idвиявив винуватця: заблокуюча нитка прийшла з моєї ІР-адреси ... Я забув закрити консоль налагодження, яку я залишив посеред транзакції ...
Elias Strehle

40

Це почалося зі мною, коли розмір моєї бази даних збільшився, і я робив багато операцій над нею.

Правда, мабуть, є якийсь спосіб оптимізувати або ваші запити, або вашу БД, але спробуйте ці 2 запити для вирішення проблеми.

Виконати це:

SET GLOBAL innodb_lock_wait_timeout = 5000; 

І тоді це:

SET innodb_lock_wait_timeout = 5000; 

2
Посилання на посилання: innodb_lock_wait_timeout
culix

1
У мене дуже зайнята база даних 11 ГБ. Це єдине, що працювало на мене, дякую!
dongemus

Просто не забудьте змінити значення на попередні значення, якщо ви не хочете їх зберігати назавжди.
Рікардо Мартінс

Це означає, що деякі мої користувачі матимуть більш повільний досвід, правда?
Арнольд Роа

9

Встановлюючи з'єднання для транзакції, ви отримуєте замок перед виконанням транзакції. Якщо ви не в змозі придбати замок, то спробуйте десь. Якщо блокування все ще неможливо отримати, викидається помилка перевищення часу очікування блокування. Чому ви не зможете придбати замок, це те, що ви не закриваєте з'єднання. Отже, коли ви намагаєтесь заблокувати замок вдруге, ви не зможете придбати замок, оскільки ваше попереднє з'єднання все ще незамкнено і тримає замок.

Рішення: закрийте з'єднання або setAutoCommit(true)(відповідно до вашого дизайну), щоб звільнити замок.


7

Перезавантажте MySQL, це працює чудово.

АЛЕ будьте обережні, що якщо такий запит застряг, десь є проблема:

  • у вашому запиті (неправильний знак, декартовий продукт, ...)
  • дуже численні записи для редагування
  • складні приєднання або тести (MD5, підрядки, LIKE %...% тощо)
  • проблема структури даних
  • модель зовнішнього ключа (блокування ланцюга / петлі)
  • неправильно додані дані

Як сказав @syedrakib, це працює, але це не є довгоживим рішенням для виробництва.

Остерігайтеся: проведення перезавантаження може вплинути на ваші дані з непослідовним станом.

Також ви можете перевірити, як MySQL обробляє ваш запит за допомогою ключового слова EXPLAIN і побачити, чи можливо щось там, щоб пришвидшити запит (індекси, складні тести, ...).


СПАСИБІ. ви безпосередньо не вирішили мою проблему, але я страждав від блокування столів, і це було так погано. Це єдина публікація в усьому Інтернеті, що приводить мене до ідеї перевірити зовнішні ключі. Тож я дізнався, що ключ первинного ключа був 11, а зовнішній ключ 10. Я не знаю, як це могло статися і чому все працювало раніше.
EscapeNetscape

@EscapeNetscape ласкаво просимо, я радий, що ця маленька відповідь допомогла тобі :)
Бендж

3

Переходимо в mysql.

Тож можна побачити, що завдання все ще працює.

Вбийте певний процес або почекайте, поки процес завершиться.


7
Це вирішує проблему. Але це не може бути рішенням для LIVE виробничого сервера ...... Як ми можемо вивести цю обробку тупикових ситуацій? Або як ми можемо уникнути цього глухого кута?
Ракіб

1
Що ж, так трапляється, що навіть при досконалому добре сформованому та ВДІЙСНО уникнутому запиті mysql, який навіть не пише розробник, а інтерфейс активних записів фреймворку, проблеми з очікуванням блокування очікування БЕЗКОШТОВНО трапляться. Тому я не пишу належний запит на mysql - тут є фактором.
Ракіб

1

Я зіткнувся з тією ж проблемою із заявою "update". Моє рішення було просто виконати операції, доступні в phpMyAdmin для таблиці. Я оптимізував, очистив та дефрагментував таблицю (не в тому порядку). Не потрібно скидати таблицю і відновлювати її з резервного копіювання. :)


1
Це може вирішити проблему. Але робити це кожного разу, коли виникає така помилка, не може бути рішенням для виробничого сервера LIVE ...... Як ми можемо вивести цю обробку тупикових ситуацій? Або як ми можемо уникнути цього глухого кута?
Ракіб

1

У мене було те саме питання. Я думаю, що це була тупикова проблема з SQL. Ви можете просто змусити закрити процес SQL з диспетчера завдань. Якщо це не вдалося виправити, просто перезавантажте комп'ютер. Вам не потрібно скидати таблицю та перезавантажувати дані.


Це вирішує проблему. Але це не може бути рішенням для LIVE виробничого сервера ...... Як ми можемо вивести цю обробку тупикових ситуацій? Або як ми можемо уникнути цього глухого кута?
Ракіб

перезапустити Apache та його служби (або принаймні MySQL), не потрібно перезавантажувати
Amjo

1

У мене виникла ця проблема при спробі видалити певну групу записів (використовуючи MS Access 2007 із з'єднанням ODBC до MySQL на веб-сервері). Як правило, я видаляв би певні записи з MySQL, а потім замінював оновлені записи (каскад видаляє декілька пов'язаних записів; це впорядковує видалення всіх пов'язаних записів для одного видалення запису).

Я спробував запустити через операції, доступні в phpMyAdmin для таблиці (оптимізувати, змити і т. Д.), Але я отримував необхідний дозвіл на помилку RELOAD, коли я намагався промити. Оскільки моя база даних знаходиться на веб-сервері, я не зміг перезапустити базу даних. Відновлення резервної копії не було можливим.

Я спробував запустити запит на видалення для цієї групи записів у доступі до mySQL cPanel в Інтернеті. Отримало те саме повідомлення про помилку

Моє рішення: я використав безкоштовний браузер MySQL Query Browser від Sun (який я раніше встановив на комп’ютері) і запустив там запит на видалення. Це спрацювало відразу, проблема вирішена. Потім мені вдалося ще раз виконати функцію за допомогою скрипту Access, використовуючи ODBC Access для з'єднання MySQL.


-2

Виправлено це.

Переконайтеся, що у запиті немає вставлених типів даних, що не збігаються. У мене виникла проблема, коли я намагався "дані агента браузера користувача", VARCHAR(255)і у мене виникли проблеми з цим блокуванням, але коли я змінив його на TEXT(255)це, виправив його.

Тому, швидше за все, це невідповідність типу даних.


-151

Я вирішив проблему, скинувши таблицю і відновивши її з резервного копіювання.


20
Між іншим, ця відповідь приносить честь бути найвищою оцінкою "Прийнятої" відповіді "Найчастіше"! 🏆
ashleedawg

1
Це також найнижча відповідь загалом на так
Боб Керман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.