2011-09-13 11 views
7

Sto provando ad elaborare milioni di record dalla mia tabella (la dimensione è di circa 30 GB) e attualmente lo sto facendo usando il paging (mysql 5.1.36). La query che utilizzo nel ciclo for èelaborazione di un gran numero di voci del database con il paging rallenta con il tempo

select blobCol from large_table 
where name= 'someKey' and city= 'otherKey' 
order by name 
LIMIT <pageNumber*pageSize>, <pageSize> 

Questo funziona perfettamente per circa 500K record. Ho una dimensione della pagina di 5000 che sto usando e dopo la pagina 100, le query iniziano a rallentare drasticamente. Le prime ~ 80 pagine vengono estratte in 2-3 secondi, ma dopo circa 130 pagine, ogni pagina impiega circa 30 secondi per essere recuperata, almeno fino alla pagina 200. Una delle mie query ha circa 900 pagine e ciò richiederebbe troppo tempo.

The table structure is (type is MyISAM) 
    name char(11) 
    id int // col1 & col2 is a composite key 
    city varchar(80) // indexed 
    blobCol longblob 

Cosa posso fare per accelerarlo? La spiegare per la query mostra questo

select_type: SIMPLE 
possible_keys: city 
key : city 
type: ref 
key_len: 242 
ref: const 
rows: 4293720 
Extra: using where; using filesort 

In caso aiuta, il my.cnf per il mio assistente (24 GB di RAM, 2 Quad proc anime) ha queste voci

key_buffer_size = 6144M 
    max_connections = 20 
    max_allowed_packet = 32M 
    table_open_cache = 1024 
    sort_buffer_size = 256M 
    read_buffer_size = 128M 
    read_rnd_buffer_size = 512M 
    myisam_sort_buffer_size = 128M 
    thread_cache_size = 16 
    tmp_table_size = 128M 
    max_heap_table_size = 64M 
+0

