Hier zijn mijn aantekeningen van het werken met MySQL-ondersteuning aan een recent, vreemd vergrendelingsprobleem (versie 5.1.37):
Alle rijen en indexitems die worden doorlopen om bij de rijen te komen die worden gewijzigd, worden vergrendeld. Het wordt behandeld op:
http://dev.mysql.com/doc /refman/5.1/en/innodb-locks-set.html
"Een locking read, een UPDATE of een DELETE zet in het algemeen recordvergrendelingen in op elk indexrecord dat wordt gescand bij de verwerking van de SQL-instructie. Het maakt niet uit of er WHERE-voorwaarden in de instructie zijn die de rij zouden uitsluiten. InnoDB doet dat wel weet de exacte WHERE-voorwaarde niet, maar weet alleen welke indexbereiken zijn gescand. ... Als u geen indexen heeft die geschikt zijn voor uw instructie en MySQL de hele tabel moet scannen om de instructie te verwerken, wordt elke rij van de tabel vergrendeld, wat in turn blokkeert alle invoegingen van andere gebruikers aan de tafel."
Het is. Een tijdelijke oplossing die vaak nuttig is, is om te doen:
UPDATE welke tabel dan ook naar iets waar de primaire sleutel in is (selecteer de primaire sleutel uit de tabel waar beperkingen worden gerangschikt op primaire sleutel);
De inner select hoeft geen sloten te nemen en de update heeft dan minder werk voor de update. De order by-clausule zorgt ervoor dat de update wordt uitgevoerd in de volgorde van de primaire sleutel die overeenkomt met de fysieke volgorde van InnoDB, de snelste manier om dit te doen.
Als het om grote aantallen rijen gaat, zoals in uw geval, kan het beter zijn om het geselecteerde resultaat op te slaan in een tijdelijke tabel met een toegevoegde vlagkolom. Selecteer vervolgens uit de tijdelijke tabel waar de vlag niet is ingesteld om elke batch te krijgen. Voer updates uit met een limiet van bijvoorbeeld 1000 of 10000 en stel de vlag in voor de batch na de update. De limieten houden de hoeveelheid vergrendeling op een aanvaardbaar niveau, terwijl het geselecteerde werk slechts één keer hoeft te worden gedaan. Beloof na elke batch om de sloten te ontgrendelen.
U kunt dit werk ook versnellen door een selecte som van een niet-geïndexeerde kolom uit te voeren voordat u elke batch updates uitvoert. Hierdoor worden de gegevenspagina's in de bufferpool geladen zonder vergrendelingen te gebruiken. Dan zal de vergrendeling korter duren omdat er geen schijflezingen zullen zijn.
Dit is niet altijd praktisch, maar als dat zo is, kan het erg nuttig zijn. Als je het niet in batches kunt doen, kun je in ieder geval eerst proberen om de gegevens vooraf te laden, als het klein genoeg is om in de bufferpool te passen.
Gebruik indien mogelijk de LEES COMMITTED transactie-isolatiemodus. Zie:
http://dev.mysql.com/doc/refman /5.1/nl/set-transactie.html
Om die verminderde vergrendeling te krijgen, is het gebruik van op rijen gebaseerde binaire logboekregistratie vereist (in plaats van de standaard binaire logboekregistratie op basis van instructies).
Twee bekende problemen:
-
Subquery's kunnen soms minder dan ideaal zijn geoptimaliseerd. In dit geval was het een onwenselijke afhankelijke subquery - de suggestie die ik deed om een subquery te gebruiken bleek daardoor niet te helpen vergeleken met het alternatief in dit geval.
-
Verwijderingen en updates hebben niet hetzelfde bereik van queryplannen als select-statements, dus soms is het moeilijk om ze goed te optimaliseren zonder de resultaten te meten om uit te zoeken wat ze precies doen.
Beide verbeteren geleidelijk. Deze bug is een voorbeeld waarbij we zojuist de optimalisaties die beschikbaar zijn voor een update hebben verbeterd, hoewel de wijzigingen aanzienlijk zijn en het nog steeds door QA gaat om er zeker van te zijn dat het geen grote nadelige effecten heeft:
http://bugs.mysql.com/bug.php?id=36569