Umgang mit temporären Tabellen, wenn ich keine Kontrolle über DB-Variablen habe

7

Ich habe keine Kontrolle über Dinge wie tmp_table_sizeund max_heap_table_size, und wenn unsere Tabellen wachsen, wächst die Zeit, die Abfragen benötigen, die temporäre Tabellen erfordern, geometrisch.

Ich frage mich, ob es eine Möglichkeit gibt, MySQL daran zu hindern, temporäre Tabellen für diese Abfragen zu verwenden. Was wäre der beste Ansatz in dieser Situation:

Hier ist ein Beispiel für den größten Täter:

SELECT `skills`.`id`
FROM (`jobs_skills`)
JOIN `jobs` ON (`jobs`.`id` = `jobs_skills`.`job_id`)
JOIN `skills` ON (`skills`.`id` = `jobs_skills`.`skill_id`)
WHERE `jobs`.`job_visibility_id` = 1
AND `jobs`.`active` = 1
AND `skills`.`valid` = 1
AND `jobs_skills`.`skill_id` IN (96,101,103,108,121,2610,99,119,2607,102,104,112,113,122,1032,1488,2608,109,126,1438,2310,2318,2622,118,1046,1387,2609,100,116,123,2611,2612,2616,2618,114,127,1562,1587,1608,2276,2615,125,1070,1071,1161,1658,2613,2614,2617,105,110,111,120,1394,1435)
GROUP BY `jobs_skills`.`job_id`

Dies copying to temp tabledauerte 107 Sekunden, 99% der gesamten Abfragezeit.

Trotz der Befürchtungen des Dr.-Syndroms biete ich an. . .

MEHR DETAILS

Hier ist die EXPLAINAnweisung für die Abfrage:

+----+-------------+-------------+--------+----------------------+--------------+---------+----------------------------------+--------+----------------------------------------------+
| id | select_type | table       | type   | possible_keys        | key          | key_len | ref                              | rows   | Extra                                        |
+----+-------------+-------------+--------+----------------------+--------------+---------+----------------------------------+--------+----------------------------------------------+
|  1 | SIMPLE      | jobs        | ref    | PRIMARY,active_index | active_index | 1       | const                            | 468958 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | jobs_skills | ref    | PRIMARY              | PRIMARY      | 4       | 557574_prod.jobs.id              |      1 | Using where; Using index                     |
|  1 | SIMPLE      | skills      | eq_ref | PRIMARY              | PRIMARY      | 4       | 557574_prod.jobs_skills.skill_id |      1 | Using where                                  |
+----+-------------+-------------+--------+----------------------+--------------+---------+----------------------------------+--------+----------------------------------------------+

und hier sind die CREATE TABLEAussagen für die relevanten Tabellen:

| jobs  | CREATE TABLE `jobs` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `user_id` int(10) unsigned NOT NULL,
  `title` varchar(40) NOT NULL,
  `description` text NOT NULL,
  `address_id` int(10) unsigned NOT NULL,
  `proximity` smallint(3) unsigned NOT NULL default '15',
  `job_payrate_id` tinyint(1) unsigned NOT NULL default '1',
  `payrate` int(10) unsigned NOT NULL,
  `start_date` int(10) unsigned NOT NULL,
  `job_start_id` tinyint(1) unsigned NOT NULL default '1',
  `duration` tinyint(1) unsigned NOT NULL COMMENT 'Full-time, Part-time, Flexible',
  `posting_date` int(10) unsigned NOT NULL,
  `revision_date` int(10) unsigned NOT NULL,
  `expiration` int(10) unsigned NOT NULL,
  `active` tinyint(1) unsigned NOT NULL default '1',
  `team_size` tinyint(2) unsigned NOT NULL default '1',
  `job_type_id` tinyint(1) unsigned NOT NULL default '1',
  `job_shift_id` tinyint(1) unsigned NOT NULL default '1',
  `job_visibility_id` tinyint(1) unsigned NOT NULL default '1',
  `position_count` smallint(5) unsigned NOT NULL default '1',
  `impressions` int(10) unsigned NOT NULL default '0',
  `clicks` int(10) unsigned NOT NULL default '0',
  `employer_email` varchar(100) NOT NULL default '',
  `job_source_id` smallint(6) unsigned NOT NULL default '0',
  `job_password` varchar(50) NOT NULL default '',
  PRIMARY KEY  (`id`),
  KEY `active_index` (`active`),
  KEY `user_id_index` (`user_id`),
  KEY `address_id_index` (`address_id`),
  KEY `posting_date_index` USING BTREE (`posting_date`)
) ENGINE=InnoDB AUTO_INCREMENT=875013 DEFAULT CHARSET=utf8

