Möglicher INDEX für ein VARCHAR-Feld in MySQL

40

Ich arbeite in einer MySQL-Datenbank mit einer Tabelle wie dieser:

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

... und ich muss eine Menge solcher Fragen stellen (mit 5-10 Zeichen in der Liste) :

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

Es wird rund 24.000.000 eindeutige Zeilen geben

1) Soll ich einen FULLTEXToder und INDEXSchlüssel für meine verwenden VARCHAR(150)?
2) Wenn ich die Zeichen von 150 auf 220 oder 250 erhöhe ... würde das einen großen Unterschied machen? (Gibt es eine Möglichkeit, dies zu berechnen?)
3) Wie ich bereits sagte, werden sie eindeutig sein, daher sollte myField ein PRIMARY KEY sein . Ist es nicht selten, einem Feld, das bereits ein VARCHAR INDEX / FULLTEXT ist, einen PRIMARY KEY hinzuzufügen?

Mark Tower
quelle
Sie müssen PRIMARY nicht verwenden, um die Eindeutigkeit zu gewährleisten. Dafür gibt es bereits EINZIGARTIG.
kommradHomer

Antworten:

62

VORSCHLAG 1: Standardindizierung

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

Wenn Sie so indizieren, können Sie entweder nach der gesamten Zeichenfolge suchen oder linksgerichtete LIKE-Suchen durchführen

VORSCHLAG 2: Volltextindizierung

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

Sie können effektiv nach einzelnen Stichwörtern und ganzen Phrasen suchen. Sie müssen eine benutzerdefinierte Stoppwortliste definieren, da MySQL keine 543 Wörter indiziert .

Hier sind meine anderen Beiträge der letzten zwei Jahre zu FULLTEXT-Indizes

VORSCHLAG 3: Hash-Indizierung

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

Wenn Sie nach einem bestimmten Wert suchen und diese Werte länger als 32 Zeichen sein können, können Sie den Hash-Wert speichern:

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

Auf diese Weise suchen Sie einfach nach Hash-Werten, um die Ergebnisse abzurufen

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

Versuche es !!!

RolandoMySQLDBA
quelle
Ich habe nicht genug Ruf, um deine Antwort zu bewerten, aber ich muss sagen, dass es großartig war. Vielen Dank für die Erklärung und die Beispiele. Ich denke, die Hash-Indizierung ist das Beste für meinen Fall, es ist eine großartige Lösung. Aber noch eine Frage: Wie hoch wird Ihrer Meinung nach die maximale Anzahl von Zeilen für die schnelle Suche in der Tabelle sein? [Verwenden des VARCHAR (32) als SCHLÜSSEL für Suchanfragen]
Mark Tower
2
Die Hash-Option hier ist immer noch ein Text und 32 Bytes für 16 Bytes. Sie können ein Bigint-Feld mit conv (left (md5 ('whatever'), 16), 16, -10) verwenden. Es gibt keine 16-Byte-Zahl, aber vielleicht ist die Hälfte des md5 ausreichend, und dann sind es nur 8 Byte im Index
atxdba
1
Es ist nicht gut, MD5 oder SHA1 zu verwenden, um Zeichenfolgen zu erzeugen, die indiziert werden. Die Verteilung von Zeichenfolgen, die durch Hashing-Funktionen wie MD5 oder SHA1 erzeugt werden, erfolgt zufällig in einem großen Bereich, wodurch die Effizienz Ihres Index verringert wird und INSERT- und SELECT-Anweisungen verlangsamt werden können. Hier ist ein Beitrag, der dies erklärt: code-epicenter.com/
Mr.M
Ich entschuldige mich dafür, dass dies ein alter Thread ist, aber meine Frage bezog sich direkt darauf, aber ich bin nicht in der Lage, eine klare Antwort auf meine Bedürfnisse zu erhalten, wenn ich die obigen und ähnliche Artikel lese. Mein Szenario ist: Ich entwickle ein sehr rudimentäres Aktiensystem, das vorerst nur aus einer Tabelle besteht. Der Zugriff erfolgt extern über eine API, sodass die gesamte Konfiguration an einem anderen Ort gespeichert wird. Aus diesem Grund benötigen wir nur eine einzige Tabelle. Die beiden Spalten, die ich indizieren möchte, haben jeweils ungefähr 200 eindeutige Einträge mit einer Länge von <20 Zeichen. Sollte ich in Betracht ziehen, Indizes hinzuzufügen?
Mike
Ist das linksgerichtet wie suchen like 'a%'?
Buchhalter م
18