Vorrei suggerire sguardo in Sospensione (proiezioni) [http://docs.jboss.org/hibernate/ core/3.3/reference/it/html/querycriteria.html # querycriteria-projection]. Anche trovato su SO che non usa le proiezioni http://stackoverflow.com/questions/168084/is-there-a-more-efficient -way-of-making-pagination-in-hibernate-than-executing-se – Shahzeb

+0

Sto eseguendo questo attraverso l'API createSQLQuery, non createQuery che utilizza le entità hiberate. Ho provato a utilizzare i metodi createCriteria e ha causato alla mia JVM l'eliminazione delle eccezioni di memoria poiché conservava tutti i dati in memoria. Questo almeno funziona, anche se è un po 'lento – randomThought

risposta

2

Ecco quello che ho fatto, e ha ridotto il tempo totale di esecuzione di un fattore 10.

Quello che ho capito formare il piano di esecuzione della mia query originale era che stava usando filesort per l'ordinamento tutti i risultati e ignorando il indici. Questo è un po 'uno spreco.

Il mio database di test: 5 record M, dimensione 20 GB. struttura della tabella come nella domanda

Invece di ottenere blobCol direttamente nella prima query, prima ottengo il valore di "nome" per l'inizio di ogni pagina. Esegui questa query a tempo indeterminato finché non restituisce 0 risultati. Ogni volta, aggiungere il risultato a un elenco

SELECT name 
FROM my_table 
where id = <anyId> // I use the id column for partitioning so I need this here 
order by name 
limit <pageSize * pageNumber>, 1 

numero di pagina Sine non è noto in precedenza, iniziare con il valore 0 e mantenere incrementare fino a quando la query restituisce null. Puoi anche fare un conteggio di selezione (*), ma questo potrebbe richiedere molto tempo e non aiuterà a ottimizzare nulla. Ogni query impiegava circa 2 secondi per essere eseguita una volta che il numero di pagina superava ~ 60.

Per me, la dimensione della pagina era 5000, quindi ho ottenuto un elenco di stringhe 'name' nella posizione 0, 5001, 10001, 15001 e così via.Il numero di pagine risultate pari a 1000 e la memorizzazione di un elenco di 1000 risultati in memoria non è costosa.

Ora, scorrere l'elenco ed eseguire questa query

SELECT blobCol 
FROM my_table 
where name >= <pageHeader> 
and name < <nextPageHeader> 
and city="<any string>" 
and id= 1 

Questo verrà eseguito N volte, dove N = dimensione della lista abbia ottenuto in precedenza. Poiché "nome" è il tasto chiave principale e "città" è anche indicizzato, EXPLAIN mostra che questo calcolo viene eseguito in memoria utilizzando l'indice.

Ora, ogni query impiega 1 secondo per l'esecuzione, invece dell'originale 30-40. Quindi, combinando il tempo di pre-elaborazione di 2 secondi per pagina, il tempo totale per pagina è 3-4 secondi anziché 30-40.

Se qualcuno ha una soluzione migliore o se c'è qualcosa di palesemente sbagliato in questo, per favore fatemelo sapere

0

Potete fare la vostra query più esatte quindi il limite è inferiore.

SELECT col1,col2, col4 
FROM large_table 
WHERE col1>"SomeKey" OR 
(col1="SomeKey" AND col2>="OtherKey") 
ORDER BY col1,col2 
LIMIT PageSize 

ma update "SomeKey" e "OtherKey" dopo ogni chiamata del database.

+0

cosa intendi con "il limite è più basso"? e ho bisogno di interrogare i risultati con col3, che è indicizzato, ma non è una delle chiavi primarie. non lo farebbe corrispondere più della stringa desiderata e come è meglio di mettere =? – randomThought

+0

Dove usi LIMIT PageSize invece di LIMIT , perché ogni volta che passi l'ID che hai lasciato su. In questo caso si passano 2 valori perché il commento dice che è una chiave composita a 2 colonne. Suppongo che sia un indice cluster sulla chiave primaria e non l'hash però. Se è un hash devi fare Where City> 'somecity' OR (City = 'somecity' AND col1> 'SomeKey) OR (City =' somecity 'AND col1 =' SomeKey 'AND col2>' OtherKey '). Questo dovrebbe essere molto veloce se si ha la possibilità di passare nuovi valori di 'somecity', 'SomeKey' e 'OtherKey' per ogni pagina. – psr

0

Ho provato lo stesso in passato con un database Oracle 10g e ottenuto lo stesso risultato (la mia tabella aveva 60 milioni di righe). Le prime pagine sono state recuperate rapidamente ma con l'aumentare del numero di pagine, la query è diventata troppo lenta. Non c'è molto che si possa fare con gli indici perché sembrano corretti e non sono sicuro di cosa si può ottenere sintonizzando la configurazione del database. Immagino di avere requisiti diversi, ma l'unica soluzione che ho trovato è stata quella di scaricare i dati nei file. Se si dispone di un set limitato di valori per col1, è possibile eliminare col1 e generare n tabelle, una per ogni valore noto di col1. Se col1 è sconosciuto, allora non conosco la soluzione a questo. È possibile recuperare piccoli set di dati da tabelle molto grandi, ma il recupero di serie di dati di grandi dimensioni richiede molto tempo e l'impaginazione non aiuta affatto. È necessario eseguire il preprocesso eseguendo il dumping sui file o generando altre tabelle per partizionare i dati.

+0

sfortunatamente ogni valore col1 è univoco per questo recordset. la tabella è partizionata sulla colonna int col2 (~ 20 partizioni). in ogni partizione, la colonna col1 è unica. Ho provato questo solo con 1 partizione fino ad ora. Non sono sicuro che cosa accadrà alle prestazioni una volta che altre partizioni inizieranno a essere riempite. – randomThought

+0

mysql ha le ottimizzazioni ORDINA per ma non funzionano se il dove e l'ordine per le chiavi sono diversi, come nel mio caso. – randomThought

+0

Sì, qualunque sia la soluzione che si trova, la chiave è di spendere tempo per la pre-elaborazione in qualche modo. Sembra semplice ma non lo è affatto. – martincho

Problemi correlati