- -

| jobs_skills | CREATE TABLE `jobs_skills` (
  `job_id` int(10) unsigned NOT NULL,
  `skill_id` int(10) unsigned NOT NULL,
  `required` tinyint(1) unsigned NOT NULL,
  PRIMARY KEY  (`job_id`,`skill_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

- -

| skills | CREATE TABLE `skills` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `parent_id` int(10) unsigned NOT NULL,
  `name` varchar(35) NOT NULL default '',
  `description` varchar(250) NOT NULL,
  `valid` tinyint(1) unsigned NOT NULL default '0',
  `is_category` tinyint(1) unsigned NOT NULL default '0',
  `last_edited` int(10) unsigned NOT NULL default '0',
  `impressions` int(10) unsigned NOT NULL default '0',
  `clicks` int(10) unsigned NOT NULL default '0',
  `jobs` int(10) unsigned NOT NULL default '0',
  PRIMARY KEY  (`id`),
  KEY `name` (`name`),
  KEY `parent` (`parent_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2657 DEFAULT CHARSET=utf8 |

Wie ich bereits sagte, ist dies nicht die einzige Abfrage mit diesem Problem. Daher wäre jeder allgemeine Rat am hilfreichsten, obwohl ich keinen für diese Abfrage spezifischen Rat ablehnen werde.

JIStone
quelle
Dies ist tatsächlich eine sehr gute Entwicklerfrage, über die andere möglicherweise Bescheid wissen müssen, um Einschränkungen durch Hosting-Unternehmen oder leitende Datenbankadministratoren zu umgehen. +1 auf diese Frage !!!
RolandoMySQLDBA

Antworten:

5

Ihre ursprüngliche Unterabfrage:

SELECT `skills`.`id`
FROM (`jobs_skills`)
JOIN `jobs` ON (`jobs`.`id` = `jobs_skills`.`job_id`)
JOIN `skills` ON (`skills`.`id` = `jobs_skills`.`skill_id`)
WHERE `jobs`.`job_visibility_id` = 1
AND `jobs`.`active` = 1
AND `skills`.`valid` = 1
AND `jobs_skills`.`skill_id` IN (96,101,103,108,121,2610,99,119,2607,102,104,112,113,122,1032,1488,2608,109,126,1438,2310,2318,2622,118,1046,1387,2609,100,116,123,2611,2612,2616,2618,114,127,1562,1587,1608,2276,2615,125,1070,1071,1161,1658,2613,2614,2617,105,110,111,120,1394,1435)
GROUP BY `jobs_skills`.`job_id`

Sie müssen die Abfrage so rafaktorisieren, dass Sie die zu erstellenden temporären Tabellen und ihre Größe steuern und mikromanagen. Basierend auf den Klauseln JOIN, WHERE und GROUP BY müssen Sie die folgenden Änderungen implementieren:

Jobs müssen auf job_visibility_id, active, id indiziert werden

Benötigte Unterabfrage

(SELECT id job_id FROM jobs WHERE job_visibility_id=1 AND active=1 ORDER BY id)

Fähigkeiten müssen auf gültige ID indiziert werden

Benötigte Unterabfrage

(SELECT id skill_id FROM skills WHERE valid=1 ORDER BY id)

jobs_skills muss auf Skill_id, Job_id indiziert werden

Benötigte Unterabfrage

(SELECT job_id FROM jobs_skills WHERE skill_id IN (96,101,103,108,121,2610,99,119,2607,102,104,112,113,122,1032,1488,2608,109,126,1438,2310,2318,2622,118,1046,1387,2609,100,116,123,2611,2612,2616,2618,114,127,1562,1587,1608,2276,2615,125,1070,1071,1161,1658,2613,2614,2617,105,110,111,120,1394,1435) ORDER BY skill_id,job_id)

SQL zum Erstellen der erforderlichen Indizes

ALTER TABLE jobs ADD INDEX (job_visibility_id,active,id);
ALTER TABLE skills ADD INDEX (valid,id);
ALTER TABLE jobs_skills ADD INDEX (skill_id,job_id);

Kombinieren Sie nun die Unterabfragen zu VOLTRON

SELECT skill_id
FROM (SELECT JS.*
FROM (SELECT skill_id,job_id FROM jobs_skills WHERE skill_id IN (96,101,103,108,121,2610,99,119,2607,102,104,112,113,122,1032,1488,2608,109,126,1438,2310,2318,2622,118,1046,1387,2609,100,116,123,2611,2612,2616,2618,114,127,1562,1587,1608,2276,2615,125,1070,1071,1161,1658,2613,2614,2617,105,110,111,120,1394,1435) ORDER BY skill_id,job_id) JS
INNER JOIN
(SELECT id job_id FROM jobs WHERE job_visibility_id=1 AND active=1 ORDER BY id) J
USING (job_id) INNER JOIN
(SELECT id skill_id FROM skills WHERE valid=1 ORDER BY id) S USING (skill_id)
) A
GROUP BY job_id;

Versuche es !!!

Übrigens, wenn die Syntax falsch ist, werde ich versuchen, sie anzupassen !!!

RolandoMySQLDBA
quelle
fantastisch - Ich kann die zugrunde liegende Methode zum Erstellen dieser Indizes / Unterabfragen sehen und sie auf die anderen problemverursachenden Abfragen anwenden. Vielen Dank!
JIStone
@RolandMySQLDBA 'Kombiniere jetzt die Unterabfragen zu VOLTRON' lol ... Danke für den Humor. Machte meinen Tag ...
StanleyJohns
Ich denke, wenn Sie die Abfrage nur gezwungen hätten, den PRIMARY-Index für die Jobtabelle zu verwenden, hätte dies ebenfalls geholfen, JOIN jobs FORCE INDEX (PRIMARY), da das Problem, wie aus der EXPLAIN-Ausgabe hervorgeht, in der Tatsache liegt, dass MySQL wählt nicht den optimalen Index, wenn Zeilen aus der Jobtabelle gefiltert werden. Sie können auch STRAIGHT_JOIN verwenden, um MySQL zu zwingen, Ihrem Join-Plan zu folgen.
ovais.tariq
@ ovais.tariq FORCE INDEX hilft in den meisten Fällen nicht immer, da das MySQL Query Optimizer die schmutzige Angewohnheit hat, Indexhinweise zu optimieren, da Zeilen, Vorschläge und sogar LIMIT-Klauseln in Luft aufgehen ( dba.stackexchange.com/questions) / 1371 /… ). Die Vorteile von FORCE INDEX werden auch beeinträchtigt, wenn zuerst JOIN-Klauseln ausgeführt werden und dann WHERE-Klauseln auf große temporäre Tabellen angewendet werden (die niemals indiziert werden).
RolandoMySQLDBA
@RolandMySQLDBA., Ja, Verknüpfungen werden zuerst und in der Reihenfolge von links nach rechts durchgeführt. Indizes helfen dem Optimierer jedoch, zuerst die richtige Tabelle auszuwählen. Angenommen, Sie haben 3 Tabellen, t1, t2 und t3. Sie würden in der Reihenfolge t3xt2xt1 verbunden, wenn t3 nach dem Index gefiltert werden kann, um die geringste Anzahl von Zeilen zu haben. Durch die Verwendung des Kraftindex stellen Sie also sicher, dass der Optimierer auf optimierte Weise von links nach rechts verbunden wird.
ovais.tariq