So optimieren Sie sehr langsames SELECT mit LEFT JOINs über große Tische

14

Ich googelte, war autodidaktisch und suchte stundenlang nach einer Lösung, aber ohne Glück. Ich habe hier einige ähnliche Fragen gefunden, aber nicht diesen Fall.

Meine Tische:

  • Personen (~ 10 Millionen Reihen)
  • Attribute (Ort, Alter, ...)
  • Verknüpfungen (M: M) zwischen Personen und Attributen (~ 40 Millionen Zeilen)

Full Dump ~ 280MB

Situation: Ich versuche, alle Personen-IDs ( person_id) von einigen Orten ( location.attribute_value BETWEEN 3000 AND 7000) auszuwählen , die ein Geschlecht haben ( gender.attribute_value = 1), in einigen Jahren geboren sind ( bornyear.attribute_value BETWEEN 1980 AND 2000) und eine Augenfarbe haben (eyecolor.attribute_value IN (2,3) ).

Dies ist meine Frage, die 3 ~ 4 min dauerte . und ich möchte optimieren:

SELECT person_id
FROM person
    LEFT JOIN attribute location ON location.attribute_type_id = 1 AND location.person_id = person.person_id
    LEFT JOIN attribute gender ON gender.attribute_type_id = 2 AND gender.person_id = person.person_id
    LEFT JOIN attribute bornyear ON bornyear.attribute_type_id = 3 AND bornyear.person_id = person.person_id
    LEFT JOIN attribute eyecolor ON eyecolor.attribute_type_id = 4 AND eyecolor.person_id = person.person_id
WHERE 1
    AND location.attribute_value BETWEEN 3000 AND 7000
    AND gender.attribute_value = 1
    AND bornyear.attribute_value BETWEEN 1980 AND 2000
    AND eyecolor.attribute_value IN (2,3)
LIMIT 100000;

Ergebnis:

+-----------+
| person_id |
+-----------+
|       233 |
|       605 |
|       ... |
|   8702599 |
|   8703617 |
+-----------+
100000 rows in set (3 min 42.77 sec)

Erklären Sie erweitert:

+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
| id | select_type | table    | type   | possible_keys                               | key             | key_len | ref                      | rows    | filtered | Extra                    |
+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
|  1 | SIMPLE      | bornyear | range  | attribute_type_id,attribute_value,person_id | attribute_value | 5       | NULL                     | 1265229 |   100.00 | Using where              |
|  1 | SIMPLE      | location | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.bornyear.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | eyecolor | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.bornyear.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | gender   | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.eyecolor.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | person   | eq_ref | PRIMARY                                     | PRIMARY         | 4       | test1.location.person_id |       1 |   100.00 | Using where; Using index |
+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
5 rows in set, 1 warning (0.02 sec)

Profiling:

