From: Ole John Aske Date: February 23 2011 3:54pm Subject: bzr commit into mysql-5.1-telco-7.0 branch (ole.john.aske:4223) Bug#11804277 List-Archive: http://lists.mysql.com/commits/131962 X-Bug: 11804277 Message-Id: <20110223155418.75BD8223@fimafeng09.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1282955185738021942==" --===============1282955185738021942== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///net/fimafeng09/export/home/tmp/oleja/mysql/mysql-5.1-telco-7.0/ based on revid:ole.john.aske@stripped 4223 Ole John Aske 2011-02-23 Fix for bug#11804277 - INCORRECT INDEX MAY BE SELECTED DUE TO INSUFFICIENT STATISTICS FROM CLUSTER Add heuristics to ha_ndbcluster::records_in_range() which identifies a range as: - An open bound range ( LT/GT BETWEEN AND ) - A (partial) EQ-range ( EQ ) ... Or a combination of these.... These are handled as follows: Open bound ranges ----------------- Without a histogram of how the values in the index are distributed, we can only assume an equal distrubution. A statistically correct estimate for a condition of the form ' LT/GT ' would then have been to assume it selects 50% of the rows in the table. However, I have experienced that this will cause the range-cost to directly compete with the cost of a full table scan. We should therefore be somewhat more conservative and estimate 10% of the rows to be returned. Closed bound range ------------------ We assume this to be somewhat better than an open bounded range returning 5% of the rows in the table. EQ-range -------- An EQ-range will excatly specify a fraction of the first part of an index. It is reasonable to assume: - Specifing a larger fraction of the index will improve the selectivity of the EQ-range. - Each part of the specified EQ-range will have the same selectivity. We can model this as a Binomial Distribution of the indexed values. http://en.wikipedia.org/wiki/Binomial_distrib modified: mysql-test/suite/ndb/r/ndb_condition_pushdown.result mysql-test/suite/ndb/r/ndb_index.result mysql-test/suite/ndb/r/ndb_index_unique.result mysql-test/suite/ndb/r/ndb_read_multi_range.result mysql-test/suite/ndb/r/ndb_statistics.result mysql-test/suite/ndb/t/ndb_statistics.test sql/ha_ndbcluster.cc === modified file 'mysql-test/suite/ndb/r/ndb_condition_pushdown.result' --- a/mysql-test/suite/ndb/r/ndb_condition_pushdown.result 2011-01-17 13:29:52 +0000 +++ b/mysql-test/suite/ndb/r/ndb_condition_pushdown.result 2011-02-23 15:54:12 +0000 @@ -1910,7 +1910,7 @@ insert into NodeAlias VALUES(null, 8 , ' 12:22:26'); explain select * from NodeAlias where (aliasKey LIKE '491803%'); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NodeAlias range NodeAlias_KeyIndex NodeAlias_KeyIndex 48 NULL 10 Using where with pushed condition +1 SIMPLE NodeAlias range NodeAlias_KeyIndex NodeAlias_KeyIndex 48 NULL 2 Using where with pushed condition select * from NodeAlias where (aliasKey LIKE '491803%') order by id; id nodeId displayName aliasKey objectVersion changed 7 8 491803% 491803% 0 2008-03-10 12:22:26 @@ -2225,7 +2225,7 @@ join tx as t2 on tx.a = t2.c and tx.b = where t2.a = 4 group by t2.c; id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 10 100.00 Using where; Using filesort +1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 2 100.00 Using where; Using filesort 1 SIMPLE tx eq_ref PRIMARY PRIMARY 8 test.t2.c,test.t2.d 1 100.00 Warnings: Note 1003 select `test`.`t2`.`c` AS `c`,count(distinct `test`.`t2`.`a`) AS `count(distinct t2.a)` from `test`.`tx` join `test`.`tx` `t2` where ((`test`.`tx`.`b` = `test`.`t2`.`d`) and (`test`.`tx`.`a` = `test`.`t2`.`c`) and (`test`.`t2`.`a` = 4)) group by `test`.`t2`.`c` @@ -2242,7 +2242,7 @@ join tx as t2 on tx.a = t2.c and tx.b = where t2.a = 4 group by t2.c; id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 10 100.00 Using where; Using filesort +1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 2 100.00 Using where; Using filesort 1 SIMPLE tx eq_ref PRIMARY PRIMARY 8 test.t2.c,test.t2.d 1 100.00 Warnings: Note 1003 select `test`.`t2`.`c` AS `c`,count(distinct `test`.`t2`.`a`) AS `count(distinct t2.a)` from `test`.`tx` join `test`.`tx` `t2` where ((`test`.`tx`.`b` = `test`.`t2`.`d`) and (`test`.`tx`.`a` = `test`.`t2`.`c`) and (`test`.`t2`.`a` = 4)) group by `test`.`t2`.`c` === modified file 'mysql-test/suite/ndb/r/ndb_index.result' --- a/mysql-test/suite/ndb/r/ndb_index.result 2010-12-22 11:13:45 +0000 +++ b/mysql-test/suite/ndb/r/ndb_index.result 2011-02-23 15:54:12 +0000 @@ -306,7 +306,7 @@ explain select i,vc from t1 where i>=1 or vc > '0'; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index_merge PRIMARY,i1,i2 i1,i2 5,18 NULL 20 Using sort_union(i1,i2); Using where with pushed condition +1 SIMPLE t1 index_merge PRIMARY,i1,i2 i1,i2 5,18 NULL 4 Using sort_union(i1,i2); Using where with pushed condition select i,vc from t1 where i>=1 or vc > '0'; i vc @@ -350,7 +350,7 @@ explain select i,vc from t2 where i>=1 or vc > '0'; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 index_merge i1,i2 i1,i2 5,19 NULL 20 Using sort_union(i1,i2); Using where with pushed condition +1 SIMPLE t2 index_merge i1,i2 i1,i2 5,19 NULL 4 Using sort_union(i1,i2); Using where with pushed condition select i,vc from t2 where i>=1 or vc > '0'; i vc === modified file 'mysql-test/suite/ndb/r/ndb_index_unique.result' --- a/mysql-test/suite/ndb/r/ndb_index_unique.result 2011-01-18 07:49:14 +0000 +++ b/mysql-test/suite/ndb/r/ndb_index_unique.result 2011-02-23 15:54:12 +0000 @@ -185,7 +185,7 @@ set @old_ecpd = @@session.engine_conditi set engine_condition_pushdown = true; explain select * from t2 where (b = 3 OR b = 5) AND c IS NULL AND a < 9 order by a; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 range PRIMARY,b b 9 NULL 2 Using where with pushed condition; Using filesort +1 SIMPLE t2 range PRIMARY,b PRIMARY 4 NULL 2 Using where with pushed condition select * from t2 where (b = 3 OR b = 5) AND c IS NULL AND a < 9 order by a; a b c 3 3 NULL === modified file 'mysql-test/suite/ndb/r/ndb_read_multi_range.result' --- a/mysql-test/suite/ndb/r/ndb_read_multi_range.result 2011-01-18 07:49:14 +0000 +++ b/mysql-test/suite/ndb/r/ndb_read_multi_range.result 2011-02-23 15:54:12 +0000 @@ -605,7 +605,7 @@ SELECT DISTINCT STRAIGHT_JOIN t1.pk FROM t1 LEFT JOIN t2 ON t2.a = t1.a AND t2.pk != 6; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3000 Using temporary -1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 20 Using where; Distinct +1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 4 Using where; Distinct SELECT DISTINCT STRAIGHT_JOIN t1.pk FROM t1 LEFT JOIN t2 ON t2.a = t1.a AND t2.pk != 6; drop table t1, t2; === modified file 'mysql-test/suite/ndb/r/ndb_statistics.result' --- a/mysql-test/suite/ndb/r/ndb_statistics.result 2011-01-18 11:49:03 +0000 +++ b/mysql-test/suite/ndb/r/ndb_statistics.result 2011-02-23 15:54:12 +0000 @@ -38,24 +38,124 @@ id select_type table type possible_keys EXPLAIN SELECT * FROM t10000 WHERE k >= 42 and k < 10000; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 10 Using where with pushed condition +1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 500 Using where with pushed condition EXPLAIN SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 10 Using where with pushed condition +1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 500 Using where with pushed condition EXPLAIN SELECT * FROM t10000 WHERE k < 42; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 10 Using where with pushed condition +1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 1000 Using where with pushed condition EXPLAIN SELECT * FROM t10000 WHERE k > 42; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 10 Using where with pushed condition +1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 1000 Using where with pushed condition EXPLAIN SELECT * FROM t10000 AS X JOIN t10000 AS Y ON Y.I=X.I AND Y.J = X.I; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE X ALL I NULL NULL NULL 10000 1 SIMPLE Y ref J,I I 10 test.X.I,test.X.I 11 Using where +EXPLAIN +SELECT * FROM t100 WHERE k < 42; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t100 range PRIMARY PRIMARY 4 NULL 10 Using where with pushed condition +EXPLAIN +SELECT * FROM t100 WHERE k > 42; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t100 range PRIMARY PRIMARY 4 NULL 10 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE k < 42; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 1000 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE k > 42; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 1000 Using where with pushed condition +EXPLAIN +SELECT * FROM t100 WHERE k BETWEEN 42 AND 10000; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t100 range PRIMARY PRIMARY 4 NULL 5 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range PRIMARY PRIMARY 4 NULL 500 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE I = 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 ref I I 5 const 200 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE J = 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 ref J J 5 const 100 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J = 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 ref J,I I 10 const,const 4 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE I = 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 ref I I 5 const 200 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J > 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range J,I I 10 NULL 100 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J < 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range J,I I 10 NULL 50 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J BETWEEN 1 AND 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range J,I I 10 NULL 50 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 ref J,I I 10 const,const 4 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE J = 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 ref J J 5 const 100 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE J = 0 AND K > 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range PRIMARY,J J 9 NULL 50 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE J = 0 AND K < 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range PRIMARY,J J 9 NULL 50 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE J = 0 AND K BETWEEN 1 AND 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range PRIMARY,J J 9 NULL 25 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE J = 0 AND K = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J > 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range J,I I 10 NULL 100 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE I > 0 AND J = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 ref J,I J 5 const 100 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE I > 0 AND J > 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range J,I J 5 NULL 1000 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE J > 1 AND I = 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range J,I I 10 NULL 100 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE J = 1 AND I > 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 ref J,I J 5 const 100 Using where with pushed condition +EXPLAIN +SELECT * FROM t10000 WHERE J > 1 AND I > 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10000 range J,I J 5 NULL 1000 Using where with pushed condition DROP TABLE t10,t100,t10000; End of 5.1 tests === modified file 'mysql-test/suite/ndb/t/ndb_statistics.test' --- a/mysql-test/suite/ndb/t/ndb_statistics.test 2011-01-18 11:49:03 +0000 +++ b/mysql-test/suite/ndb/t/ndb_statistics.test 2011-02-23 15:54:12 +0000 @@ -62,6 +62,81 @@ EXPLAIN SELECT * FROM t10000 AS X JOIN t10000 AS Y ON Y.I=X.I AND Y.J = X.I; +# +# Improved heurists for ::records_in_range() statistics +# + +# Open bounded range should return 10% of #rows in table +EXPLAIN +SELECT * FROM t100 WHERE k < 42; +EXPLAIN +SELECT * FROM t100 WHERE k > 42; +EXPLAIN +SELECT * FROM t10000 WHERE k < 42; +EXPLAIN +SELECT * FROM t10000 WHERE k > 42; + +#Closed bounded range should return 5% of #rows in table +EXPLAIN +SELECT * FROM t100 WHERE k BETWEEN 42 AND 10000; +EXPLAIN +SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000; + +#EQ-range selectivity depends on +# - key length specified +# - #rows in table. +# - unique/non-unique index +# - min 2% selectivity +# +# Possibly combined with open/closed ranges as +# above which further improves selectivity +# +EXPLAIN +SELECT * FROM t10000 WHERE I = 0; +EXPLAIN +SELECT * FROM t10000 WHERE J = 0; + +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J = 0; + +EXPLAIN +SELECT * FROM t10000 WHERE I = 0; +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J > 1; +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J < 1; +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J BETWEEN 1 AND 10; +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J = 1; + +EXPLAIN +SELECT * FROM t10000 WHERE J = 0; +EXPLAIN +SELECT * FROM t10000 WHERE J = 0 AND K > 1; +EXPLAIN +SELECT * FROM t10000 WHERE J = 0 AND K < 1; +EXPLAIN +SELECT * FROM t10000 WHERE J = 0 AND K BETWEEN 1 AND 10; +EXPLAIN +SELECT * FROM t10000 WHERE J = 0 AND K = 1; + +## Verify selection of 'best' index +## (The one of index I/J being EQ) +EXPLAIN +SELECT * FROM t10000 WHERE I = 0 AND J <> 1; +EXPLAIN +SELECT * FROM t10000 WHERE I <> 0 AND J = 1; +EXPLAIN +SELECT * FROM t10000 WHERE I <> 0 AND J <> 1; + +EXPLAIN +SELECT * FROM t10000 WHERE J <> 1 AND I = 0; +EXPLAIN +SELECT * FROM t10000 WHERE J = 1 AND I <> 0; +EXPLAIN +SELECT * FROM t10000 WHERE J <> 1 AND I <> 0; + DROP TABLE t10,t100,t10000; === modified file 'sql/ha_ndbcluster.cc' --- a/sql/ha_ndbcluster.cc 2011-02-18 10:19:20 +0000 +++ b/sql/ha_ndbcluster.cc 2011-02-23 15:54:12 +0000 @@ -11182,7 +11182,87 @@ ha_ndbcluster::records_in_range(uint inx DBUG_RETURN(rows); } - DBUG_RETURN(10); /* Good guess when you don't know anything */ + /* Use simple heuristics to estimate fraction + of 'stats.record' returned from range. + */ + if (stats.records != 0 && stats.records != HA_POS_ERROR) + { + Uint64 rows; + Uint64 table_rows= stats.records; + size_t min_key_length= (min_key) ? min_key->length : 0; + size_t max_key_length= (max_key) ? max_key->length : 0; + + // Might have an closed/open range bound: + // Low range open + if (!min_key_length) + { + rows= (!max_key_length) + ? table_rows // No range was specified + : table_rows/10; // -oo .. -> 10% selectivity + } + // High range open + else if (!max_key_length) + { + rows= table_rows/10; // ..oo -> 10% selectivity + } + else + { + size_t bounds_len= min(min_key_length,max_key_length); + uint eq_bound_len= 0; + uint eq_bound_offs= 0; + + KEY_PART_INFO* key_part= key_info->key_part; + KEY_PART_INFO* end= key_part+key_info->key_parts; + for (; key_part != end; key_part++) + { + uint part_length= key_part->store_length; + if (eq_bound_offs+part_length > bounds_len || + memcmp(&min_key->key[eq_bound_offs], + &max_key->key[eq_bound_offs], + part_length)) + { + break; + } + eq_bound_len+= key_part->length; + eq_bound_offs+= part_length; + } + + if (!eq_bound_len) + { + rows= table_rows/20; // .. -> 5% selectivity + } + else + { + // Has an equality range on a leading part of 'key_length': + // - Null indicator, and HA_KEY_BLOB_LENGTH bytes in + // 'extra_length' are removed from key_fraction calculations. + // - Assume reduced selectivity for non-unique indexes + // by decreasing 'eq_fraction' by 20% + // - Assume equal selectivity for all eq_parts in key. + + double eq_fraction = (double)(eq_bound_len) / + (key_length - key_info->extra_length); + if (idx_type == ORDERED_INDEX) // Non-unique index -> less selectivity + eq_fraction/= 1.20; + if (eq_fraction >= 1.0) // Exact match -> 1 row + DBUG_RETURN(1); + + rows = (Uint64)(table_rows / pow(table_rows, eq_fraction)); + if (rows > (table_rows/50)) // EQ-range: Max 2% of rows + rows= (table_rows/50); + + if (min_key_length > eq_bound_offs) + rows/= 2; + if (max_key_length > eq_bound_offs) + rows/= 2; + } + } + if (rows < 2) // At least 2 rows as not exact + rows= 2; + DBUG_RETURN(min(rows,table_rows)); + } + + DBUG_RETURN(10); /* Poor guess when you don't know anything */ } ulonglong ha_ndbcluster::table_flags(void) const --===============1282955185738021942== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/ole.john.aske@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: ole.john.aske@stripped\ # n0enzknm304djz0g # target_branch: file:///net/fimafeng09/export/home/tmp/oleja/mysql\ # /mysql-5.1-telco-7.0/ # testament_sha1: 87150c79e02faa2597d52b2b8b29a67a59c0a218 # timestamp: 2011-02-23 16:54:18 +0100 # base_revision_id: ole.john.aske@stripped\ # bzlov561777tbemr # # Begin bundle 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 kinChIBY6cII --===============1282955185738021942==--