2015-10-11 7 views
5

Il mio sito Web ha più di 20.000.000 di voci, le voci hanno categorie (FK) e tag (M2M). Per quanto riguarda la query, come SELECT id FROM table ORDER BY id LIMIT 1000000, 10, MySQL ha bisogno di scansionare 1000010 righe, ma questo è davvero inaccettabilmente lento (e pks, indici, join ecc. Ecc. Non aiutano molto qui, ancora 1000010 righe). Così sto cercando di accelerare l'impaginazione memorizzando conteggio delle righe e il numero di riga con i trigger come questo:È una cattiva idea conservare il conteggio delle righe e il numero di righe per accelerare l'impaginazione?

DELIMITER // 
CREATE TRIGGER @trigger_name 
AFTER INSERT 
ON entry_table FOR EACH ROW 
BEGIN 
    UPDATE category_table SET row_count = (@rc := row_count + 1) 
    WHERE id = NEW.category_id; 
    NEW.row_number_in_category = @rc; 
END // 

e poi posso semplicemente:

SELECT * 
FROM entry_table 
WHERE row_number_in_category > 10 
ORDER BY row_number_in_category 
LIMIT 10 

(ora solo 10 righe scansionato e quindi seleziona sono fiammeggiante veloce, anche se gli inserti sono più lenti, ma sono rari rispetto a selezionare, quindi è ok)

È un approccio cattivo e ci sono buone alternative?

+2

Mi sembra un'ottimale ottimizzazione; ammesso che tu abbia eliminato tutte le altre cause perf (come gli indici) questo tipo di denormalizzazione è accettabile, anche se consideri di archiviare queste informazioni in una tabella di metadati separata per mantenere "pulito" lo schema principale. – Dai

+1

È una buona idea, ma probabilmente non è necessaria perché, diversamente da Postgresql, mysql gestisce molto bene il conteggio (*) sulle tabelle indicizzate. Vedere la mia risposta qui per maggiori dettagli http://stackoverflow.com/a/33006075/267540 – e4c5

+0

Basta scansionare quelle righe se 'id' non è indicizzato. Stai risolvendo il problema sbagliato. – EJP

risposta

1

Anche se mi piace la soluzione nella domanda. Potrebbe presentare alcuni problemi se i dati in entry_table sono cambiati, magari eliminati o assegnati a categorie diverse nel tempo.

Limita inoltre le modalità di ordinamento dei dati, il metodo presuppone che i dati vengano ordinati solo dall'ordine di inserimento. La copertura di più metodi di ordinamento richiede trigger aggiuntivi e dati di riepilogo.

Un modo alternativo di impaginazione è quello di passare in offset del campo che si sta ordinando/impaginando anziché un offset al parametro limite.

Invece di questo:

SELECT id FROM table ORDER BY id LIMIT 1000000, 10 

fare questo - assumendo in questo scenario che l'ultimo risultato visualizzato aveva un id 1000000.

SELECT id FROM table WHERE id > 1000000 ORDER BY id LIMIT 0, 10 

Tracciando l'offset della impaginazione, questo può essere passati alle query successive per i dati ed evita che le righe di ordinamento del database non facciano mai parte del risultato finale.

Se in realtà volevi solo 10 righe su 20 milioni, potresti andare oltre e indovinare che le prossime 10 righe corrispondenti si verificheranno nei prossimi 1000 risultati complessivi. Forse con qualche logica per ripetere la query con una maggiore indennità se questo non è il caso.

SELECT id FROM table WHERE id BETWEEN 1000000 AND 1001000 ORDER BY id LIMIT 0, 10 

Questo dovrebbe essere significativamente più veloce perché l'ordinamento sarà probabilmente in grado di limitare il risultato in un unico passaggio.

+1

Vale la pena sottolineare che, a meno che gli ID siano tutti contigui e inizia da 1, 'LIMIT 1000000, 10' NON è uguale a' id> 1000000 ... LIMIT 0, 10'. – Arth

+1

@Arth, Grazie, sì è giusto. Sebbene il principio funzioni, le due query nell'esempio potrebbero non restituire gli stessi risultati che dici. Buon chiarimento –