+------------------------------+-----------+
| Status                       | Duration  |
+------------------------------+-----------+
| Sending data                 |  3.069452 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.968915 |
| Waiting for query cache lock |  0.000019 |
| Sending data                 |  3.042468 |
| Waiting for query cache lock |  0.000043 |
| Sending data                 |  3.264984 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.823919 |
| Waiting for query cache lock |  0.000038 |
| Sending data                 |  2.863903 |
| Waiting for query cache lock |  0.000014 |
| Sending data                 |  2.971079 |
| Waiting for query cache lock |  0.000020 |
| Sending data                 |  3.053197 |
| Waiting for query cache lock |  0.000087 |
| Sending data                 |  3.099053 |
| Waiting for query cache lock |  0.000035 |
| Sending data                 |  3.064186 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.939404 |
| Waiting for query cache lock |  0.000018 |
| Sending data                 |  3.440288 |
| Waiting for query cache lock |  0.000086 |
| Sending data                 |  3.115798 |
| Waiting for query cache lock |  0.000068 |
| Sending data                 |  3.075427 |
| Waiting for query cache lock |  0.000072 |
| Sending data                 |  3.658319 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.335427 |
| Waiting for query cache lock |  0.000049 |
| Sending data                 |  3.319430 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.496563 |
| Waiting for query cache lock |  0.000029 |
| Sending data                 |  3.017041 |
| Waiting for query cache lock |  0.000032 |
| Sending data                 |  3.132841 |
| Waiting for query cache lock |  0.000050 |
| Sending data                 |  2.901310 |
| Waiting for query cache lock |  0.000016 |
| Sending data                 |  3.107269 |
| Waiting for query cache lock |  0.000062 |
| Sending data                 |  2.937373 |
| Waiting for query cache lock |  0.000016 |
| Sending data                 |  3.097082 |
| Waiting for query cache lock |  0.000261 |
| Sending data                 |  3.026108 |
| Waiting for query cache lock |  0.000026 |
| Sending data                 |  3.089760 |
| Waiting for query cache lock |  0.000041 |
| Sending data                 |  3.012763 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  3.069694 |
| Waiting for query cache lock |  0.000046 |
| Sending data                 |  3.591908 |
| Waiting for query cache lock |  0.000060 |
| Sending data                 |  3.526693 |
| Waiting for query cache lock |  0.000076 |
| Sending data                 |  3.772659 |
| Waiting for query cache lock |  0.000069 |
| Sending data                 |  3.346089 |
| Waiting for query cache lock |  0.000245 |
| Sending data                 |  3.300460 |
| Waiting for query cache lock |  0.000019 |
| Sending data                 |  3.135361 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  2.909447 |
| Waiting for query cache lock |  0.000039 |
| Sending data                 |  3.337561 |
| Waiting for query cache lock |  0.000140 |
| Sending data                 |  3.138180 |
| Waiting for query cache lock |  0.000090 |
| Sending data                 |  3.060687 |
| Waiting for query cache lock |  0.000085 |
| Sending data                 |  2.938677 |
| Waiting for query cache lock |  0.000041 |
| Sending data                 |  2.977974 |
| Waiting for query cache lock |  0.000872 |
| Sending data                 |  2.918640 |
| Waiting for query cache lock |  0.000036 |
| Sending data                 |  2.975842 |
| Waiting for query cache lock |  0.000051 |
| Sending data                 |  2.918988 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  2.943810 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.330211 |
| Waiting for query cache lock |  0.000025 |
| Sending data                 |  3.411236 |
| Waiting for query cache lock |  0.000023 |
| Sending data                 | 23.339035 |
| end                          |  0.000807 |
| query end                    |  0.000023 |
| closing tables               |  0.000325 |
| freeing items                |  0.001217 |
| logging slow query           |  0.000007 |
| logging slow query           |  0.000011 |
| cleaning up                  |  0.000104 |
+------------------------------+-----------+
100 rows in set (0.00 sec)

Tabellenstrukturen:

CREATE TABLE `attribute` (
  `attribute_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `attribute_type_id` int(11) unsigned DEFAULT NULL,
  `attribute_value` int(6) DEFAULT NULL,
  `person_id` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`attribute_id`),
  KEY `attribute_type_id` (`attribute_type_id`),
  KEY `attribute_value` (`attribute_value`),
  KEY `person_id` (`person_id`)
) ENGINE=MyISAM AUTO_INCREMENT=40000001 DEFAULT CHARSET=utf8;

CREATE TABLE `person` (
  `person_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `person_name` text CHARACTER SET latin1,
  PRIMARY KEY (`person_id`)
) ENGINE=MyISAM AUTO_INCREMENT=20000001 DEFAULT CHARSET=utf8;

Die Abfrage wurde auf einem virtuellen DigitalOcean-Server mit SSD und 1 GB RAM durchgeführt.

Ich gehe davon aus, dass es Probleme mit dem Datenbankdesign geben könnte. Haben Sie Vorschläge, um diese Situation besser zu gestalten? Oder nur um die Auswahl oben anzupassen?

