2012-04-20 7 views
6

Sto tentando di eliminare 267 record su circa 40 milioni. La query si presenta come:"Il numero totale di blocchi supera la dimensione della tabella di blocco" Eliminazione di 267 record

delete from pricedata 
where 
pricedate > '20120413' 

pricedate è un campo char(8).

so di regolazione innodb_buffer_pool_size, ma se posso fare

select from pricedata 
where 
pricedate > '20120413' 

e ottenere 267 record (e questo è tutto ci sono), nessun errore, perché lo fa soffocare sul eliminare?

E se la regolazione di innodb_buffer_pool_size non funziona, cosa devo fare?

+2

Ti avere un indice su "prezzo"? – Quassnoi

+0

Ne ho messo uno subito dopo la pubblicazione (e prima della tua risposta, sfortunatamente). Sta ancora costruendo! Per fortuna è un fine settimana ... – davej

risposta

2

Che cosa ha funzionato: cambiare innodb_buffer_pool_size per 256M (vedi commenti sotto commento originale di Quassnoi).

7

Sembra che non si dispone di un indice su pricedate (o MySQL non utilizza questo indice per qualche motivo).

Con REPEATABLE READ (il livello di isolamento di default), InnoDB posti blocchi condivisi sui registri letti e filtrati dalla query e sembra non si dispone di spazio sufficiente per 40M serrature.

Per aggirare il problema utilizzare una qualsiasi di queste soluzioni:

  1. creare l'indice sulla pricedate se non è lì (può richiedere tempo)

  2. Suddividere la query in blocchi più piccoli:

    DELETE 
    FROM pricedata 
    WHERE pricedate > '20120413' 
         AND id BETWEEN 1 AND 1000000 
    
    DELETE 
    FROM pricedata 
    WHERE pricedate > '20120413' 
         AND id BETWEEN 1000001 AND 2000000 
    

    ecc. (Modificare gli intervalli id secondo necessità). Si noti che ciascuna istruzione deve essere eseguita nella propria transazione (non dimenticare di eseguire il commit dopo ogni istruzione se AUTOCOMMIT è disattivato).

  3. Eseguire la query DELETE con il livello di isolamento della transazione READ COMMITTED. Otterrà i blocchi di sollevamento InnoDB dai record non appena vengono letti. Ciò non funzionerà se si utilizza la modalità di istruzione di accesso binario e non si consente query binlog-non sicure (questa è l'impostazione predefinita).

+0

Eccellente, grazie. Proverò e riferirò. – davej

+0

@davej: su una tabella di quelle dimensioni ci vorranno almeno 40 minuti e se ci sono molte colonne (o lunghe 'TEXT' o colonne binarie) questo richiede ore e giorni. Se non stai usando 'InnoDB 1.1' (o il plugin), ci vorrà ancora di più. – Quassnoi

+0

Grazie ancora per il tuo contributo informativo. Ci sono un totale di 8 campi; "prezzo", è l'unico non numerico. Ci sono, anche, 2 BIGINT (20), quindi non ho idea di come possa farcela. Comunque, devo uscire comunque per qualche ora, quindi lascerò correre. Per fortuna è un test db! – davej

4

(Una risposta in ritardo, ma alwayx bene avere quando le persone trovano questo problema in google)

una soluzione senza dover modificare l'innodb_buffer_pool_size o la creazione di un indice può essere quello di limitare la quantità di righe da cancellare .

Ad esempio, nel tuo caso DELETE from pricedata where pricedata > '20120413' limit 100;. Questo rimuoverà 100 righe e lascerà 167 dietro. Quindi, puoi eseguire nuovamente la stessa query ed eliminarne altri 100. Per gli ultimi 67 è complicato ... quando la quantità di righe rimaste nel database è inferiore al limite specificato, ti ritroverai di nuovo con l'errore relativo al numero di serrature. Probabilmente perché il server cercherà più righe corrispondenti da riempire fino a 100. In questo caso, utilizzare limit 67 per eliminare l'ultima parte. (Naturalmente si può usare limit 267 già in principio così)

E per coloro che amano lo script ... un bel esempio ho usato in uno script bash per ripulire i vecchi dati:

# Count the number of rows left to be deleted 
    QUERY="select count(*) from pricedata where pricedata > '20120413';" 
    AMOUNT=`${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} | tail -1` 
    ERROR=0 
    while [ ${AMOUNT} -gt 0 -a ${ERROR} -eq 0 ] 
    do 
     ${LOGGER} " ${AMOUNT} rows left to delete" 
     if [ ${AMOUNT} -lt 1000 ] 
     then 
     LIMIT=${AMOUNT} 
     else 
     LIMIT=1000 
     fi 
     QUERY="delete low_priority from pricedata where pricedata > '20120413' limit ${LIMIT};" 
     ${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} 
     STATUS=$? 
     if [ ${STATUS} -ne 0 ] 
     then 
     ${LOGGER} "Cleanup failed for ${TABLE}" 
     ERROR=1 
     fi 
     QUERY="select count(*) from pricedata where pricedata > '20120413';" 
     AMOUNT=`${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} | tail -1` 
    done