Попередження "Блокування очікування очікування перевищено; спробуйте перезапустити транзакцію », хоча я не використовую транзакцію


267

Я запускаю наступний UPDATEоператор MySQL :

mysql> update customer set account_import_id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

Я не використовую транзакцію, тож чому я отримаю цю помилку? Я навіть спробував перезапустити MySQL-сервер, і це не допомогло.

У таблиці 406 733 рядки.

Відповіді:


211

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

Що відбувається, це те, що інший потік занадто довго тримає блокування запису на деякому записі (ви оновлюєте кожен запис у таблиці!), І ваш потік вичерпується.

Більш детальну інформацію про подію можна переглянути, видавши а

SHOW ENGINE INNODB STATUS

після події (в sqlредакторі). Ідеально робити це на тихій тестовій машині.


1
Чи є спосіб зберегти вихід у файл? Я спробував ПОКАЗУВАТИ ДЖЕРЕЛА ІННОДБА СТАТУС \ G> innodb_stat.txt, але не працює.
янтак

14
З командного рядка: mysql [вставте облікові дані] -e "ПОКАЗУЙТЕ СТАТУС ІННОДБА" GN> INODB_stat.txt
VenerableAgents

якщо багато потоків (або процесів mysql) зайняті, наприклад, деякий запит потребує дуже тривалого часу, тому вам доведеться почекати, processщоб деякі не працювали. Якщо так, ви можете отримати цю помилку. Маю рацію?
zhuguowei

6
Запуск декількох (2+) UPDATE запитів в одному рядку під час однієї транзакції також спричинить цю помилку.
Okneloper

Для тих, хто використовує роз'єм Python MySQL, використовується connection.commit()для здійснення INSERTабо UPDATEщойно ви прострілили.
AER

334

ЯК ЗРОБИТИ РОЗКЛЮЧЕННЯ для замкнутих таблиць у MySQL:

Порушення подібних замків може призвести до неприйняття атомарності в базі даних на операторах sql, які спричинили блокування.

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

1) Введіть MySQL

mysql -u your_user -p

2) Давайте подивимось список заблокованих таблиць

mysql> show open tables where in_use>0;

3) Давайте подивимось список поточних процесів, один із них блокує вашу таблицю

mysql> show processlist;

4) Вбити один із цих процесів

mysql> kill <put_process_id_here>;

14
Це небезпечно і хакі. Правильне рішення - виправити свою програму.
Zenexer

71
Дурниця, це дозволяє скасувати безлад і потім виправити додаток. Якби я міг дати цьому хлопцеві 100 голосів за цю проблему, яку я повинен був виправити ЗАРАЗ.
Лізардкс

7
Я згоден з Lizardx. Це було дуже корисним рішенням у ситуації, коли я не мав честі телефонувати ПОКАЗАТИ ДВИГАТЕЛЯ ІННОДБУ
Травіс Шнебергер

8
Наскільки небезпечним є вбивство тривалого запиту ? Клієнт, що дзвонить, просто отримає помилку.
Xeoncross

7
@EricLeschinski я отримую вашу точку зору, але я повинен запитати , чому в світі ви б використовували неакуратний базу даних , як MySQL на Life-критичної системи?
Xeoncross

96
mysql> set innodb_lock_wait_timeout=100

Query OK, 0 rows affected (0.02 sec)

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 100   |
+--------------------------+-------+

Тепер знову запустіть замок. У вас є 100 секунд часу, щоб видати SHOW ENGINE INNODB STATUS\Gбазу даних і побачити, яка інша транзакція блокує вашу.


8
Ця відповідь не пояснює, чому запитувач отримує свою помилку. Не могли б ви детальніше зупинитися на тому, чому крім того, щоб просто дати відповідь?
Санки

5
+1, хоча це не відповідає на питання безпосередньо, для мене це гарна посилання на вирішення цього питання.
Йосеф Харуш

