2015-04-28 9 views
5

Ho una tabella con 2 milioni di righe. Ho due indici (stato, genere) e anche (compleanno).Le query MySQL su due diversi indici sono veloci, ma combinate in una query lenta. Perché?

Trovo strano che questa query sta prendendo 3,6 secondi o più DOMANDA N ° 1

SELECT COUNT(*) FROM ts_user_core 
WHERE birthday BETWEEN '1980-01-01' AND '1985-01-01' 
    AND status='ok' AND gender='female'; 

stessi per questo: DOMANDA N ° 2

SELECT COUNT(*) FROM ts_user_core 
WHERE status='ok' AND gender='female' 
    AND birthday between '1980-01-01' AND '1985-01-01'; 

Mentre questa ricerca sta prendendo 0,140 secondi QUERY N ° 3

select count(*) from ts_user_core where (birthday between '1990-01-01' and '2000-01-01'); 

Anche questa query richiede 0,2 secondi DOMANDA N ° 4

select count(*) from ts_user_core where status='ok' and gender='female' 

mi aspetto che la prima query ad essere il modo più veloce, come può essere possibile questo comportamento? Non riesco a gestire così tanto tempo per questa query.

Ecco il risultato di: enter image description here

So che posso aggiungere un nuovo indice con 3 colonne, ma c'è un modo per avere una query più veloce senza l'aggiunta di un indice per ogni clausola WHERE?

Grazie per i vostri consigli

+2

Per '0.140' dove clausola è diversa. Ci possono essere miliardi di righe tra '1980-01-01' e '1985-01-01' e solo una riga tra '1990-01-01' e '2000-01-01' –

+5

