Possibile INDICE su un campo VARCHAR in MySql


40

Sto lavorando in un database MySql , con una tabella come questa:

+--------------+
|  table_name  |
+--------------+
|    myField   |
+--------------+

... e ho bisogno di fare molte domande come questa (con 5-10 stringhe nell'elenco) :

SELECT myField FROM table_name
WHERE myField IN ('something', 'other stuff', 'some other a bit longer'...)

Ci saranno circa 24.000.000 di file uniche

1) Dovrei usare un FULLTEXToe INDEXchiave per la mia VARCHAR(150)?
2) Se aumentassi i caratteri da 150 a 220 o 250 ... farebbe una grande differenza? (C'è un modo per calcolarlo?)
3) Come ho già detto, saranno unici, quindi myField dovrebbe essere un PRIMARY KEY . Non è raro aggiungere una CHIAVE PRIMARIA a un campo che è già un VARCHAR INDEX / FULLTEXT?


non è necessario utilizzare PRIMARY per unicità. C'è già UNICO per questo.
kommradHomer,

Risposte:


62

SUGGERIMENTO # 1: indicizzazione standard

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    key (myfield)
);

Se indicizzi in questo modo, puoi cercare l'intera stringa o eseguire ricerche LIKE orientate a sinistra

SUGGERIMENTO # 2: indicizzazione FULLTEXT

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    fulltext (myfield)
);

Puoi utilizzare in modo efficace le ricerche per singole parole chiave e frasi intere. Dovrai definire un elenco di parole d'ordine personalizzate perché MySQL non indicizzerà 543 parole .

Ecco i miei altri post degli ultimi due anni sugli indici FULLTEXT

SUGGERIMENTO # 3: indicizzazione hash

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    hashmyfield char(32) not null,
    primary key (id),
    key (hashmyfield)
);

Se stai cercando un valore specifico e tali valori potrebbero avere lunghezze ben superiori a 32 caratteri, puoi memorizzare il valore hash:

INSERT INTO mytable (myfield,hashmyfield)
VALUES ('whatever',MD5('whatever'));

In questo modo, basta cercare i valori di hash per recuperare i risultati

SELECT * FROM mytable WHERE hashmyfield = MD5('whatever');

Provaci !!!


Non ho abbastanza reputazione per votare la tua risposta, ma devo dire che è stato FANTASTICO. Grazie per la spiegazione e gli esempi. Penso che l'indicizzazione di hash sia la migliore per il mio caso, è una soluzione fantastica. Ma ancora una domanda: quale pensi che sarà il limite delle righe per le ricerche rapide nella tabella? [usando come KEY il VARCHAR (32) per le ricerche]
Mark Tower

2
L'opzione hash qui è ancora un testo e 32 byte per un totale di 16 byte. Puoi usare un campo bigint con conv (a sinistra (md5 ('qualunque'), 16), 16, -10). Non c'è un valore numerico di 16 byte, ma potresti trovare metà dell'md5 sufficiente e quindi sono solo 8 byte nell'indice
atxdba,

1
Non è consigliabile utilizzare MD5 o SHA1 per produrre stringhe che verranno indicizzate. La distribuzione di stringhe prodotte da funzioni di hashing come MD5 o SHA1 è casuale in un ampio spazio che riduce l'efficienza dell'indice, che può rallentare le istruzioni INSERT e SELECT. Ecco un post che lo spiega: code-epicenter.com/…
Mr.M

Mi scuso perché questo è un vecchio thread, ma la mia domanda era direttamente correlata a questo, ma non sono in grado di ottenere una risposta chiara alle mie esigenze leggendo quanto sopra e altri articoli simili. Il mio scenario è: sto sviluppando un sistema di stock molto rudimentale che per ora consiste in una sola tabella. Vi si accede esternamente tramite un'API, quindi tutta la configurazione è conservata altrove, motivo per cui è necessaria un'unica tabella. Le due colonne che sto pensando di indicizzare, avrebbero circa 200 voci univoche ciascuna, di lunghezza <20 caratteri. Dovrei considerare l'aggiunta di indici?
Mike,

Questo è orientato a sinistra come la ricerca like 'a%'?
Ragioniere

18

MySQL ti consente di definire un indice prefissato, il che significa che devi definire i primi N caratteri dalla stringa originale da indicizzare, e il trucco è scegliere un numero N che sia abbastanza lungo da dare una buona selettività, ma abbastanza corto da risparmiare spazio. Il prefisso dovrebbe essere abbastanza lungo da rendere l'indice quasi utile quanto lo sarebbe se indicizzassi l'intera colonna.

Prima di andare oltre, definiamo alcuni termini importanti. La selettività dell'indice è il rapporto tra i valori indicizzati distinti totali e il numero totale di righe . Ecco un esempio per la tabella di test:

+-----+-----------+
| id  | value     |
+-----+-----------+
| 1   | abc       |
| 2   | abd       |
| 3   | adg       |
+-----+-----------+

Se indicizziamo solo il primo carattere (N = 1), la tabella dell'indice sarà simile alla seguente tabella:

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| a             | 1,2,3     |
+---------------+-----------+

In questo caso, la selettività dell'indice è uguale a IS = 1/3 = 0,33.

Vediamo ora cosa accadrà se aumentiamo il numero di caratteri indicizzati a due (N = 2).

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| ab             | 1,2      |
| ad             | 3        |
+---------------+-----------+

