Domanda

Di tanto in tanto ho incontrato un comportamento MySQL strano. Supponiamo che io ho indici (tipo, rel, creato), (tipo), (rel). La scelta migliore per una query come questa:

SELECT id FROM tbl
WHERE rel = 3 AND type = 3
ORDER BY created;

potrebbe essere quella di utilizzare l'indice (type, rel, created). Ma MySQL decide di indici intersect (type) e (rel), e che porta a perfomance peggiori. Ecco un esempio:

mysql> EXPLAIN
    -> SELECT id FROM tbl
    -> WHERE rel = 3 AND type = 3
    -> ORDER BY created\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl
         type: index_merge
possible_keys: idx_type,idx_rel,idx_rel_type_created
          key: idx_type,idx_rel
      key_len: 1,2
          ref: NULL
         rows: 4343
        Extra: Using intersect(idx_type,idx_rel); Using where; Using filesort

E la stessa query, ma con un pizzico aggiunto:

mysql> EXPLAIN
    -> SELECT id FROM tbl USE INDEX (idx_type_rel_created)
    -> WHERE rel = 3 AND type = 3
    -> ORDER BY created\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl
         type: ref
possible_keys: idx_type_rel_created
          key: idx_type_rel_created
      key_len: 3
          ref: const,const
         rows: 8906
        Extra: Using where

Credo MySQL richiede un piano di esecuzione che contiene meno numero nella colonna "righe" del comando EXPLAIN. Da questo punto di vista, l'indice intersezione con 4343 righe sembra davvero meglio che usare il mio indice combinato con 8906 righe. Quindi, forse il problema è dentro quei numeri?

mysql> SELECT COUNT(*) FROM tbl WHERE type=3 AND rel=3;
+----------+
| COUNT(*) |
+----------+
|     3056 |
+----------+

Da questo si può concludere che MySQL sbaglia a calcolare numero approssimativo di righe per indice combinato.

Quindi, cosa posso fare qui per rendere MySQL prendere il piano di esecuzione giusto?

Non è possibile utilizzare ottimizzatore suggerimenti, perché devo attenersi a Django ORM L'unica soluzione che ho trovato ancora è quello di rimuovere tali indici di un campo.

versione di MySQL è 5.1.49.

La struttura della tabella è:

CREATE TABLE tbl (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` tinyint(1) NOT NULL,
  `rel` smallint(2) NOT NULL,
  `created` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_type` (`type`),
  KEY `idx_rel` (`rel`),
  KEY `idx_type_rel_created` (`type`,`rel`,`created`)
) ENGINE=MyISAM;
È stato utile?

Soluzione

E 'difficile dire esattamente perché sceglie MySQL index_merge_intersection sopra l'indice di scansione, ma si deve notare che con gli indici compositi, le statistiche fino alla colonna data vengono memorizzati per gli indici compositi.

Il valore della information_schema.statistics.cardinality per la type colonna dell'indice composito mostrerà la cardinalità di (rel, type), non type sé.

Se esiste una correlazione tra rel e type, quindi cardinalità (rel, type) sarà inferiore rispetto al prodotto di cardinalità di rel e type presi separatamente dagli indici su colonne corrispondenti.

È per questo che il numero di righe viene calcolato in modo non corretto (un incrocio non può essere più grande in termini di dimensioni di un sindacato).

È possibile proibire index_merge_intersection impostando su OFF in @@optimizer_switch:

SET optimizer_switch = 'index_merge_intersection=off'

Altri suggerimenti

Un'altra cosa che vale la pena ricordare: che non avrebbe il problema se avete cancellato l'indice solo su tipo. l'indice non è necessario poiché duplica una parte dell'indice composito.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top