Mysql
 sql >> база данни >  >> RDS >> Mysql

MySQL:Постоянно се получава изчакване за заключване на метаданните на таблицата

Приетото решение е, за съжаление, грешно . Правилно е, доколкото пише,

Това наистина е (почти със сигурност; вижте по-долу) какво да правя. Но след това предполага,

...и 1398не връзката с ключалката. Как е възможно? 1398 е връзката изчаква за ключалката. Това означава, че все още го няма ключалката и следователно убиването й няма полза. Процесът, задържащ ключалката, ще продължи да държи ключалката и следващия следователно нишката, която се опитва да направи нещо, ще също спрете и въведете „Изчакване на заключване на метаданни“ в надлежния ред.

Нямате гаранция, че процесите, „изчакващи заключване на метаданни“ (WFML) също няма да блокират, но можете да сте сигурни, че убиването само на WFML процеси няма да постигне точно нищо .

Истинската причина е, че друг процес задържа ключалката , и което е по-важно, SHOW FULL PROCESSLIST няма да ви каже директно кой е .

ТоЩЕ да ви кажа дали процесът върши нещо, да. Обикновено работи. Тук процесът, задържащ заключването, не прави нищо , и се скрива сред други нишки, като също така не прави нищо.

В този случай виновникът е почти сигурно процес1396 , който стартира преди процес 1398 и сега е в Sleep състояние и е в продължение на 46 секунди. От 1396 г. очевидно направи всичко, което трябваше да направи (както се доказва от факта, че сега спи и го направи за 46 секунди, що се отнася до MySQL ), нито една нишка, която е заспала преди това, би могла да задържи ключалката (или 1396 също би спряла).

ВАЖНО :ако сте се свързали с MySQL като ограничен потребител, SHOW FULL PROCESSLIST щене покажете всички процеси. Така че заключването може да бъде задържано от процес, който не виждате.

По-добър SHOW PROCESSLIST

SELECT ID, TIME, USER, HOST, DB, COMMAND, STATE, INFO
    FROM INFORMATION_SCHEMA.PROCESSLIST WHERE DB IS NOT NULL
    AND (`INFO` NOT LIKE '%INFORMATION_SCHEMA%' OR INFO IS NULL)
    ORDER BY `DB`, `TIME` DESC

