Mysql
 sql >> Database >  >> RDS >> Mysql

Guida ai manichini per bloccare innodb

Ecco le mie note di lavoro con il supporto MySQL su uno strano problema di blocco recente (versione 5.1.37):

Tutte le righe e le voci di indice attraversate per arrivare alle righe modificate verranno bloccate. È coperto su:

http://dev.mysql.com/doc /refman/5.1/en/innodb-locks-set.html

"Una lettura di blocco, un UPDATE o un DELETE generalmente impostano i blocchi dei record su ogni record di indice che viene scansionato nell'elaborazione dell'istruzione SQL. Non importa se ci sono condizioni WHERE nell'istruzione che escluderebbero la riga. InnoDB lo fa non ricorda l'esatta condizione WHERE, ma sa solo quali intervalli di indici sono stati scansionati. ... Se non hai indici adatti alla tua istruzione e MySQL deve scansionare l'intera tabella per elaborare l'istruzione, ogni riga della tabella viene bloccata, che in turn blocca tutti gli inserimenti di altri utenti sul tavolo."

È. Una soluzione alternativa che è spesso utile è eseguire:

UPDATE qualunque tabella imposta qualsiasi cosa su qualcosa in cui primarykey in (seleziona primarykey da qualunque tabella in cui i vincoli ordinano per primarykey);

La selezione interna non ha bisogno di bloccare e l'aggiornamento avrà quindi meno lavoro da fare per l'aggiornamento. La clausola order by garantisce che l'aggiornamento venga eseguito nell'ordine della chiave primaria in modo che corrisponda all'ordine fisico di InnoDB, il modo più veloce per farlo.

Laddove è coinvolto un numero elevato di righe, come nel tuo caso, può essere meglio archiviare il risultato di selezione in una tabella temporanea con una colonna flag aggiunta. Quindi seleziona dalla tabella temporanea in cui il flag non è impostato per ottenere ogni batch. Esegui gli aggiornamenti con un limite, diciamo, 1000 o 10000 e imposta il flag per il batch dopo l'aggiornamento. I limiti manterranno la quantità di blocco a un livello tollerabile mentre il lavoro selezionato dovrà essere eseguito solo una volta. Impegnarsi dopo ogni batch per rilasciare i blocchi.

Puoi anche accelerare questo lavoro eseguendo una somma selezionata di una colonna non indicizzata prima di eseguire ogni batch di aggiornamenti. Ciò caricherà le pagine di dati nel pool di buffer senza eseguire i blocchi. Quindi il blocco durerà per un intervallo di tempo più breve perché non ci saranno letture del disco.

Questo non è sempre pratico, ma quando lo è può essere molto utile. Se non puoi farlo in batch, puoi almeno provare prima il select per precaricare i dati, se è abbastanza piccolo da entrare nel pool di buffer.

Se possibile, utilizzare la modalità di isolamento della transazione READ COMMITTED. Vedi:

http://dev.mysql.com/doc/refman /5.1/en/set-transaction.html

Per ottenere quel blocco ridotto è necessario l'uso della registrazione binaria basata su riga (piuttosto che la registrazione binaria predefinita basata su istruzioni).

Due problemi noti:

  1. A volte le sottoquery possono essere meno che idealmente ottimizzate. In questo caso si trattava di una sottoquery dipendente indesiderabile:il suggerimento che ho fatto di utilizzare una sottoquery si è rivelato inutile rispetto all'alternativa in questo caso per questo motivo.

  2. Eliminazioni e aggiornamenti non hanno la stessa gamma di piani di query delle istruzioni select, quindi a volte è difficile ottimizzarli correttamente senza misurare i risultati per capire esattamente cosa stanno facendo.

Entrambi stanno gradualmente migliorando. Questo bug è un esempio in cui abbiamo appena migliorato le ottimizzazioni disponibili per un aggiornamento, anche se le modifiche sono significative e stiamo ancora passando il controllo qualità per essere sicuri che non abbia grandi effetti negativi:

http://bugs.mysql.com/bug.php?id=36569