MySQL si blocca mentre CREA TABELLA COME SELEZIONA


10

Sto eseguendo la seguente query (fittizia)

CREATE TABLE large_temp_table AS 
    SELECT a.*, b.*, c.* 
    FROM a
    LEFT JOIN b ON a.foo = b.foo
    LEFT JOIN c ON a.bar = c.bar

Supponiamo che l'esecuzione della query richieda 10 minuti. Tentando di aggiornare i valori nelle tabelle a, bec durante l'esecuzione, attenderà il completamento della query precedente. Voglio evitare questo blocco (la coerenza dei dati non è di interesse). Come posso raggiungerlo?

Utilizzo: tabelle MySQL 5.1.41 e InnoDB

ps IMPOSTAZIONE LIVELLO DI ISOLAMENTO DELLE TRANSAZIONI LEGGUTO NON CONSIGLIATO; non produce alcun cambiamento nel comportamento

Aggiornamento Durante l'esecuzione della query, l'output di SHOW ENGINE INNODB STATUS è il seguente (ho fatto una query molto lenta qui allo scopo)

=====================================
120323 15:26:29 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 8 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 1470, signal count 1468
Mutex spin waits 0, rounds 7525, OS waits 112
RW-shared spins 803, OS waits 364; RW-excl spins 1300, OS waits 959
------------
TRANSACTIONS
------------
Trx id counter 0 3145870
Purge done for trx's n:o < 0 3141943 undo n:o < 0 0
History list length 22
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, OS thread id 2958192640
MySQL thread id 7942, query id 69073 localhost root
SHOW ENGINE INNODB STATUS
---TRANSACTION 0 3145869, ACTIVE 20 sec, OS thread id 2955325440, thread declared inside InnoDB 343
mysql tables in use 1, locked 1
6 lock struct(s), heap size 1024, 162 row lock(s)
MySQL thread id 7935, query id 69037 localhost root Copying to tmp table
CREATE TABLE 1_temp_foo AS
                       SELECT SQL_NO_CACHE
                           a.*
                       FROM
                           crm_companies AS a
                       LEFT JOIN users b ON a.zipcode = b.uid
                       LEFT JOIN calc_base_materials c ON a.zipcode = c.material_id
                       LEFT JOIN calc_base_material_langtext d ON a.zipcode = d.material_id
                       LEFT JOIN crm_people e ON a.zipcode = e.telephone1_number
                       ORDER BY a.country, a.name1
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
27579 OS file reads, 613 OS file writes, 392 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 5, seg size 7,
0 inserts, 0 merged recs, 0 merges
Hash table size 34679, node heap has 9 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 1 2030837110
Log flushed up to   1 2030837110
Last checkpoint at  1 2030837110
0 pending log writes, 0 pending chkp writes
231 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 21060366; in additional pool allocated 1048576
Dictionary memory allocated 2897304
Buffer pool size   512
Free buffers       0
Database pages     503
Modified db pages  0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 36022, created 166, written 504
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
1 queries inside InnoDB, 0 queries in queue
1 read views open inside InnoDB
Main thread id 2957578240, state: waiting for server activity
Number of rows inserted 2022, updated 7, deleted 13, read 528536
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 8.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

Aggiornamento 2

Quando si tenta di aggiornare b, c o d mentre la query esegue INNODB STATUS è la seguente:

=====================================
120323 16:12:58 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 27 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 2959, signal count 2957
Mutex spin waits 0, rounds 27587, OS waits 426
RW-shared spins 1321, OS waits 516; RW-excl spins 2578, OS waits 1855
------------
TRANSACTIONS
------------
Trx id counter 0 3145998
Purge done for trx's n:o < 0 3145994 undo n:o < 0 0
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, OS thread id 2958602240
MySQL thread id 7990, query id 69621 localhost root
SHOW INNODB STATUS
---TRANSACTION 0 3145997, ACTIVE 35 sec, OS thread id 2955325440, thread declared inside InnoDB 227
mysql tables in use 1, locked 0
MySQL thread id 7984, query id 69594 localhost root Copying to tmp table
CREATE TABLE 1_temp_foo AS
                       SELECT SQL_NO_CACHE
                           a.*
                       FROM
                           crm_companies AS a
                       LEFT JOIN users b ON a.zipcode = b.uid
                       LEFT JOIN calc_base_materials c ON a.zipcode = c.material_id
                       LEFT JOIN calc_base_material_langtext d ON a.zipcode = d.material_id
                       LEFT JOIN crm_people e ON a.zipcode = e.telephone1_number
                       ORDER BY a.country, a.name1
Trx read view will not see trx with id >= 0 3145998, sees < 0 3145998
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
54447 OS file reads, 1335 OS file writes, 509 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 5, seg size 7,
584 inserts, 584 merged recs, 4 merges
Hash table size 34679, node heap has 1 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 1 2060137545
Log flushed up to   1 2060137545
Last checkpoint at  1 2060137545
0 pending log writes, 0 pending chkp writes
338 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 20799534; in additional pool allocated 1047808
Dictionary memory allocated 2897304
Buffer pool size   512
Free buffers       0
Database pages     511
Modified db pages  0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 70769, created 661, written 3156
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
1 queries inside InnoDB, 0 queries in queue
2 read views open inside InnoDB
Main thread id 2957578240, state: waiting for server activity
Number of rows inserted 2022, updated 66643, deleted 13, read 626517
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 7.59 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

E c'è l'attuale elenco dei processi aperti

