sql >> Databáze >  >> RDS >> Mysql

MySQL:Trvale získáván Čekání na zámek metadat tabulky

Přijaté řešení je bohužel špatné . Je to správné, pokud se to říká,

To je skutečně (téměř jistě; viz níže), co dělat. Ale pak to naznačuje,

...a 1398 není spojení se zámkem. Jak by to jen mohlo být? 1398 je spojení čeká pro zámek. To znamená, že zatím nemá zámek, a proto jeho zabití nic nepomůže. Proces přidržení zámku bude stále držet zámek a další vlákno, které se snaží něco udělat, proto také zastavte a zadejte "Čekání na zámek metadat" v řádném pořadí.

Nemáte žádnou záruku, že se nezablokují i ​​procesy „čekající na zámek metadat“ (WFML), ale můžete si být jisti, že zabitím pouze procesů WFML nedosáhnete přesně ničeho .

Skutečnou příčinou je, že jiný proces drží zámek a co je důležitější, SHOW FULL PROCESSLIST neřekne přímo, která to je .

BUDE vám sdělí, zda proces probíhá něco, ano. Obvykle to funguje. Zde proces, který drží zámek, nedělá nic , a skrývá se mezi ostatními vlákny, které také nic nedělají.

V tomto případě je viník téměř jistě proces 1396 , který začal před procesem 1398 a nyní je ve Sleep stavu a trvá 46 sekund. Od roku 1396 jasně udělal vše, co potřeboval (což dokazuje skutečnost, že nyní spí, a to po dobu 46 sekund, pokud jde o MySQL ), žádné vlákno, které předtím usnulo, nemohlo udržet zámek (nebo by se 1396 také zastavilo).

DŮLEŽITÉ :pokud jste se připojili k MySQL jako omezený uživatel, SHOW FULL PROCESSLIST nebude ukázat všechny procesy. Takže zámek může být držen procesem, který nevidíte.

Lepší 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