Горното може да бъде настроено да показва само процесите в състояние на SLEEP и така или иначе ще ги сортира по низходящо време, така че е по-лесно да намерите процеса, който виси (обикновено това е Sleep 'инг непосредствено преди тези, които „изчакват заключване на метаданни“).

Важното нещо

Оставете на мира всеки процес „изчакване за заключване на метаданни“. .

Бързо и мръсно решение, наистина не се препоръчва, но бързо

Убийте всички процеси в състояние „Спящ режим“ в същата база данни, които са по-стари от най-старите нишка в състояние "чакане за заключване на метаданни". Ето какво казва Арно Амори би направил:

  • за всяка база данни, която има поне една нишка в WaitingForMetadataLock:
    • най-старата връзка в WFML на тази DB се оказва на Z секунди
    • ВСИЧКИ „Sleep“ нишки в тази DB и по-стари от Z трябва да изчезнат. Започнете с най-пресните, за всеки случай.
    • Ако в тази DB съществува една по-стара и неспяща връзка, тогава може би това е тази, която държи ключалката, но прави нещо . Разбира се, можете да го убиете, но особено ако е АКТУАЛИЗИРАНЕ/ВМЪКВАНЕ/ИЗТРИВАНЕ, правите това на свой собствен риск.

Деветдесет и девет пъти от сто, нишката, която трябва да бъде убита, е най-младата сред тези в състояние на сън, които са по-възрастни отколкото по-старият, който чака заключване на метаданни:

TIME     STATUS
319      Sleep
205      Sleep
 19      Sleep                      <--- one of these two "19"
 19      Sleep                      <--- and probably this one(*)
 15      Waiting for metadata lock  <--- oldest WFML
 15      Waiting for metadata lock
 14      Waiting for metadata lock

(*) поръчката TIME всъщност има милисекунди, или поне така ми казаха, просто не ги показва. Така че докато и двата процеса имат времева стойност 19, най-ниският трябва да е по-млад.

По-фокусирана корекция

Стартирайте SHOW ENGINE INNODB STATUS и вижте секцията "ТРАНЗАКЦИЯ". Ще намерите, между другото, нещо като

TRANSACTION 1701, ACTIVE 58 sec;2 lock struct(s), heap size 376, 1 row lock(s), undo log entries 1
MySQL thread id 1396, OS thread handle 0x7fd06d675700, query id 1138 hostname 1.2.3.4 whatever;

Сега проверявате с SHOW FULL PROCESSLIST какво прави идентификатор на нишка 1396 със своята #1701 транзакция. Вероятно е в състояние "Спи". И така:активна транзакция (#1701) с активно заключване, дори е направила някои промени, тъй като има запис в дневника за отмяна... но в момента е неактивен. Това и никой друг не е нишката, която трябва да убиете. Загуба на тези промени.

Не забравяйте, че да не правите нищо в MySQL не означава да не правите нищо като цяло. Ако получите някои записи от MySQL и създадете CSV за FTP качване, по време на FTP качването връзката MySQL е неактивна.

Всъщност, ако процесът, използващ MySQL и MySQL сървърът, са на една и съща машина, тази машина работи с Linux и имате root привилегии, има начин да разберете кой процес има връзката, която е поискала заключването. Това от своя страна позволява да се определи (от използване на процесора или, в най-лошия случай, strace -ff -p pid ) дали този процес е наистина прави нещо или не, за да реши дали е безопасно да се убива.

Защо се случва това?

Виждам това да се случва с уеб приложения, които използват „постоянни“ или „обединени“ MySQL връзки, които в днешно време обикновено спестяват много малко време:екземплярът на webapp е прекратен, но връзката не , така че ключалката му е все още жива... и блокира всички останали.

Друг интересен начин което открих, в хипотезите по-горе, е да се изпълни заявка, връщаща някои редове, и да се извлекат само някои от тях . Ако заявката не е настроена на "автоматично почистване" (както и да го прави основният DBA), тя ще запази връзката отворена и ще предотврати преминаването на пълно заключване на таблицата. Това ми се случи в част от кода, който провери дали ред съществува, като избрах този ред и проверих дали има грешка (не съществува) или не (трябва да съществува), но без реално извличане на реда .

Попитайте DB

Друг начин да намерите виновника, ако имате скорошен MySQL, но не твърде скорошен тъй като това ще бъде оттеглено , е (трябват ви отново привилегии върху информационната схема)

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS 
     WHERE LOCK_TRX_ID IN 
        (SELECT BLOCKING_TRX_ID FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS);

Реално решение, изискващо време и работа

Проблемът обикновено е причинен от тази архитектура:

Когато уеб приложението умре или екземплярът за лека нишка на webapp умре, пулът на контейнера/връзките може да не . И това е контейнерът което поддържа връзката отворена, така че очевидно връзката не се затваря. Съвсем предвидимо, MySQL не счита операцията за завършена .

Ако уеб приложението не почисти след себе си (без ROLLBACK или COMMIT за транзакция, без UNLOCK TABLES и т.н.), тогава каквото и да е започнало да прави това уеб приложение все още е запазено , и все още може да блокира всички останали.

Тогава има две решения. По-лошото е намаляване на времето за изчакване . Но познайте какво се случва, ако изчакате твърде дълго между две заявки (точно:„MySQL сървърът е изчезнал“). След това можете да използвате mysql_ping ако е налице (скоро ще бъде оттеглено. Има заобиколни решения за ЗНП. Или може да проверите за това грешка и отворете отново връзката, ако това се случи (това е начинът на Python). Така че - срещу малка такса за изпълнение - това е изпълнимо.

По-доброто, по-интелигентно решение е по-малко лесно за прилагане. Постарайте се скриптът да бъде чист след себе си, като се уверите, че извличате всички редове или освобождавате всички ресурси за заявка, улавяте всички изключения и се справяте правилно с тях или, ако е възможно, пропуснете напълно постоянните връзки . Оставете всеки екземпляр да създаде своя собствена връзка или използвайте интелигентен водач на басейн (в PHP PDO използвайте PDO::ATTR_PERSISTENT изрично зададен на false ). Като алтернатива (напр. в PHP) можете да накарате манипулаторите за унищожаване и изключения да принудят да изчистят връзката чрез извършване или връщане на транзакции и издаване на изрично отключване на таблицата.

Не знам начин за търсене на съществуващи ресурси за набор от резултати, за да ги освободя; единственият начин би бил да запишете тези ресурси в частен масив.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Възможно ли е да преместите запис от една таблица в друга с помощта на един SQL оператор?

  2. Как да получите цяло число от MySQL като цяло число в PHP?

  3. Как да направя анкета за база данни в реално време в MySQL/PHP?

  4. Получаване на HTML отговор вместо JSON в android

  5. MYSQL се различава по изход от скрипта