Elenco dei processi

Risposte:


10

Vedo questa domanda nel tuo SHOW INNODB STATUS\G

CREATE TABLE 1_temp_foo AS 
                   SELECT SQL_NO_CACHE 
                       a.* 
                   FROM 
                       crm_companies AS a 
                   LEFT JOIN users b ON a.zipcode = b.uid 
                   LEFT JOIN calc_base_materials c ON a.zipcode = c.material_id 
                   LEFT JOIN calc_base_material_langtext d ON a.zipcode = d.material_id 
                   LEFT JOIN crm_people e ON a.zipcode = e.telephone1_number 
                   ORDER BY a.country, a.name1 

Questa query mi dà i brividi perché combina tre cose che potresti non aver pensato:

  • InnoDB è coinvolto in base alla premessa iniziale: Using: MySQL 5.1.41 and InnoDB Tables
  • Anche MyISAM è coinvolta. Perché è coinvolto MyISAM? TUTTE LE TABELLE DI TEMP INTERNE SONO MyISAM !!! Il join risultante è una tabella MyISAM che deve essere convertita in InnoDB quando la tabella temporanea è stata popolata. Qual è il livello di blocco predefinito per le tabelle MyISAM? Blocco a livello di tabella.
  • DDL è coinvolto poiché deve essere creata una tabella appena creata. Quella nuova tabella non si manifesterebbe fino a quando la tabella temporanea non viene popolata, convertita in InnoDB e infine rinominata 1_temp_foo.

C'è un altro effetto collaterale degno di nota. Quando lo fai

CREATE TABLE tblname AS SELECT ...

La tabella risultante non ha indici.

Ho qualcosa che potresti trovare utile per aggirare il problema di blocco. Implica innanzitutto di creare la tabella come query separata, quindi di popolarla. Esistono due opzioni per creare la tabella temporanea:

OPZIONE n. 1 : prova a creare la tabella con lo stesso layout

CREATE TABLE 1_temp_foo LIKE crm_companies;

Ciò creerà la tabella 1_temp_fooper avere gli stessi indici e motore di archiviazione esatti della tabella originale crm_companies.

OPZIONE 2 : provare a creare la tabella solo con lo stesso motore di archiviazione, ma senza indici.

CREATE TABLE 1_temp_foo SELECT * FROM crm_companies WHERE 1=2;
ALTER TABLE 1_temp_foo ENGINE=InnoDB;

Dopo aver creato la tabella (in qualunque modo tu scelga), ora puoi popolare la tabella in questo modo:

INSERT INTO 1_temp_foo
SELECT SQL_NO_CACHE a.*                   
FROM                   
    crm_companies AS a                   
    LEFT JOIN users b ON a.zipcode = b.uid                   
    LEFT JOIN calc_base_materials c ON a.zipcode = c.material_id                   
    LEFT JOIN calc_base_material_langtext d ON a.zipcode = d.material_id                   
    LEFT JOIN crm_people e ON a.zipcode = e.telephone1_number                   
    ORDER BY a.country, a.name
;

Ora, questa query dovrebbe produrre blocchi a livello di riga per avere i dati disponibili per letture ripetibili. In altre parole, questa è una query transazionale.

AVVERTIMENTO

L'OPZIONE # 2 presenta vantaggi rispetto all'OPZIONE # 1

  • Vantaggio n. 1 : se crm_companies ha vincoli di chiave esterna, OPZIONE n. 1 non è realmente possibile. Dovresti scegliere OPZIONE # 2 per motivi di semplicità.
  • Vantaggio n. 2 : poiché OPZIONE n. 2 crea una tabella senza indici definiti dall'utente, la tabella dovrebbe essere caricata più rapidamente rispetto a se la tabella fosse stata creata tramite OPZIONE n. 1.

2

Oltre a impostare il livello di isolamento della transazione su READ COMMITTED (o READ UNCOMMITTED), è necessario che il formato del registro binario sia impostato su MIXED o ROW. La replica basata su STATEMENT blocca questo tipo di istruzione per assicurarsi che tutto sia "sicuro". Puoi anche impostare temporaneamente innodb_locks_unsafe_for_binlog = 1, ma potresti finire con uno slave fuori sincrono in quel modo.

SET binlog_format = ROW;
CREATE TABLE ... SELECT ...

Sfortunatamente questo non funziona neanche :(
clop

1
Qual è l'output di SHOW ENGINE INNODB STATUS quando è in esecuzione l'istruzione CREATE TABLE?
Aaron Brown,

1
Inoltre, assicurarsi di utilizzare READ COMMITTED, non READ UNCOMMITTED. Potresti imbatterti in questo bug che non è stato corretto fino alla 5.1.47 bugs.mysql.com/bug.php?id=48607
Aaron Brown

1
È strano. Ci sono chiavi esterne? Puoi pubblicare INNODB STATUS mentre tenti di aggiornare la riga? Hai provato questo su una versione più recente di MySQL? (suppongo che stai usando 5.1.41 perché viene spedito con la tua distribuzione?)
Aaron Brown

1
Sei sicuro che la tabella degli utenti sia InnoDB? L'aggiornamento non viene visualizzato nell'output SHOW ENGINE INNODB STATUS e nell'elenco dei processi viene visualizzato come bloccato, che di solito è il risultato delle tabelle MyISAM. Sono contento che la risposta di @ RolandoMySQLDBA abbia risolto il tuo problema, ma penso che ci sia qualcos'altro in corso qui.
Aaron Brown,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.