In questo scenario IS = 2/3 = 0,66 significa che abbiamo aumentato la selettività dell'indice, ma abbiamo anche aumentato la dimensione dell'indice. Il trucco è trovare il numero minimo N che porterà alla massima selettività dell'indice .

Esistono due approcci che è possibile eseguire calcoli per la tabella del database. Farò dimostrazione su questo dump del database .

Supponiamo di voler aggiungere all'indice la colonna last_name negli impiegati della tabella e vogliamo definire il numero N più piccolo che produrrà la migliore selettività dell'indice.

Innanzitutto cerchiamo di identificare i cognomi più frequenti:

select count(*) as cnt, last_name from employees group by employees.last_name order by cnt

+-----+-------------+
| cnt | last_name   |
+-----+-------------+
| 226 | Baba        |
| 223 | Coorg       |
| 223 | Gelosh      |
| 222 | Farris      |
| 222 | Sudbeck     |
| 221 | Adachi      |
| 220 | Osgood      |
| 218 | Neiman      |
| 218 | Mandell     |
| 218 | Masada      |
| 217 | Boudaillier |
| 217 | Wendorf     |
| 216 | Pettis      |
| 216 | Solares     |
| 216 | Mahnke      |
+-----+-------------+
15 rows in set (0.64 sec)

Come puoi vedere, il cognome Baba è il più frequente. Ora troveremo i prefissi last_name che si verificano più frequentemente , iniziando con i prefissi a cinque lettere.

+-----+--------+
| cnt | prefix |
+-----+--------+
| 794 | Schaa  |
| 758 | Mande  |
| 711 | Schwa  |
| 562 | Angel  |
| 561 | Gecse  |
| 555 | Delgr  |
| 550 | Berna  |
| 547 | Peter  |
| 543 | Cappe  |
| 539 | Stran  |
| 534 | Canna  |
| 485 | Georg  |
| 417 | Neima  |
| 398 | Petti  |
| 398 | Duclo  |
+-----+--------+
15 rows in set (0.55 sec)

Ci sono molte più occorrenze di ogni prefisso, il che significa che dobbiamo aumentare il numero N fino a quando i valori sono quasi gli stessi dell'esempio precedente.

Ecco i risultati per N = 9

select count(*) as cnt, left(last_name,9) as prefix from employees group by prefix order by cnt desc limit 0,15;

+-----+-----------+
| cnt | prefix    |
+-----+-----------+
| 336 | Schwartzb |
| 226 | Baba      |
| 223 | Coorg     |
| 223 | Gelosh    |
| 222 | Sudbeck   |
| 222 | Farris    |
| 221 | Adachi    |
| 220 | Osgood    |
| 218 | Mandell   |
| 218 | Neiman    |
| 218 | Masada    |
| 217 | Wendorf   |
| 217 | Boudailli |
| 216 | Cummings  |
| 216 | Pettis    |
+-----+-----------+

Ecco i risultati per N = 10.

+-----+------------+
| cnt | prefix     |
+-----+------------+
| 226 | Baba       |
| 223 | Coorg      |
| 223 | Gelosh     |
| 222 | Sudbeck    |
| 222 | Farris     |
| 221 | Adachi     |
| 220 | Osgood     |
| 218 | Mandell    |
| 218 | Neiman     |
| 218 | Masada     |
| 217 | Wendorf    |
| 217 | Boudaillie |
| 216 | Cummings   |
| 216 | Pettis     |
| 216 | Solares    |
+-----+------------+
15 rows in set (0.56 sec)

Questi sono ottimi risultati. Ciò significa che possiamo creare un indice sulla colonna last_name indicizzando solo i primi 10 caratteri. Nella colonna della definizione della tabella, last_name è definito come VARCHAR(16), e questo significa che abbiamo salvato 6 byte (o più se ci sono caratteri UTF8 nel cognome) per voce. In questa tabella ci sono 1637 valori distinti moltiplicati per 6 byte è di circa 9 KB e immagina come questo numero aumenterebbe se la nostra tabella contiene milioni di righe.

Puoi leggere altri modi per calcolare il numero di N nel mio post Indici prefissati in MySQL .

Anche l'uso delle funzioni MD5 e SHA1 per generare valori che dovrebbero essere indicizzati non è un buon approccio . Perché? Leggilo per posta Come scegliere il giusto tipo di dati per una chiave primaria nel database MySQL


Questa è una risposta molto dettagliata a una domanda diversa.
Mustaccio,

1
Ma stai scherzando?
Mr.M,

Puoi spiegare cosa è sbagliato o cosa non può essere applicato alla domanda?
Mr.M,

2
Hey MrD. In realtà mi piace la tua risposta. Perché ? Nel mio vecchio risposta, ho detto nel SUGGERIMENTO # 1: If you index like this, you can either look for the whole string or do left-oriented LIKE searches. Ho anche detto in SUGGERIMENTO # 3: If you are looking for one specific value and those values could be lengths well beyond 32 characters, you could store the hash value:. La tua risposta dimostra adeguatamente perché non si dovrebbero usare chiavi enormi e si dovrebbe indicizzare sui caratteri più a sinistra, il che può fare la differenza nelle prestazioni. La tua risposta appartiene qui. +1 per la risposta e benvenuto in DBA StackExchange.
RolandoMySQLDBA,
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.