1
@ArtB dev.mysql.com/doc/innodb/1.1/en/… По суті, ОП отримує помилку, оскільки в таблиці викликався замок, а час, що минув до завершення транзакції, перевищив lock_wait_timeoutзначення
фір

70

Погляньте, чи добре налаштована ваша база даних. Особливо ізоляція транзакцій. Не дуже гарна ідея збільшувати змінну innodb_lock_wait_timeout.

Перевірте рівень ізоляції транзакцій у вашій базі даних у mysql cli:

mysql> SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation, @@session.transaction_isolation;
+-----------------------+-----------------+------------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation  | @@session.tx_isolation |
+-----------------------+-----------------+------------------------+
| REPEATABLE-READ       | REPEATABLE-READ | REPEATABLE-READ        |
+-----------------------+-----------------+------------------------+
1 row in set (0.00 sec)

Ви можете отримати покращення, змінюючи рівень ізоляції, використовуйте оракул, як READ COMMITTED, а не REPEATABLE READ (InnoDB Defaults)

mysql> SET tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> 

Також спробуйте використовувати SELECT FOR UPDATE лише в разі необхідності.


1
Це чудове рішення для блокування питань.
повторний

3
Для мене чудово працює, версія my.cnf - це [mysqld] ізоляція транзакцій = ЧИТАТИ ВІДГОТОВЛЕНО
Бенуа Готьє

Лише зауваження: MySQL 8 перейменував tx_isolationзмінну в transaction_isolation.
xonya

Піклуватися потрібно, щоб знати, у що ви потрапляєте, коли переходите від ізоляції читання до ЧИТАЙТЕ ЗАВЕРШЕНО. У вас можуть виникнути брудні дані, яких ви, можливо, захочете уникати. Вікіпедія Isoloation
huggie

27

Жодне із запропонованих рішень не працювало для мене, але це було.

Щось блокує виконання запиту. Швидше за все, інше оновлення запиту, вставлення або видалення з однієї з таблиць у вашому запиті. Ви повинні з'ясувати, що це:

SHOW PROCESSLIST;

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

KILL {id};

Повторно запустіть свій початковий запит.


Я випадково вбивство всіх процесів, перерахованих із ПОКАЗНИКОМ ПРОЦЕСЛІСТА; Тепер я отримую 500 помилок у phpmyadmin. Це 500 помилок, пов'язаних із вбивством цих процесів? Якщо так, то як я можу його перезапустити.
Дашрат

Я бачу, що ви описуєте, проте вбивство процесу не працює. Команда процесу "вбита", але вона залишається у списку процесів.
Торстен

12

На 100% те, що сказав MarkR. autocommit робить кожну заяву однією операцією з заявою.

SHOW ENGINE INNODB STATUSповинні дати вам декілька підказок щодо причини глухого кута. Добре подивіться і на ваш повільний журнал запитів, щоб побачити, що ще запитує таблицю, і спробуйте видалити все, що робить повний таблицюможна. Блокування рівня рядків працює добре, але не тоді, коли ви намагаєтесь заблокувати всі рядки!


5

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


3
можливо innodb_lock_wait_timeoutвmy.cnf
oblig

1
Я встановив у my.cnf: <br/> innodb_lock_wait_timeout = 120 <br/> Типовим значенням для mysql 5.5 є 50. Після цієї зміни я не зміг побачити цю проблему в своїх тестових одиницях! Це сталося після переходу з прокси-балу на басейн tomcat jdbc. Можливо, через більший час транзакцій з басейном tomcat ?!
Чемпіон

3

Кількість рядків не величезна ... Створіть індекс на account_import_id, якщо це не первинний ключ.

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);

OMG ... це просто врятувало мене. Я по-царському накрутив виробничий БД, скинувши індекс, і це виправили. Дякую тобі.
Нік Готч

2

Якщо ви щойно вбили великий запит, це займе час rollback. Якщо ви подасте інший запит до того, як вбитий запит буде виконано відкат, ви можете отримати помилку тайм-аута блокування. Ось що зі мною трапилось. Рішення було просто трохи почекати.

