Perché sarebbe MySQL utilizzare indice di un'intersezione al posto dell'indice combinato?
-
13-10-2019 - |
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;
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.