Possible INDEX sur un champ VARCHAR dans MySql

40

Je travaille dans une base de données MySql , avec un tableau comme celui-ci:

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

... et je dois faire beaucoup de requêtes comme ceci (avec 5 à 10 chaînes dans la liste) :

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

Il y aura environ 24.000.000 lignes uniques

1) Dois-je utiliser une touche FULLTEXTou et INDEXpour mon VARCHAR(150)?
2) Si j'augmente les caractères de 150 à 220 ou 250 ... cela ferait-il une grande différence? (Est - il possible de le calculer?)
3) Comme je l' ai dit, ils vont être unique, donc myField devrait être une clé primaire . N’est-il pas rare d’ajouter une PRIMARY KEY à un champ qui est déjà un VARCHAR INDEX / FULLTEXT?

Mark Tower
la source
vous n'avez pas besoin d'utiliser PRIMARY pour l'unicité. Il y a déjà UNIQUE pour ça.
kommradHomer

Réponses:

62

SUGGESTION 1: Indexation standard

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

Si vous indexez de la sorte, vous pouvez rechercher la chaîne entière ou effectuer des recherches LIKE orientées à gauche.

SUGGESTION 2: Indexation FULLTEXT

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

Vous pouvez utiliser efficacement les recherches de mots-clés individuels ainsi que de phrases entières. Vous devrez définir une liste de mots vides personnalisée, car MySQL n'indexera pas 543 mots .

Voici mes autres articles des deux dernières années sur les index FULLTEXT.

SUGGESTION 3: Indexation du hachage

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

Si vous recherchez une valeur spécifique et que ces valeurs peuvent avoir une longueur supérieure à 32 caractères, vous pouvez stocker la valeur de hachage:

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

De cette façon, il vous suffit de rechercher des valeurs de hachage pour récupérer les résultats.

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

Essaie !!!

RolandoMySQLDBA
la source
Je n'ai pas assez de réputation pour voter votre réponse, mais je dois dire que c'était génial. Merci pour l'explication et les exemples. Je pense que l'indexation de hachage est la meilleure pour mon cas, c'est une solution géniale. Mais toujours une question: quelle sera selon vous la limite de lignes pour les recherches rapides dans le tableau? [utiliser comme clé le VARCHAR (32) pour les recherches]
Mark Tower
2
L'option de hachage ici est toujours un texte et 32 ​​octets pour ce qui est vraiment 16 octets. Vous pouvez utiliser un champ bigint avec conv (left (md5, 16), 16, -10). Il n'y a pas de 16 octets numériques, mais vous pouvez trouver la moitié du md5 suffisante et seulement 8 octets dans l'index
atxdba
1
Il n'est pas bon d'utiliser MD5 ou SHA1 pour produire des chaînes qui seront indexées. La distribution des chaînes générées par les fonctions de hachage telles que MD5 ou SHA1 est aléatoire dans un grand espace, ce qui diminue l'efficacité de votre index et peut ralentir les instructions INSERT et SELECT. Voici le post l'expliquant: code-epicenter.com/…
Mr.M
Je m'excuse car il s'agit d'un vieux fil, mais ma question portait directement sur cela, mais je ne suis pas en mesure d'obtenir une réponse claire à mes besoins en lisant ce qui précède et d'autres articles similaires. Mon scénario est le suivant: je développe un système de stock très rudimentaire qui consiste en un seul tableau pour le moment. Il est accessible de manière externe via une API, de sorte que toute la configuration est conservée ailleurs - ce qui explique pourquoi nous n’avons besoin que d’une seule table. Les deux colonnes que je pense indexer auraient environ 200 entrées uniques chacune, d’une longueur <20 caractères. Devrais-je envisager d'ajouter des index?
Mike
Est-ce que cette recherche est orientée vers la gauche like 'a%'?
Comptable م
18

MySQL vous permet de définir un index préfixé, ce qui signifie que vous définissez les N premiers caractères de la chaîne d'origine à indexer. L'astuce consiste à choisir un nombre N suffisamment long pour permettre une bonne sélectivité, mais suffisamment court pour économiser de l'espace. Le préfixe doit être suffisamment long pour rendre l'index presque aussi utile que si vous aviez indexé la colonne entière.

Avant d'aller plus loin, définissons quelques termes importants. La sélectivité d'index est le rapport entre le total des valeurs indexées distinctes et le nombre total de lignes . Voici un exemple de table de test:

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

Si nous n'indexons que le premier caractère (N = 1), alors la table d'index ressemblera à la table suivante:

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

Dans ce cas, la sélectivité de l’indice est égale à IS = 1/3 = 0,33.

Voyons maintenant ce qui se passera si nous augmentons le nombre de caractères indexés à deux (N = 2).

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

Dans ce scénario, IS = 2/3 = 0.66, ce qui signifie que nous avons augmenté la sélectivité de l’indice, mais nous avons également augmenté la taille de l’indice. L'astuce consiste à trouver le nombre minimal N qui aboutira à la sélectivité maximale de l' index .

Il existe deux approches pour effectuer des calculs pour votre table de base de données. Je vais faire une démonstration sur le dump de cette base de données .

Supposons que nous voulions ajouter la colonne last_name dans la table employee à l'index et définir le plus petit nombre N qui produirait la meilleure sélectivité pour l'index.

Premièrement, identifions les noms de famille les plus fréquents:

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)

Comme vous pouvez le constater, le nom de famille Baba est le plus fréquent. Nous allons maintenant rechercher les préfixes last_name les plus fréquents , en commençant par les préfixes de cinq lettres.

+-----+--------+
| 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)

Il y a beaucoup plus d'occurrences de chaque préfixe, ce qui signifie que nous devons augmenter le nombre N jusqu'à ce que les valeurs soient presque identiques à celles de l'exemple précédent.

Voici les résultats de la recherche pour 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    |
+-----+-----------+

Voici les résultats pour 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)

Ce sont de très bons résultats. Cela signifie que nous pouvons créer une indexation sur la colonne last_name en n'indexant que les 10 premiers caractères. Dans la définition de table, la colonne nom_famille est définie comme VARCHAR(16), ce qui signifie que nous avons enregistré 6 octets (ou plus si le nom contient des caractères UTF8) par entrée. Dans ce tableau, il y a 1637 valeurs distinctes multipliées par 6 octets, soit environ 9 Ko, et imaginez comment ce nombre augmenterait si notre table contenait des millions de lignes.

Vous pouvez lire d’autres méthodes de calcul du nombre de N dans mon post Les index préfixés dans MySQL .

Utiliser les fonctions MD5 et SHA1 pour générer des valeurs qui devraient être indexées n’est pas non plus une bonne approche . Pourquoi? Lisez-le en post Comment choisir le bon type de données pour une clé primaire dans la base de données MySQL

Mr.M
la source
C'est une réponse très verbeuse à une question différente.
Mustaccio
1
Vous plaisantez j'espère?
M.
Pouvez-vous expliquer ce qui ne va pas ou ce qui ne peut pas être appliqué à la question?
Mr.M
2
Hey MrD. En fait, j'aime votre réponse. Pourquoi ? Dans ma vieille réponse, je l' ai dit dans SUGGESTION # 1: If you index like this, you can either look for the whole string or do left-oriented LIKE searches. J'ai aussi dit dans SUGGESTION # 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:. Votre réponse montre bien pourquoi on ne devrait pas utiliser de grandes clés et indexer sur les caractères les plus à gauche, ce qui peut améliorer les performances. Votre réponse appartient ici. +1 pour votre réponse et bienvenue dans DBA StackExchange.
RolandoMySQLDBA