Bug #26528 | Optimizer produces inefficient execution plan on 5.0.x compared to 4.1.21 | ||
---|---|---|---|
Submitted: | 21 Feb 2007 15:02 | Modified: | 22 Feb 2007 16:21 |
Reporter: | Valeriy Kravchuk | Email Updates: | |
Status: | Not a Bug | Impact on me: | |
Category: | MySQL Server: Optimizer | Severity: | S3 (Non-critical) |
Version: | 5.0.34, 5.0.27 | OS: | Windows (Windows) |
Assigned to: | Igor Babaev | CPU Architecture: | Any |
Tags: | regression |
[21 Feb 2007 15:02]
Valeriy Kravchuk
[22 Feb 2007 5:51]
Igor Babaev
For the query in the subselect (derived table) 4.1 returns the following execution plan: +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | 1 | SIMPLE | H | ALL | idcont,numeroD,index1,index2 | NULL | NULL | NULL | 35111 | Using temporary; Using filesort | | 1 | SIMPLE | P | ref | idetat,mft_refP,ls_conteneur,Id,index7 | idetat | 11 | marfret.H.Id_conteneur | 1 | | | 1 | SIMPLE | R | eq_ref | mft_ref,ref_contrat,index10 | mft_ref | 50 | marfret.P.Mft_refP | 1 | | | 1 | SIMPLE | TY | ALL | NULL | NULL | NULL | NULL | 37 | | | 1 | SIMPLE | RE | ref | index1,idmvtportdtyperef,idmvt,stock | index1 | 4 | marfret.H.Id_mouvement | 1 | Using where; Using index | | 1 | SIMPLE | SE | ref | seqc,seqcp,imex,seqc_imex | seqc_imex | 266 | func,marfret.TY.type | 2 | Using where; Using index | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ The execution by this plan on my machine takes: (18.90 sec) If we change the order of tables in the original query we get a different execution plan: mysql> explain -> select R.type_con as rt ,SE.imex as im, H.Lib_mvt, -> R.type_con,R.contrat as tc, -> TY.fr, H.Type_mvt, H.TypeP, H.TypeD, H.Pv, -> H.etat, H.date_mvt, H.Id_sspP, -> CONCAT(H.Type_mvt, H.TypeP, H.TypeD,H.Pv), -> SE.imex , H.Id_conteneur, H.Id_sspD -> from -> (mft_typemvt as TY, mft_ref as R, mft_passeport as P, mft_sequence SE) -> INNER JOIN -> mft_historique_gilles as H -> ON ((P.Mft_refP=R.mft_ref) -> AND (H.Id_conteneur = P.Id_conteneur ) -> AND TY.type=SE.imex -> AND CONCAT(H.Type_mvt, H.TypeP, H.TypeD, H.Pv) = SE.sequencec -> ) -> left outer join -> (mft_refhisto as RE) -> ON ((RE.id_mouvement=H.Id_mouvement)) -> where (((H.numeroD="4499" ) OR ( RE.portD="FRLEH" )) AND SE.imex !="") -> group by H.id_conteneur; +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | 1 | SIMPLE | TY | ALL | NULL | NULL | NULL | NULL | 37 | Using temporary; Using filesort | | 1 | SIMPLE | H | ALL | idcont,numeroD,index1,index2 | NULL | NULL | NULL | 35111 | | | 1 | SIMPLE | P | ref | idetat,mft_refP,ls_conteneur,Id,index7 | idetat | 11 | marfret.H.Id_conteneur | 1 | | | 1 | SIMPLE | R | eq_ref | mft_ref,ref_contrat,index10 | mft_ref | 50 | marfret.P.Mft_refP | 1 | | | 1 | SIMPLE | RE | ref | index1,idmvtportdtyperef,idmvt,stock | index1 | 4 | marfret.H.Id_mouvement | 1 | Using where; Using index | | 1 | SIMPLE | SE | ref | seqc,seqcp,imex,seqc_imex | seqc_imex | 266 | func,marfret.TY.type | 2 | Using where; Using index | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ This plan is executed for: (1 min 32.90 sec) This happens because the optimizer does not evaluate all possible execution plans and as a result sometimes it misses the fastest ones. See continuation in the next comment.
[22 Feb 2007 5:52]
Igor Babaev
5.1 is smarter in this respect. We can force the optimizer to evaluate all possible plans by setting the option optimizer_prune_level to 0. By default it's value is set to 1 and with it 5.1 returns the following execution plan for our original query: +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | 1 | SIMPLE | TY | ALL | NULL | NULL | NULL | NULL | 37 | Using temporary; Using filesort | | 1 | SIMPLE | H | ALL | idcont,numeroD,index1,index2 | NULL | NULL | NULL | 35213 | | | 1 | SIMPLE | P | ref | idetat,mft_refP,ls_conteneur,Id,index7 | idetat | 13 | marfret.H.Id_conteneur | 1 | | | 1 | SIMPLE | R | eq_ref | mft_ref,ref_contrat,index10 | mft_ref | 52 | marfret.P.Mft_refP | 1 | | | 1 | SIMPLE | RE | ref | index1,idmvtportdtyperef,idmvt,stock | index1 | 4 | marfret.H.Id_mouvement | 1 | Using where; Using index | | 1 | SIMPLE | SE | ref | seqc,seqcp,imex,seqc_imex | seqc_imex | 270 | func,marfret.TY.type | 2 | Using where; Using index | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ The plan is executed for: (2 min 3.17 sec) If I change the value of optimizer_prune_level: mysql> select @@optimizer_prune_level; +-------------------------+ | @@optimizer_prune_level | +-------------------------+ | 1 | +-------------------------+ 1 row in set (0.00 sec) mysql> set @@optimizer_prune_level=0; Query OK, 0 rows affected (0.00 sec) the optimizer returns a much better plan: +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | 1 | SIMPLE | H | ALL | idcont,numeroD,index1,index2 | NULL | NULL | NULL | 35213 | Using temporary; Using filesort | | 1 | SIMPLE | P | ref | idetat,mft_refP,ls_conteneur,Id,index7 | idetat | 13 | marfret.H.Id_conteneur | 1 | | | 1 | SIMPLE | RE | ref | index1,idmvtportdtyperef,idmvt,stock | index1 | 4 | marfret.H.Id_mouvement | 1 | Using where; Using index | | 1 | SIMPLE | TY | ALL | NULL | NULL | NULL | NULL | 37 | | | 1 | SIMPLE | SE | ref | seqc,seqcp,imex,seqc_imex | seqc_imex | 270 | func,marfret.TY.type | 2 | Using where; Using index | | 1 | SIMPLE | R | eq_ref | mft_ref,ref_contrat,index10 | mft_ref | 52 | marfret.P.Mft_refP | 1 | | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ whose execution time is: (5.58 sec) And the choice of this plan does not depend on the order of tables in the query: mysql> explain -> select R.type_con as rt ,SE.imex as im, H.Lib_mvt, -> R.type_con,R.contrat as tc, -> TY.fr, H.Type_mvt, H.TypeP, H.TypeD, H.Pv, -> H.etat, H.date_mvt, H.Id_sspP, -> CONCAT(H.Type_mvt, H.TypeP, H.TypeD,H.Pv), -> SE.imex , H.Id_conteneur, H.Id_sspD -> from -> (mft_ref as R, mft_passeport as P, mft_sequence SE, mft_typemvt as TY) -> INNER JOIN -> mft_historique_gilles as H -> ON ((P.Mft_refP=R.mft_ref) -> AND (H.Id_conteneur = P.Id_conteneur ) -> AND TY.type=SE.imex -> AND CONCAT(H.Type_mvt, H.TypeP, H.TypeD, H.Pv) = SE.sequencec -> ) -> left outer join -> (mft_refhisto as RE) -> ON ((RE.id_mouvement=H.Id_mouvement)) -> where (((H.numeroD="4499" ) OR ( RE.portD="FRLEH" )) AND SE.imex !="") -> group by H.id_conteneur; +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ | 1 | SIMPLE | H | ALL | idcont,numeroD,index1,index2 | NULL | NULL | NULL | 35213 | Using temporary; Using filesort | | 1 | SIMPLE | P | ref | idetat,mft_refP,ls_conteneur,Id,index7 | idetat | 13 | marfret.H.Id_conteneur | 1 | | | 1 | SIMPLE | RE | ref | index1,idmvtportdtyperef,idmvt,stock | index1 | 4 | marfret.H.Id_mouvement | 1 | Using where; Using index | | 1 | SIMPLE | TY | ALL | NULL | NULL | NULL | NULL | 37 | | | 1 | SIMPLE | SE | ref | seqc,seqcp,imex,seqc_imex | seqc_imex | 270 | func,marfret.TY.type | 2 | Using where; Using index | | 1 | SIMPLE | R | eq_ref | mft_ref,ref_contrat,index10 | mft_ref | 52 | marfret.P.Mft_refP | 1 | | +----+-------------+-------+--------+----------------------------------------+-----------+---------+------------------------+-------+---------------------------------+ Conclusion: there is no performance degradation for the customer's query. This is not a bug.
[22 Feb 2007 16:37]
Igor Babaev
Correction for my previous [open] comment: the option optimizer_prune_level is valid for 5.0+ and I checked the queries I referred to with 5.0 (never checked them with 5.1, but nothing changed in this functionality).