Деталі:

Я видав DELETE-запит, щоб видалити близько 900 000 з приблизно 1 мільйона рядків.

Я запустив це помилково (видаляє лише 10% рядків): DELETE FROM table WHERE MOD(id,10) = 0

Замість цього (видаляє 90% рядків): DELETE FROM table WHERE MOD(id,10) != 0

Я хотів видалити 90% рядків, а не 10%. Тож я вбив процес у командному рядку MySQL, знаючи, що він відкине всі видалені досі рядки.

Тоді я негайно запустив правильну команду і lock timeout exceededнезабаром отримав помилку. Я зрозумів, що замок насправді може бути rollbackзапитом убитого, який все ще відбувається на задньому плані. Тому я зачекав кілька секунд і повторно провів запит.


1

Переконайтеся, що таблиці баз даних використовують механізм зберігання InnoDB та рівень ізоляції транзакцій, ЯКІТЬ ПРОЧИТАНО.

Ви можете перевірити це за допомогою SELECT @@ GLOBAL.tx_isolation, @@ tx_isolation; на консолі mysql.

Якщо воно не встановлено як "ПРОЧИТАНО-ЗАВАНТАЖЕНО", то його потрібно встановити. Перш ніж встановити, переконайтеся, що у вас є привілеї SUPER в mysql.

Ви можете скористатися допомогою http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html .

Встановивши це, я думаю, що ваша проблема буде вирішена.


Ви також можете перевірити, чи не намагаєтесь ви оновлювати це за два процеси одночасно. Користувачі (@tala) стикалися з подібними повідомленнями про помилки в цьому контексті, можливо, двічі перевірте, що ...


1

Я прийшов з Google і просто хотів додати рішення, яке працювало на мене. Моя проблема полягала в тому, що я намагався видалити записи величезної таблиці, в якій було багато FK в каскаді, тому я отримав таку ж помилку, що і OP.

Я відключив, autocommitа потім він працював, просто додаючи COMMITв кінці речення SQL. Наскільки я зрозумів, це випускає буфер по бітах, а не чекати в кінці команди.

Щоб мати приклад з ОП, це повинно було працювати:

mysql> set autocommit=0;

mysql> update customer set account_import_id = 1; commit;

Не забудьте знову активувати, autocommitякщо ви хочете залишити конфігурацію MySQL, як раніше.

mysql> set autocommit=1;


0

Пізно до партії (як завжди), проте моєю проблемою було те, що я написав поганий SQL (будучи початківцем), і в декількох процесах було заблоковано запис (и) <- не впевнений у відповідній мові. Мені довелося просто: SHOW PROCESSLISTа потім вбити ідентифікатори, використовуючиKILL <id>


0

Таке щось трапилось зі мною, коли я використовував конфігурацію мови php для виходу; в середині транзакції. Тоді ця транзакція "зависає", і вам потрібно вбити процес mysql (описаний вище з listlist;)


0

У моєму випадку я запускав ненормальний запит для виправлення даних. Якщо ви заблокуєте таблиці у своєму запиті, вам не доведеться мати справу з тайм-аутом блокування:

LOCK TABLES `customer` WRITE;
update customer set account_import_id = 1;
UNLOCK TABLES;

Це, мабуть, не є хорошою ідеєю для нормального використання.

Для отримання додаткової інформації див: посібник з посилання MySQL 8.0


0

Я зіткнувся з цим, маючи 2 підключення доктрини DBAL, одне з них, як транзакційних (для важливих журналів), вони призначені для паралельного виконання, не залежно один від одного.

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

Мої інтеграційні тести були завернуті в транзакції для відкату даних після дуже тестового.

beginTransaction()
CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery() // CONFLICT
)
rollBack()

Моє рішення полягало в тому, щоб відключити транзакцію з упаковкою в цих тестах і скинути дані db іншим способом.


-4

Була ця сама помилка, хоча я оновлював лише одну таблицю з одним записом, але після перезавантаження mysql це було вирішено.

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