MySQL Indexing VarChar


10

Sto cercando di indicizzare il mio blogentriesdatabase per prestazioni migliori ma ho riscontrato un problema.

Ecco la struttura:

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

Una query come la seguente utilizza correttamente l'indice:

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| id | select_type | tabella | digitare | possible_keys | chiave | key_len | rif | righe | Extra |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| 1 | SEMPLICE | blogentries | indice | NULL | PRIMARIO | 114 | NULL | 126 | Utilizzando l'indice |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

Tuttavia, quando aggiungo il comando entry_idnella SELECTquery, utilizza il filesort

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| id | select_type | tabella | digitare | possible_keys | chiave | key_len | rif | righe | Extra |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| 1 | SEMPLICE | blogentries | TUTTO | NULL | NULL | NULL | NULL | 126 | Utilizzo di filesort |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

Mi chiedevo perché questo stesse accadendo e come posso evitarlo? È dovuto al VarChar, e che dovrebbe essere cambiato in qualcos'altro?

Sto cercando di fare in modo che tutte le mie query utilizzino l'indice mentre sto eseguendo valori alti Handler_read_rnde Handler_read_rnd_nextalti.

Se hai bisogno di altre informazioni, posso pubblicarle anche io.


filesort significa che sta eseguendo l'ordinamento sul disco.
Kermit,

Prova ad aggiungere WHERE 1=1alla tua seconda query.
Kermit,

Quale versione di MySQL è questa? Qual è la dimensione del buffer di ordinamento ( SELECT @@sort_buffer_size)?

@njk filesort è il risultato della parte 'ORDER BY' della query

1
@TashPemhiwa Non necessariamente, vedi la prima affermazione.
Kermit,

Risposte:


6

Dato che non hai una WHEREclausola in nessuna delle query, stai restituendo tutte le righe in entrambi i casi, quindi penso che l'uso o il non uso dell'indice avrebbe un impatto minimo sulle prestazioni in questi esempi.


Sicuramente MySQL dovrebbe usare l'indice per il ORDER BY?
Eggyal

@eggyal Non se è troppo grande per la memoria.
Kermit,

@njk: Non ha senso ... può attraversare l'indice, in ordine, senza dover caricare tutto in memoria. I risultati sarebbero ordinati senza la necessità di eseguire filesort.
Eggyal

@eggyal vorrei mettere in discussione la dimensione di varchar(5000).
Kermit,

@njk: Ma quella colonna non è né nell'indice né utilizzata nell'ordinamento.
Eggyal

2

Come documentato in ORDER BYOttimizzazione :

Per query lente per le quali filesortnon viene utilizzato, provare a ridurre max_length_for_sort_dataa un valore appropriato per attivare a filesort.

Nel suo articolo di blog Che cosa è esattamente read_rnd_buffer_size , Peter Zaitsev spiega:

Per me questo significa che da MySQL 4.1 questa opzione viene utilizzata in una ristretta gamma di casi: se si recuperano pochi campi (meno di max_length_for_sort_data ) i dati devono essere archiviati nel buffer di ordinamento e nel file di ordinamento, quindi non sarebbe necessario read_rnd_buffer, se le colonne selezionate sono lunghi, quindi sono più lunghi di max_length_for_sort_data , ciò significa spesso che ci sono alcune colonne TEXT / BLOB tra di loro. Sarebbe usato comunque se c'è un gran numero di colonne o ci sono lunghe colonne VARCHAR usate - bastano solo un paio di UTARCH VARCHAR (255) per creare una riga più lunga di max_length_for_sort_data nella sua presentazione statica.

Ciò suggerisce che si max_length_for_sort_datatratta di un limite alla dimensione totale delle colonne selezionate, al di sopra della quale filesortverrà utilizzato un valore anziché un ordinamento basato su indice.

Nel tuo caso, la selezione entry_id(5002 byte) assume la dimensione totale rispetto al valore predefinito di 1 KiB di questa variabile e quindi filesortviene utilizzata. Per aumentare il limite a 8 KiB, è possibile:

SET SESSION max_length_for_sort_data = 8192;

Ho una tabella con un'impostazione molto simile a questa e questa impostazione non sembra innescare alcun cambiamento nell'uso di filesort.

@muffinista: è interessante. Suppongo che potrebbe essere correlato ad alcune delle altre impostazioni del buffer, per la risposta di @ RolandoMySQLDBA ?
Eggyal

2

Hai ottenuto molte risposte interessanti qui, ma nessuno ha risposto esattamente alla domanda: perché sta succedendo questo? A quanto ho capito, quando una query SELECT contiene dati di lunghezza variabile in MySQL e non esiste un indice che corrisponde a TUTTE le colonne richieste, utilizzerà sempre un fileort. La dimensione dei dati non è terribilmente rilevante qui. È difficile trovare una risposta diretta a questa domanda nella documentazione di MySQL, ma ecco un buon post sul blog in cui qualcuno sta riscontrando un problema molto simile al tuo.

Vedi anche: 10 suggerimenti per l'ottimizzazione delle query MySQL (che non fanno schifo) .

Quindi, se è fattibile avere un indice su entry_id, allora puoi aggiungerlo ed essere tutto pronto. Ma dubito che sia un'opzione, quindi cosa fare?

Se dovresti fare qualcosa al riguardo è una domanda separata. È importante sapere che "filesort" è scarsamente chiamato in MySQL - in realtà è solo il nome dell'algoritmo utilizzato per ordinare questa particolare query e, in molti casi, l'ordinamento avverrà effettivamente in memoria. Se non ti aspetti che questa tabella cresca molto, probabilmente non è un grosso problema.