Martin
quelle
4
Das ist der Preis, den Sie für das EAV-Design zahlen. Vielleicht möchten Sie einen zusammengesetzten Index fürattribute (person_id, attribute_type_id, attribute_value)
mustaccio 16.10.15
1
Ich würde versuchen, diese Indizes hinzuzufügen: (attribute_type_id, attribute_value, person_id)und (attribute_type_id, person_id, attribute_value)
ypercubeᵀᴹ
5
Verwenden Sie InnoDB und werfen Sie MyISAM weg. Dies ist 2015, MyiSAM ist schon lange tot.
ypercubeᵀᴹ
2
Als Erstes wird der LEFT-Join entfernt. Er hat keine Auswirkung, da Sie alle Tabellen in Ihrer WHERE-Bedingung verwenden und alle Joins effektiv in INNER-Joins umwandeln (der Optimierer sollte dies verstehen und optimieren können, aber es besser nicht schwieriger machen ). Die zweite Sache - Deaktivieren Sie die Abfrage - Cache , es sei denn Sie haben einen starken Grund , es zu verwenden (= Sie es getestet und gemessen , dass es Ihnen hilft)
jkavalik
2
OT: Ist es nicht seltsam, dass Sie LIMIT ohne ORDER BY verwenden? Dies wird einige zufällige 100000 Zeilen zurückgeben?
ibre5041

Antworten:

6

Wählen Sie einige Attribute aus, die berücksichtigt werden sollen person. Indizieren Sie sie in wenigen Kombinationen - verwenden Sie zusammengesetzte Indizes und keine einspaltigen Indizes.

Dies ist im Grunde der einzige Ausweg aus EAV-Sucks-at-Performance, bei dem Sie sich gerade befinden.

Hier finden Sie weitere Informationen: http://mysql.rjweb.org/doc.php/eav, einschließlich eines Vorschlags zur Verwendung von JSON anstelle der Schlüsselwerttabelle.

Rick James
quelle
3

Addiere Indeces zu attributefür:

  • (person_id, attribute_type_id, attribute_value) und
  • (attribute_type_id, attribute_value, person_id)

Erläuterung

Mit Ihrem aktuellen Design EXPLAINerwartet Ihre Abfrage, 1,265,229 * 4 * 4 * 4 = 80,974,656Zeilen in zu untersuchen attribute. Sie können durch Hinzufügen eines diese Zahl reduzieren Composite - Index auf attributefür (person_id, attribute_type_id). Diesen Index Kriterien nur 1 statt 4 Zeilen für die einzelnen untersuchen location, eyecolorund gender.

Sie könnten diesen Index erweitern umfassen attribute_type_valueauch: (person_id, attribute_type_id, attribute_value). Dies würde diesen Index in einen abdeckenden Index für diese Abfrage verwandeln , was ebenfalls die Leistung verbessern sollte.

Darüber hinaus sollte das Hinzufügen eines Index für (attribute_type_id, attribute_value, person_id)(erneut ein abdeckender Index durch Einschließen person_id) die Leistung verbessern, anstatt nur einen Index für einen Index zu verwenden, attribute_valuebei dem mehr Zeilen untersucht werden müssten. In diesem Fall wird der erste Schritt in Ihrer Erklärung beschleunigt: Auswahl eines Bereichs ausbornyear .

Durch die Verwendung dieser beiden Indizes wurde die Ausführungszeit Ihrer Abfrage auf meinem System von ~ 2,0 s auf ~ 0,2 s gesenkt, wobei die EXPLAIN-Ausgabe folgendermaßen aussah:

+----+-------------+----------+--------+-------------------------------------+-------------------+---------+--------------------------------+---------+----------+--------------------------+
| id | select_type | table    | type   | possible_keys                       | key               | key_len | ref                            |    rows | filtered | Extra                    |
+----+-------------+----------+--------+-------------------------------------+-------------------+---------+--------------------------------+---------+----------+--------------------------+
|  1 | SIMPLE      | bornyear | range  | person_type_value,type_value_person | type_value_person |       9 |                                | 1861881 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | location | ref    | person_type_value,type_value_person | person_type_value |       8 | bornyear.person_id,const       |       1 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | eyecolor | ref    | person_type_value,type_value_person | person_type_value |       8 | bornyear.person_id,const       |       1 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | gender   | ref    | person_type_value,type_value_person | person_type_value |      13 | bornyear.person_id,const,const |       1 |   100.00 | Using index              |
|  1 | SIMPLE      | person   | eq_ref | PRIMARY                             | PRIMARY           |       4 | bornyear.person_id             |       1 |   100.00 | Using index              |
+----+-------------+----------+--------+-------------------------------------+-------------------+---------+--------------------------------+---------+----------+--------------------------+
wolfgangwalther
quelle
1
Vielen Dank für die ausführliche Antwort und Erklärung. Ich habe alles getan, was Sie erwähnt haben, aber die Abfrage dauert noch ca. 2 Minuten. Bitte, welchen Tabellentyp (innodb, myisam) verwenden Sie und welche genaue Abfrage haben Sie durchgeführt?
Martin
1
Abgesehen vom Hinzufügen der Indices habe ich genau die Daten und Definitionen verwendet, die Sie auch verwendet haben, und deshalb MyISAM verwendet. Ich habe die erste Zeile Ihrer Abfrage in geändert, SELECT person.person_idda sie sonst offensichtlich nicht ausgeführt werden würde. Haben Sie getan, ANALYZE TABLE attributenachdem Sie die Indices hinzugefügt haben? Möglicherweise möchten Sie auch Ihre neue EXPLAINAusgabe (nach dem Hinzufügen von Indices) zu Ihrer Frage hinzufügen.
wolfgangwalther
3