Mit MySQL können Sie einen Präfixindex definieren, dh, Sie definieren die ersten N Zeichen der zu indizierenden Originalzeichenfolge. Der Trick besteht darin, eine Zahl N zu wählen, die lang genug ist, um eine gute Selektivität zu erzielen, aber kurz genug, um Platz zu sparen. Das Präfix sollte lang genug sein, um den Index fast so nützlich zu machen, als ob Sie die gesamte Spalte indiziert hätten.

Bevor wir weitermachen, lassen Sie uns einige wichtige Begriffe definieren. Die Indexselektivität ist das Verhältnis der gesamten indizierten Werte und der Gesamtanzahl der Zeilen . Hier ist ein Beispiel für eine Testtabelle:

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

Indizieren wir nur das erste Zeichen (N = 1), sieht die Indextabelle wie folgt aus:

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

In diesem Fall ist die Indexselektivität gleich IS = 1/3 = 0,33.

Lassen Sie uns nun sehen, was passieren wird, wenn wir die Anzahl der indizierten Zeichen auf zwei erhöhen (N = 2).

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

In diesem Szenario ist IS = 2/3 = 0,66, was bedeutet, dass wir die Indexselektivität erhöht haben, aber wir haben auch die Größe des Index erhöht. Der Trick besteht darin, die minimale Zahl N zu finden, die zu maximaler Indexselektivität führt .

Es gibt zwei Ansätze, mit denen Sie Berechnungen für Ihre Datenbanktabelle durchführen können. Ich werde auf diesem Datenbank-Dump demonstrieren .

Lassen Sie uns sagen , dass wir Spalte hinzufügen möchten last_name in der Tabelle Mitarbeiter auf den Index, und wir wollen die kleinste Zahl definieren , N , die die beste Index Selektivität produzieren.

Lassen Sie uns zuerst die häufigsten Nachnamen identifizieren:

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)

Wie Sie sehen, ist der Nachname Baba der häufigste. Jetzt werden wir die am häufigsten vorkommenden Nachnamen- Präfixe finden, beginnend mit Präfixen aus fünf Buchstaben.

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

Es gibt viel mehr Vorkommen für jedes Präfix, was bedeutet, dass wir die Zahl N erhöhen müssen, bis die Werte fast dieselben sind wie im vorherigen Beispiel.

Hier sind Ergebnisse für 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    |
+-----+-----------+

Hier sind Ergebnisse für 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)

Das sind sehr gute Ergebnisse. Dies bedeutet, dass wir einen Index für die Spalte Nachname erstellen können, wobei nur die ersten 10 Zeichen indiziert werden. In der Tabellendefinitionsspalte ist Nachname als definiert. VARCHAR(16)Dies bedeutet, dass wir 6 Bytes (oder mehr, wenn der Nachname UTF8-Zeichen enthält) pro Eintrag gespeichert haben. In dieser Tabelle gibt es 1637 verschiedene Werte, multipliziert mit 6 Bytes, was ungefähr 9 KB entspricht. Stellen Sie sich vor, wie diese Zahl wachsen würde, wenn unsere Tabelle Millionen von Zeilen enthält.

Sie können andere Methoden zum Berechnen der Anzahl von N in My Post Prefixed-Indizes in MySQL lesen .

Die Verwendung von MD5- und SHA1-Funktionen zur Erzeugung von Werten, die indiziert werden sollen, ist ebenfalls kein guter Ansatz . Warum? Read it in post So wählen Sie den richtigen Datentyp für einen Primärschlüssel in einer MySQL-Datenbank aus

Mr.M
quelle
Dies ist eine sehr ausführliche Antwort auf eine andere Frage.
mustaccio
1
Willst du mich veräppeln?
Mr.M
Können Sie erklären, was falsch ist oder was nicht auf die Frage angewendet werden kann?
Mr.M
2
Hey MrD. Ich mag deine Antwort wirklich. Warum ? In meiner alten Antwort, sagte ich in SUGGESTION # 1: If you index like this, you can either look for the whole string or do left-oriented LIKE searches. Ich sagte auch , in 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:. Ihre Antwort zeigt hinreichend, warum man keine großen Schlüssel verwenden und auf Zeichen ganz links indexieren sollte, was die Leistung beeinträchtigen kann. Ihre Antwort gehört hierher. +1 für Ihre Antwort und Willkommen bei DBA StackExchange.
RolandoMySQLDBA