D'altra parte, se questa tabella contiene un milione di righe, potresti avere un problema. Se è necessario supportare l'impaginazione delle query in questa tabella, è possibile che si verifichino problemi di prestazioni davvero gravi. In tal caso, partizionare i dati di lunghezza variabile in una nuova tabella e fare un JOIN per recuperarli è un'ottimizzazione valida da considerare.

Ecco un paio di altre risposte su SO che parlano di questa domanda:


La prima query dell'OP " contiene dati di lunghezza variabile in MySQL e non esiste alcun indice che corrisponda a TUTTE le colonne richieste ", ma filesortapparentemente non è stato utilizzato in quel caso. Penso anche che anche l'ordinamento di una piccola tabella nella memoria da sola potrebbe rivelarsi un risultato inaccettabile: ad esempio se la query viene eseguita molto (e la tabella cambia in modo che le cache non possano essere utilizzate).
Eggyal

Non ho tempo di provarlo, ma mi chiedo se questo è innescato da un VARCHAR che richiede 2 byte per la memorizzazione della lunghezza come specificato in dev.mysql.com/doc/refman/5.1/en/char. html - quindi la prima query rientra in quel limite ma la seconda no.

0

Prova ad aggiungere una WHEREclausola nelle tue query.

L'indice può essere utilizzato anche se ORDER BY non corrisponde esattamente all'indice, purché tutte le parti non utilizzate dell'indice e tutte le colonne ORDER BY aggiuntive siano costanti nella clausola WHERE . In alcuni casi, MySQL non può utilizzare gli indici per risolvere ORDER BY , sebbene utilizzi comunque gli indici per trovare le righe che corrispondono alla clausola WHERE .

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


Ma in questo caso la ORDER BY fa corrispondere esattamente l'indice, quindi non c'è bisogno di avere una WHEREclausola.
Eggyal

Ho una clausola "where" nella query effettiva sul sito, quindi so che non è la causa dell'ordinamento dei file. Mi chiedo se è l'uso di varchar?

0

Per quanto ne so, varchar può contenere solo un massimo di 8000 byte, ovvero circa 4000 caratteri. Pertanto, 5000 sembrerebbero eccedere il limite di archiviazione, e in questo caso probabilmente il motivo per cui lo smistamento viene incasinato.

"varchar [(n | max)] Dati di caratteri a lunghezza variabile, non Unicode. n può essere un valore compreso tra 1 e 8.000. max indica che la dimensione massima della memoria è 2 ^ 31-1 byte. La dimensione della memoria è effettiva lunghezza dei dati immessi + 2 byte. I dati immessi possono essere lunghi 0 caratteri. I sinonimi SQL-2003 per varchar sono variabili o caratteri variabili ".

Spero che questo risponda alla tua domanda


Come documentato in The CHARe VARCHARTipi : "I valori nelle colonne VARCHAR sono stringhe di lunghezza variabile. La lunghezza può essere specificata come un valore compreso tra 0 e 255 prima di MySQL 5.0.3 e tra 0 e 65.535 in 5.0.3 e versioni successive. la lunghezza massima di a VARCHARin MySQL 5.0.3 e successive è soggetta alla dimensione massima della riga (65.535 byte, che è condivisa tra tutte le colonne) e al set di caratteri utilizzato. "
Eggyal

0

Hai solo 126 righe nella tua tabella. Anche se ogni riga ha dimensioni massime di circa 5 KB, ciò significherebbe che la dimensione totale da leggere dal disco è solo di circa 600 KB - questo non è molto. Ad essere sinceri, è una quantità molto piccola, probabilmente inferiore alla dimensione della cache della maggior parte delle unità disco moderne.

Ora, se il server deve recuperare i dati per soddisfare la query, l'operazione più costosa consiste nel leggerli dal disco. Ma leggerlo secondo l'ordine dell'indice NON è sempre il modo più veloce per farlo, specialmente quando la quantità di dati è così piccola.

Nel tuo caso, è MOLTO più efficiente leggere i dati di tutta la tabella dal disco come blocco singolo nella memoria (probabilmente in una sola operazione di lettura o ricerca del disco), quindi ordinarli in RAM per soddisfare ORDER BY, che è istantaneo rispetto al disco operazione di lettura. Se il server legge i tuoi dati in base all'indice, dovrebbe eseguire fino a 126 (oops!) Operazioni di lettura, cercando più volte avanti e indietro nello stesso file di dati.

In altre parole, la scansione sequenziale NON è sempre una cosa negativa e mysql non è necessariamente stupido. Se provi a forzare mysql a usare quell'indice, molto probabilmente funzionerà più lentamente della scansione sequenziale che hai attualmente.

E il motivo per cui è stato utilizzato l'indice quando il campo da 5 KB non è stato incluso è perché i dati recuperati non costituivano il 99% dei dati nella tabella. Quando hai incluso il tuo campo da 5 KB, ora la query deve leggere il 99% dei dati, ed è più economico leggere tutto e ordinarlo in memoria in seguito.


Sembra che tu stia confondendo una serie di cose da Come evitare le scansioni della tabella completa , che hanno a che fare con l'uso dell'indice in JOINcondizioni e WHEREclausole soddisfacenti , non ORDER BYclausole.
Eggyal

Esattamente il contrario. In questo caso particolare, la scansione della tabella completa è BUONA semplicemente perché è PIÙ VELOCE della lettura per ordine dell'indice.

0

Quale versione di MySQL stai usando?

IN 5.1, ho provato a configurare il tuo scenario e ho popolato alcuni dati fittizi. Usando gli SQL che hai fornito, ricevo solo una scansione della tabella ogni volta secondo EXPLAIN. Per impostazione predefinita, quando si utilizza l'ordine di MYSQL ricorre al filesort anche se l'indice primario viene utilizzato nell'ordine di.

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.