Ich gehe davon aus, dass es Probleme mit dem Datenbankdesign geben könnte.

Sie verwenden ein sogenanntes "Entity-Attribute-Value" -Design, das aufgrund seines Designs oft eine schlechte Leistung erbringt.

Haben Sie Vorschläge, um diese Situation besser zu gestalten?

Die klassische relationale Methode, dies zu entwerfen, besteht darin, für jedes Attribut eine eigene Tabelle zu erstellen. In der Regel können Sie diese separaten Tabellen haben: location, gender, bornyear, eyecolor.

Das Folgende hängt davon ab, ob bestimmte Attribute für eine Person immer definiert sind oder nicht. Und ob eine Person nur einen Wert eines Attributs haben kann. Zum Beispiel hat die Person normalerweise nur ein Geschlecht. In Ihrem aktuellen Design hindert Sie nichts daran, drei Zeilen für dieselbe Person mit unterschiedlichen Werten für das Geschlecht hinzuzufügen. Sie können auch einen Geschlechtswert nicht auf 1 oder 2 festlegen, sondern auf eine Zahl, die keinen Sinn ergibt, z. B. 987, und die Datenbank enthält keine Einschränkungen, die dies verhindern würden. Dies ist jedoch ein weiteres Problem bei der Aufrechterhaltung der Datenintegrität beim EAV-Design.

Wenn Sie das Geschlecht der Person immer kennen, ist es wenig sinnvoll, es in einer separaten Tabelle abzulegen, und es ist viel besser, eine Nicht-Null-Spalte GenderIDin der personTabelle zu haben, die ein Fremdschlüssel für die Nachschlagetabelle mit der Liste von wäre alle möglichen Geschlechter und ihre Namen. Wenn Sie das Geschlecht der Person die meiste Zeit, aber nicht immer kennen, können Sie diese Spalte auf null setzen und festlegen, NULLwenn keine Informationen verfügbar sind. Wenn das Geschlecht der Person meistens nicht bekannt ist, ist es möglicherweise besser, eine separate Tabelle zu haben gender, die mit person1: 1 verknüpft ist und nur Zeilen für die Personen enthält, deren Geschlecht bekannt ist.

Ähnliche Überlegungen gelten für eyecolorund bornyear- es ist unwahrscheinlich, dass die Person zwei Werte für ein eyecoloroder hat bornyear.

Wenn es für eine Person möglich ist, mehrere Werte für ein Attribut zu haben, würden Sie es definitiv in eine separate Tabelle stellen. Beispielsweise ist es nicht ungewöhnlich, dass eine Person mehrere Adressen hat (zu Hause, bei der Arbeit, auf dem Postweg, in den Ferien usw.), sodass Sie sie alle in einer Tabelle auflisten location. Tabellen personund locationwürde 1: M verknüpft werden.


Oder nur um die Auswahl oben anzupassen?