Výše uvedené lze vyladit tak, aby zobrazovalo pouze procesy ve stavu SLEEP, a každopádně je seřadí podle času sestupně, takže je snazší najít proces, který visí (obvykle je to Sleep 'ing jeden bezprostředně před těmi "čeká na zámek metadat").

Důležitá věc

Nechte jakýkoli proces „čekání na zámek metadat“ na pokoji .

Rychlé a špinavé řešení, ve skutečnosti se nedoporučuje, ale je rychlé

Zabijte všechny procesy ve stavu „Spánek“ ve stejné databázi, které jsou starší než nejstarší vlákno ve stavu „čekání na zámek metadat“. To je to, co Arnaud Amaury by udělal:

  • pro každou databázi, která má alespoň jedno vlákno v WaitingForMetadataLock:
    • nejstarší připojení ve WFML v dané DB je staré Z sekund
    • VŠECHNA vlákna "Sleep" na této DB a starší než Z musí odejít. Pro jistotu začněte těmi nejčerstvějšími.
    • Pokud na této DB existuje jedno starší a nespící připojení, pak možná to je to, které drží zámek, ale něco dělá . Můžete ho samozřejmě zabít, ale zvláště pokud se jedná o AKTUALIZACE/VLOŽENÍ/VYMAZÁNÍ, činíte tak na vlastní nebezpečí.

Devadesát devětkrát ze sta je vlákno, které má být zabito, nejmladší mezi těmi ve stavu spánku, které jsou starší než ten starší, který čeká na zámek metadat:

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

(*) pořadí TIME má ve skutečnosti milisekundy, nebo tak mi bylo řečeno, jen je neukazuje. Takže zatímco oba procesy mají hodnotu času 19, ten nejnižší by měl být mladší.

Přesnější oprava

Spusťte SHOW ENGINE INNODB STATUS a podívejte se do sekce "TRANSAKCE". Najdete mimo jiné něco jako

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;

Nyní zkontrolujte pomocí SHOW FULL PROCESSLIST co dělá vlákno id 1396 se svou transakcí #1701. Je pravděpodobné, že je ve stavu „Spánek“. Takže:aktivní transakce (#1701) s aktivním zámkem, dokonce provedla nějaké změny, protože má položku Undo log... ale momentálně je nečinná. To a žádné jiné není vlákno, které potřebujete zabít. Ztráta těchto změn.

Pamatujte, že nedělat nic v MySQL neznamená nedělat nic obecně. Pokud získáte nějaké záznamy z MySQL a vytvoříte CSV pro FTP upload, během FTP uploadu je připojení MySQL nečinné.

Pokud jsou proces využívající MySQL a server MySQL na stejném počítači, na tomto počítači běží Linux a máte oprávnění root, existuje způsob, jak zjistit, který proces má připojení, které požadovalo zámek. To zase umožňuje určit (z využití CPU nebo v nejhorším případě strace -ff -p pid ), zda je tento proces skutečně dělat něco nebo ne, pomoci rozhodnout, zda je bezpečné zabíjet.

Proč se to děje?

Vidím to u webových aplikací, které používají „trvalá“ nebo „sdružená“ připojení MySQL, která v dnešní době obvykle šetří velmi málo času:instance webové aplikace byla ukončena, ale připojení ne , takže jeho zámek je stále naživu... a blokuje všechny ostatní.

Další zajímavý způsob co jsem našel, je ve výše uvedených hypotézách spustit dotaz vracející některé řádky a načíst pouze některé z nich . Pokud dotaz není nastaven na „automatické čištění“ (jakkoli to základní DBA dělá), ponechá připojení otevřené a zabrání úplnému uzamčení tabulky. Stalo se mi to v kusu kódu, který ověřoval, zda řádek existuje, výběrem tohoto řádku a ověřením, zda došlo k chybě (neexistuje) nebo ne (musí existovat), ale bez skutečného načtení řádku .

Zeptejte se DB

Další způsob, jak zjistit viníka, pokud máte nejnovější MySQL, ale ne příliš aktuální protože toto bude ukončeno , is (opět potřebujete oprávnění k informačnímu schématu)

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

Aktuální řešení vyžadující čas a práci

Problém je obvykle způsoben touto architekturou:

Když webová aplikace zemře nebo instance lehkého vlákna webové aplikace zemře, kontejner/fond připojení nemusí . A je to kontejner která udržuje připojení otevřené, takže se zjevně připojení neuzavírá. Zcela předvídatelně MySQL nepovažuje operaci za dokončenou .

Pokud se webová aplikace po sobě nevyčistila (žádné ROLLBACK nebo COMMIT u transakce žádné UNLOCK TABLES , atd.), pak vše, co tato webová aplikace začala dělat, stále existuje a může stále blokovat všechny ostatní.

Pak existují dvě řešení. Nejhorší je snížit časový limit nečinnosti . Ale hádejte, co se stane, když budete mezi dvěma dotazy čekat příliš dlouho (přesně:„MySQL server odešel“). Poté můžete použít mysql_ping pokud je k dispozici (brzy bude ukončena podpora. Existují zástupná řešení pro CHOP. Nebo můžete zkontrolovat to chyba a znovu otevřete připojení, pokud k tomu dojde (toto je způsob Pythonu). Takže - za malý poplatek za výkon - je to proveditelné.

Lepší a chytřejší řešení je méně jednoduché na implementaci. Snažte se, aby byl skript po sobě čistý, zajistěte načtení všech řádků nebo uvolnění všech zdrojů dotazů, zachycení všech výjimek a správné zacházení s nimi, nebo pokud je to možné, úplně přeskočte trvalá připojení . Nechte každou instanci vytvořit si vlastní připojení nebo použijte chytré bazénový řidič (v PHP PDO použijte PDO::ATTR_PERSISTENT explicitně nastaveno na false ). Alternativně (např. v PHP) můžete nechat destruct a handlery výjimek vynutit vyčištění připojení potvrzením nebo vrácením transakcí a vydáním explicitního odemknutí tabulky.

Nevím o způsobu dotazování na existující zdroje sady výsledků, abych je uvolnil; jediným způsobem by bylo uložit tyto prostředky v soukromém poli.



  1. Funkce Escape pro regulární výraz nebo vzory LIKE

  2. PHP MYSQL - Rozdíl mezi 127.0.0.1 a localhostem

  3. Žádná databáze vybraná chyba v PHP s MySQLi

  4. Jak formátovat čísla jako měnu v SQL Server (T-SQL)