MySQL view esegue sempre la scansione della tabella completa?
-
10-07-2019 - |
Domanda
Sto cercando di ottimizzare una query che utilizza una vista in MySQL 5.1. Sembra che anche se seleziono 1 colonna dalla vista esegua sempre una scansione completa della tabella. È questo il comportamento previsto?
La vista è solo un SELEZIONA " Tutte le colonne da queste tabelle - NON * " per le tabelle che ho specificato nella prima query di seguito.
Questo è il mio spiegazione dell'output di quando seleziono la colonna indicizzata PromotionID dalla query che costituisce la vista. Come puoi vedere, è molto diverso dall'output nella vista.
EXPLAIN SELECT pb.PromotionID FROM PromotionBase pb INNER JOIN PromotionCart pct ON pb.PromotionID = pct.PromotionID INNER JOIN PromotionCode pc ON pb.PromotionID = pc.PromotionID WHERE pc.PromotionCode = '5TAFF312C0NT'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: pc
type: const
possible_keys: PRIMARY,fk_pc_pb
key: PRIMARY
key_len: 302
ref: const
rows: 1
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: pb
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: const
rows: 1
Extra: Using index
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: pct
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: const
rows: 1
Extra: Using index
3 rows in set (0.00 sec)
L'output quando seleziono la stessa cosa ma dalla vista
EXPLAIN SELECT vpc.PromotionID FROM vw_PromotionCode vpc WHERE vpc.PromotionCode = '5TAFF312C0NT'\G;
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 5830
Extra: Using where
*************************** 2. row ***************************
id: 2
select_type: DERIVED
table: pcart
type: index
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
rows: 33
Extra: Using index
*************************** 3. row ***************************
id: 2
select_type: DERIVED
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.pcart.PromotionID
rows: 1
Extra:
*************************** 4. row ***************************
id: 2
select_type: DERIVED
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 5. row ***************************
id: 3
select_type: UNION
table: pp
type: index
possible_keys: PRIMARY
key: pp_p
key_len: 4
ref: NULL
rows: 1
Extra: Using index
*************************** 6. row ***************************
id: 3
select_type: UNION
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.pp.PromotionID
rows: 1
Extra:
*************************** 7. row ***************************
id: 3
select_type: UNION
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 8. row ***************************
id: 4
select_type: UNION
table: pcp
type: index
possible_keys: PRIMARY
key: pcp_cp
key_len: 4
ref: NULL
rows: 1
Extra: Using index
*************************** 9. row ***************************
id: 4
select_type: UNION
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.pcp.PromotionID
rows: 1
Extra:
*************************** 10. row ***************************
id: 4
select_type: UNION
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 11. row ***************************
id: 5
select_type: UNION
table: ppc
type: index
possible_keys: PRIMARY
key: ppc_pc
key_len: 4
ref: NULL
rows: 1
Extra: Using index
*************************** 12. row ***************************
id: 5
select_type: UNION
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.ppc.PromotionID
rows: 1
Extra:
*************************** 13. row ***************************
id: 5
select_type: UNION
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 14. row ***************************
id: 6
select_type: UNION
table: ppt
type: index
possible_keys: PRIMARY
key: ppt_pt
key_len: 4
ref: NULL
rows: 1
Extra: Using index
*************************** 15. row ***************************
id: 6
select_type: UNION
table: pb
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: readyinteractive.ppt.PromotionID
rows: 1
Extra:
*************************** 16. row ***************************
id: 6
select_type: UNION
table: pc
type: ref
possible_keys: fk_pc_pb
key: fk_pc_pb
key_len: 4
ref: readyinteractive.pb.PromotionID
rows: 249
Extra: Using where
*************************** 17. row ***************************
id: NULL
select_type: UNION RESULT
table: <union2,3,4,5,6>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
Extra:
17 rows in set (0.18 sec)
Soluzione
Le viste in MySQL non sono indicizzate, quindi per loro stessa natura richiedono una scansione completa ogni volta che si accede. In generale, ciò rende Views davvero utile solo per le situazioni in cui si ha una query statica abbastanza complessa che restituisce un set di risultati ridotto e si prevede di afferrare l'intero set di risultati ogni volta.
Modifica: Naturalmente le Viste utilizzeranno gli indici sulle tabelle sottostanti in modo che la Vista stessa sia ottimizzata (altrimenti non avrebbero alcun senso da usare) ma perché non ci sono indici su una vista non è possibile ottimizzare una query WHERE sulla vista.
La costruzione di indici per Views sarebbe comunque costosa perché, anche se non ho provato a profilare alcuna Views, sono abbastanza certo che una tabella temporanea è costruita dietro le quinte e quindi il set di risultati è tornato. Ci vuole già un sacco di tempo per costruire la tabella temporanea, non vorrei una vista che provasse anche a indovinare quali indici sono necessari. Il che fa apparire il secondo punto, ovvero che MySQL non offre attualmente un metodo per specificare quali indici utilizzare per una vista, quindi come fa a sapere quali campi devono essere indicizzati? Indovina in base alla tua query?
Puoi prendere in considerazione l'utilizzo di una Tabella temporanea perché quindi è possibile specificare gli indici sui campi nella tabella temporanea. Tuttavia, per esperienza, questo tende ad essere molto, molto lento.
Se tutta questa vista contiene è SELEZIONA TUTTO DA tabella1, tabella2, tabella3; allora dovrei chiedere perché questa query deve essere in una vista a tutti? Se per qualche motivo è assolutamente necessario, è possibile utilizzare una procedura memorizzata per incapsulare la query poiché sarà quindi possibile ottenere prestazioni ottimizzate mantenendo il vantaggio di una chiamata più semplice al database per il set di risultati.
Altri suggerimenti
Ho approfondito la questione e ho perso un punto chiave delle informazioni :( La mia query di visualizzazione in realtà ha un'unione con un'altra tabella. Questo sta facendo sì che la vista usi l'algoritmo TEMPORARY TABLE invece dell'algoritmo MERGE.
L'algoritmo TEMPORARY TABLE non consente l'uso di indici nelle tabelle sottostanti.
Questo sembra essere un bug in MySQL ed è stato segnalato nel 2006, ma non sembra che sia stato risolto nel 2009! http://forums.mysql.com/read.php?100.56681, 56681
Sembra che dovrò riscrivere la query come join esterno.