Wenn Sie das EAV-Design verwenden, würde ich zumindest Folgendes tun.

  • Set Spalten attribute_type_id, attribute_value, person_idauf NOT NULL.
  • Richten Sie einen Fremdschlüssel ein, der attribute.person_idmit verknüpft ist person.person_id.
  • Erstellen Sie einen Index für drei Spalten (attribute_type_id, attribute_value, person_id). Die Reihenfolge der Spalten ist hier wichtig.
  • Soweit ich weiß, werden Fremdschlüssel von MyISAM nicht berücksichtigt. Verwenden Sie sie also nicht, sondern verwenden Sie stattdessen InnoDB.

Ich würde die Abfrage so schreiben. Verwenden Sie INNERanstelle von LEFTJoins und schreiben Sie explizit eine Unterabfrage für jedes Attribut, um dem Optimierer alle Möglichkeiten zur Verwendung des Index zu geben.

SELECT person.person_id
FROM
    person
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 1
            AND location.attribute_value BETWEEN 3000 AND 7000
    ) AS location ON location.person_id = person.person_id
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 2
            AND location.attribute_value = 1
    ) AS gender ON gender.person_id = person.person_id
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 3
            AND location.attribute_value BETWEEN 1980 AND 2000
    ) AS bornyear ON bornyear.person_id = person.person_id
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 4
            AND location.attribute_value IN (2, 3)
    ) AS eyecolor ON eyecolor.person_id = person.person_id
LIMIT 100000;

Es kann auch sinnvoll sein, die Tabelle nach zu partitionieren .attributeattribute_type_id

Vladimir Baranov
quelle
Leistungsvorsicht: JOIN ( SELECT ... )Optimiert nicht gut. JOINingdirekt am tisch klappt besser (ist aber immer noch problematisch).
Rick James
2

Ich hoffe ich habe eine ausreichende Lösung gefunden. Es ist von diesem Artikel inspiriert .

Kurze Antwort:

  1. Ich habe 1 Tabelle mit allen Attributen erstellt. Eine Spalte für ein Attribut. Plus Primärschlüsselspalte.
  2. Attributwerte werden in Textzellen (für die Volltextsuche) im CSV-ähnlichen Format gespeichert.
  3. Erstellt Volltextindizes. Davor ist es wichtig, ft_min_word_len=1(für MyISAM) im [mysqld]Abschnitt und innodb_ft_min_token_size=1(für InnoDb) in der my.cnfDatei den mysql-Dienst neu zu starten.
  4. Suchbeispiel: SELECT * FROM person_index WHERE MATCH(attribute_1) AGAINST("123 456 789" IN BOOLEAN MODE) LIMIT 1000Wo 123, 456a 789sind IDs, die Personen zugeordnet haben sollen attribute_1. Diese Abfrage dauerte weniger als 1 Sek.

Ausführliche Antwort:

Schritt 1. Tabelle mit Volltextindizes erstellen. InnoDb unterstützt Volltextindizes aus MySQL 5.7. Wenn Sie also 5.5 oder 5.6 verwenden, sollten Sie MyISAM verwenden. Bei der FT-Suche ist es manchmal sogar schneller als bei InnoDb.