Mentre MySQL può utilizzare più indici in una query, ne sceglie una che potrebbe non essere ottimale. [Vedi questo thread, in particolare la risposta accettata.] (Http://stackoverflow.com/questions/12222630/can-mysql-use-multiple-indexes-for-a-single-query) Puoi trarre vantaggio dall'indicizzazione '(stato , genere, compleanno) '. – bishop

+1

La prima e la seconda query sono, infatti, identiche. L'ordine delle condizioni nella clausola WHERE non è rilevante, MySQL li valuta nell'ordine che ritiene più appropriato per ottenere risultati più velocemente. – axiac

risposta

2

c'è un modo per ottimizzare la query senza aggiungere un indice per ogni possibile clausola where?

Sì, un po '. Ma ci vuole una comprensione di come funzionano gli INDICI.

Diamo un'occhiata a tutti gli SELECTs che hai presentato finora.

  1. per costruire l'indice ottimale per un SELECT, iniziamo con tutti gli elementi = constant nella clausola WHERE. Metti quelle colonne in un indice in qualsiasi ordine. Questo ci dà INDEX(status, gender, ...) o INDEX(gender, status, ...), ma nulla decide tra loro (ancora).
  2. add on una gamma o tutti i ORDER BY. Nella tua prima coppia di SELECTs, sarebbe birthday. Ora abbiamo INDEX(status, gender, birthday) o INDEX(gender, status, birthday). Ciascuno di questi è "migliore" per i primi due SELECTs.

Questi indici funzionano piuttosto bene per il numero 4: select count(*) from ts_user_core where status='ok' and gender='female'. Quindi nessun indice extra necessario per questo.

Ora, cerchiamo di lavorare su 3 #: select count(*) from ts_user_core where (birthday between '1990-01-01' and '2000-01-01');

  • Non può utilizzare gli indici che abbiamo finora.
  • INDEX(birthday) è essenzialmente l'unica scelta.

Ora, supponiamo di avere anche ... WHERE status='foo'; (senza gender). Questo ci costringerebbe a scegliere INDEX(status, gender, birthday) invece della variante di esso.

Risultato: 2 buoni indici di gestire tutti i 5 seleziona:

INDEX(status, gender, birthday) 
INDEX(birthday) 

Suggerimento: Se si finisce con più di 5 INDEXes o di un indice con più di 5 colonne in esso, è probabilmente saggio di accorciare alcuni indici. Qui è dove le cose diventano davvero confuse. Se vuoi presentarmi una dozzina di indici 'realistici', ti guiderò attraverso.

Note su altri commenti:

  • Per tempi, eseguire ogni query due volte e prendere la seconda volta - al fine di evitare effetti caching. (Il tuo odori 0.140 odora di cache dell'indice.)
  • Per i tempi, disattivare la cache delle query o utilizzare SQL_NO_CACHE.
  • L'ottimizzatore raramente utilizza due indici in una singola query.
  • Mostraci il EXPLAIN semplice; possiamo aiutarti a leggerlo.
  • Il tempo extra necessario per la scelta tra più INDEX è di solito vale la pena.
  • Se si dispone di INDEX(a,b,c), non è necessario INDEX(a,b).
+0

grazie mille! Può essere un problema avere 10 indici sulla stessa tabella? –

+0

Si _can_ avere 10 indici su una singola tabella. Ma non è necessariamente saggio. Tuttavia, non esiste un modo semplice per decidere se 10 è ok. Vai avanti e fai 10; inizia un'altra domanda se sembra darti problemi. [Questo blog] (http://mysql.rjweb.org/doc.php/index_cookbook_mysql) potrebbe aiutare alcuni. –

1

Nel primo caso, si hanno due indici, e mentre MySQL ottimizzatore leggere la tua ricerca, si deve scoprire quale piano è più ottimale.

Poiché si dispone di due indici, l'ottimizzatore impiega più tempo a decidere quale piano sia più ottimale, in quanto crea più piani di esecuzione possibili.

In secondo luogo, MySQL si posiziona alla prima pagina dell'indice che contiene lo stato 'ok' e legge tutte le pagine mentre il sesso non viene modificato in 'maschile', che è più veloce del primo caso.

Provare a creare un indice con tre colonne dalla clausola WHERE.

+0

c'è un modo per ottimizzare la query senza aggiungere un indice per ogni possibile clausola where? –

1

È più che probabile che mysql stia terminando l'utilizzo dell'indice dopo aver eseguito una scansione dell'intervallo nell'intervallo di date.

eseguire le seguenti query nel client mysql per vedere come si sta usando i vostri indici:

EXPLAIN EXTENDED 
SELECT COUNT(*) FROM ts_user_core 
WHERE birthday BETWEEN '1980-01-01' AND '1985-01-01' 
AND status='ok' AND gender='female'; 

SHOW INDEX IN ts_user_core; 

Sto indovinando che l'indice o la chiave primaria ha compleanno prima di stato e/o di genere nell'indice provocando un intervallo di scansione. Mysql interromperà ogni ulteriore utilizzo dell'indice dopo aver eseguito una scansione dell'intervallo.

In questo caso, è possibile riorganizzare le colonne dell'indice per spostare lo stato e il genere prima della nascita o creare un nuovo indice specifico per questa query con stato e sesso prima della data di nascita.

Prima di riorganizzare un indice esistente, accertarsi tuttavia che nessun'altra query eseguita dal sistema dipenda dall'ordine corrente.

1

La differenza tra No1 e No2 è ridotta ai dati memorizzati memorizzati nella cache.Se avessi guardato i piani di esecuzione, scopriresti che erano esattamente gli stessi.

selezionare conteggio (*) da ts_user_core dove (compleanno tra '1990-01-01' e '2000-01-01');

Con un indice in data di compleanno non guarderà i dati della tabella (e allo stesso modo per stato e genere). Ma MySQL può usare solo un indice per tabella - quindi per una query che utilizza entrambi i predicati, selezionerà l'indice più specifico (mostrato in EXPLAIN) per risolvere il predicato, quindi recupera le righe della tabella corrispondenti (operazione costosa) per risolvere il secondo predicato .

Se si aggiunge un indice con tutte e 3 le colonne, si avrà un indice di copertura per la query composta. In alternativa, aggiungere la chiave primaria (non hai dirci la struttura della tabella, darò per scontato "id") e ...

SELECT COUNT(*) 
FROM ts_user_core bday 
INNER JOIN ts_user_core stamf 
ON bday.id=stamf.id 
WHERE bday.birthday BETWEEN '1980-01-01' AND '1985-01-01' 
AND stamf.status='ok' AND stamf.gender='female'; 

Nota a margine:

status =' ok' e sesso = 'femminili'

colonne che hanno un piccolo insieme di valori possibili e/o dati inclinati (in modo tale che alcuni valori sono molto più frequenti di altri) tendono a non funzionare bene come indici, anche se il le statistiche qui suggeriscono che potrebbe non essere un problema.

+0

Ho provato la tua domanda, dovrebbe funzionare più veloce della mia? Perché l'ho provato e ci sono voluti 19 secondi ... –

+0

Hai cambiato gli indici? – symcbean

+0

mysql può utilizzare più di un indice per tabella per query, a partire dalla versione 5.0 quando ha ottenuto il supporto per l'unione di indici. https://dev.mysql.com/doc/refman/5.0/en/index-merge-optimization.html –

Problemi correlati