CREATE TABLE `person_attribute_ft` (
  `person_id` int(11) NOT NULL,
  `attr_1` text,
  `attr_2` text,
  `attr_3` text,
  `attr_4` text,
  PRIMARY KEY (`person_id`),
  FULLTEXT KEY `attr_1` (`attr_1`),
  FULLTEXT KEY `attr_2` (`attr_2`),
  FULLTEXT KEY `attr_3` (`attr_3`),
  FULLTEXT KEY `attr_4` (`attr_4`),
  FULLTEXT KEY `attr_12` (`attr_1`,`attr_2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

Schritt 2. Fügen Sie Daten aus der EAV-Tabelle (Entity-Attribut-Wert) ein. Zum Beispiel angegeben in Frage kann es mit 1 einfachen SQL erfolgen:

INSERT IGNORE INTO `person_attribute_ft`
SELECT
    p.person_id,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 1 AND a.person_id = p.person_id LIMIT 10) attr_1,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 2 AND a.person_id = p.person_id LIMIT 10) attr_2,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 3 AND a.person_id = p.person_id LIMIT 10) attr_3,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 4 AND a.person_id = p.person_id LIMIT 10) attr_4
FROM person p

Das Ergebnis sollte ungefähr so ​​aussehen:

mysql> select * from person_attribute_ft limit 10;
+-----------+--------+--------+--------+--------+
| person_id | attr_1 | attr_2 | attr_3 | attr_4 |
+-----------+--------+--------+--------+--------+
|         1 | 541    | 2      | 1927   | 3      |
|         2 | 2862   | 2      | 1939   | 4      |
|         3 | 6573   | 2      | 1904   | 2      |
|         4 | 2432   | 1      | 2005   | 2      |
|         5 | 2208   | 1      | 1995   | 4      |
|         6 | 8388   | 2      | 1973   | 1      |
|         7 | 107    | 2      | 1909   | 4      |
|         8 | 5161   | 1      | 2005   | 1      |
|         9 | 8022   | 2      | 1953   | 4      |
|        10 | 4801   | 2      | 1900   | 3      |
+-----------+--------+--------+--------+--------+
10 rows in set (0.00 sec)

Schritt 3. Wählen Sie aus der Tabelle mit der folgenden Abfrage:

mysql> SELECT SQL_NO_CACHE *
    -> FROM `person_attribute_ft`
    -> WHERE 1 AND MATCH(attr_1) AGAINST ("3000 3001 3002 3003 3004 3005 3006 3007" IN BOOLEAN MODE)
    -> AND MATCH(attr_2) AGAINST ("1" IN BOOLEAN MODE)
    -> AND MATCH(attr_3) AGAINST ("1980 1981 1982 1983 1984" IN BOOLEAN MODE)
    -> AND MATCH(attr_4) AGAINST ("2,3" IN BOOLEAN MODE)
    -> LIMIT 10000;
+-----------+--------+--------+--------+--------+
| person_id | attr_1 | attr_2 | attr_3 | attr_4 |
+-----------+--------+--------+--------+--------+
|     12131 | 3002   | 1      | 1982   | 2      |
|     51315 | 3007   | 1      | 1984   | 2      |
|    147283 | 3001   | 1      | 1984   | 2      |
|    350086 | 3005   | 1      | 1982   | 3      |
|    423907 | 3004   | 1      | 1982   | 3      |
... many rows ...
|   9423907 | 3004   | 1      | 1982   | 3      |
|   9461892 | 3007   | 1      | 1982   | 2      |
|   9516361 | 3006   | 1      | 1980   | 2      |
|   9813933 | 3005   | 1      | 1982   | 2      |
|   9986892 | 3003   | 1      | 1981   | 2      |
+-----------+--------+--------+--------+--------+
90 rows in set (0.17 sec)

Die Abfrage wählt alle Zeilen aus:

  • Übereinstimmung mit mindestens einer dieser IDs in attr_1:3000, 3001, 3002, 3003, 3004, 3005, 3006 or 3007
  • UND zur gleichen Zeit passend 1in attr_2(diese Spalte repräsentiert das Geschlecht. Wenn diese Lösung also angepasst wäre, sollte sie smallint(1)mit einem einfachen Index usw. versehen sein.)
  • UND zur gleichen Zeit passend zu mindestens einem von 1980, 1981, 1982, 1983 or 1984inattr_3
  • UND gleichzeitig passend 2oder 3inattr_4

Fazit:

Ich weiß, dass diese Lösung nicht perfekt und ideal für viele Situationen ist, sondern als gute Alternative für die Gestaltung von EAV-Tischen verwendet werden kann.

Ich hoffe es hilft jemandem.

Martin
quelle
1
Ich halte es für sehr unwahrscheinlich, dass dieses Design mit zusammengesetzten Indizes eine bessere Leistung erzielt als Ihr ursprüngliches Design. Welche Tests haben Sie durchgeführt, um sie zu vergleichen?
ypercubeᵀᴹ
0

Versuchen Sie, Abfrageindexhinweise zu verwenden, die angemessen aussehen

MySQL Index Hinweise

Muhammad Muazzam
quelle
1
Hinweise helfen möglicherweise einer Version der Abfrage, schaden dann aber einer anderen. Beachten Sie, dass der Optimierer bornyear als die beste erste Tabelle ausgewählt hat, wahrscheinlich, weil die unerwünschtesten Zeilen herausgefiltert wurden.
Rick James