Bug #48093 | 6.0 Server not processing equivalent IN clauses properly with Innodb tables | ||
---|---|---|---|
Submitted: | 15 Oct 2009 18:36 | Modified: | 23 Nov 2010 3:02 |
Reporter: | Patrick Crews | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: Optimizer | Severity: | S3 (Non-critical) |
Version: | 6.0 | OS: | Any |
Assigned to: | Jørgen Løland | CPU Architecture: | Any |
Tags: | IN, innodb, mrr, regression |
[15 Oct 2009 18:36]
Patrick Crews
[19 Oct 2009 15:04]
Patrick Crews
EXPLAIN output: Query 1: EXPLAIN SELECT `date_key` FROM C WHERE `varchar_key` IN ( SELECT `varchar_nokey` FROM C ) AND `int_key` = 8 ; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY C ref int_key int_key 5 const 2 Using where 2 DEPENDENT SUBQUERY C ALL NULL NULL NULL NULL 20 Using where Query 2: EXPLAIN SELECT `date_key` FROM C WHERE `varchar_key` IN ( 'w', 'm', 'm', 'k', 'r', 't', 'j', 'u', 'h', 'o', NULL, 'k', 'e', 'n', 't', 'c', 'm', 'y', 'f', 'd' ) AND `int_key` = 8 /* TRANSFORM_OUTCOME_UNORDERED_MATCH */; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE C index_merge int_key,varchar_key int_key,varchar_key 5,9 NULL 1 Using intersect(int_key,varchar_key); Using where
[28 Oct 2009 12:20]
Jørgen Løland
I simplified the test case. Notice that there is a difference between having a total of 3 and 4 records in the table /*!50400 SET SESSION engine_condition_pushdown = 0 */; CREATE TABLE t1 ( i int(11) DEFAULT NULL, v1 varchar(1) DEFAULT NULL, v2 varchar(20) DEFAULT NULL, KEY i (i), KEY v (v1,i) ) ENGINE=innodb DEFAULT CHARSET=latin1; INSERT INTO t1 VALUES (1,'f','no'); INSERT INTO t1 VALUES (2,'u','yes'); INSERT INTO t1 VALUES (2,'h','yes'); # 3 records in the table - CORRECT result SELECT v2 FROM t1 WHERE v1 IN ( 'f', 'd', 'h', 'u' ) AND i = 2; v2 yes yes EXPLAIN SELECT v2 FROM t1 WHERE v1 IN ( 'f', 'd', 'h', 'u' ) AND i = 2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref i,v i 5 const 1 Using where INSERT INTO t1 VALUES (3,'d','no'); # 4 records in the table - INCORRECT result SELECT v2 FROM t1 WHERE v1 IN ( 'f', 'd', 'h', 'u' ) AND i = 2; v2 yes EXPLAIN SELECT v2 FROM t1 WHERE v1 IN ( 'f', 'd', 'h', 'u' ) AND i = 2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index_merge i,v i,v 5,9 NULL 1 Using intersect(i,v); Using where DROP TABLE t1;
[28 Oct 2009 12:54]
Jørgen Løland
Does not fail on MyISAM, Memory or Falcon, but none of these engines perform the lookup using the intersection.
[30 Oct 2009 10:57]
Jørgen Løland
Some observations: * The optimizer chooses to use index merge of index i and index v. * For index merge to work, index i and index v need to return records in Rowid order (ROR) * AFAIK, only the primary key index of InnoDB is guaranteed to return records in Rowid order * Index i (QUICK_RANGE_SELECT) returns records in this order: [2,u,..],[2,h,..] (this happens to be rowid order, but probably only by coincidence) * Index v (QUICK_RANGE_SELECT) returns records in this order: [2,h,..],[2,u,..] (this is not rowid order) * The following happens in QUICK_ROR_INTERSECT_SELECT: index(i)->get_next() => [2,u] index(v)->get_next() => [2,h] compare record ids: RID([2,h]) > RID([2,u]) index(i)->get_next() => [2,h] compare record ids: RID([2,h]) == RID([2,h]) => return record index(i)->get_next() => EOF, i.e., no more records to return * The optimizer decides to use index_merge because InnoDB claims that both indexes return records in rowid order. This is the relevant test in opt_range.cc#check_quick_select(): param->is_ror_scan= TRUE; if (file->index_flags(keynr, 0, TRUE) & HA_KEY_SCAN_NOT_ROR) param->is_ror_scan= FALSE; HA_KEY_SCAN_NOT_ROR was introduced by WL#2474/WL#2475, but InnoDB's index_flags method was never updated (ha_innodb.h): ulong index_flags(uint idx, uint part, bool all_parts) const { ulong fl= (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_READ_RANGE | HA_KEYREAD_ONLY); return fl; } * Under the assumption that only the primary key index in InnoDB is guaranteed to return records in rowid order (needs to be verified), the following should be added to InnoDB's index_flags(): if (idx != primary) fl|= HA_KEY_SCAN_NOT_ROR;
[30 Oct 2009 12:07]
Jørgen Løland
Modifying index_flags() in ha_innodb.h as suggested above makes the query return correct result. The new query plan is: EXPLAIN extended SELECT v2 FROM t1 WHERE v1 IN ( 'f', 'd', 'h', 'u' ) AND i = 2; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ref i,v i 5 const 2 100.00 Using where Warnings: Note 1003 select `test`.`t1`.`v2` AS `v2` from `test`.`t1` where ((`test`.`t1`.`i` = 2) and (`test`.`t1`.`v1` in ('f','d','h','u'))) DROP TABLE t1;
[30 Oct 2009 13:56]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/88749 3691 Jorgen Loland 2009-10-30 Bug#48093: 6.0 Server not processing equivalent IN clauses properly with Innodb tables InnoDB never appended the HA_KEY_SCAN_NOT_ROR flag in index_flags(). This made the optimizer believe that all indexes returned records in ROR (Rowid Ordered Retrieval) order. However, only the primary key index returns records in order of row id. Since the optimizer thought the indexes were ROR, it could chose to use the index merge strategy on indexes that did not preserve ROR order. This lead to missing records in the result set. This patch makes index_flags return the HA_KEY_SCAN_NOT_ROR flag unless the index is the primary key index. @ mysql-test/r/range.result Added test for BUG#48093 @ mysql-test/t/range.test Added test for BUG#48093 and sourced have_innodb.inc so that the added test and existing tests in this file that actually require InnoDB can use this storage engine. @ storage/innobase/handler/ha_innodb.h Append HA_KEY_SCAN_NOT_ROR to flags returned from index_flags unless the index is the primary key index.
[3 Nov 2009 9:41]
Jørgen Løland
The assumption that non-pk indexes do not return rowid ordered rows is only a half truth. The meaning of HA_KEY_SCAN_NOT_ROR is to specify whether or not single-value lookups in the index will return rowid ordered records. The reason for out of order records in the test case above is the MRR implementation that looks at each of the values in the IN (...) list one at a time. Although the index will return ROR records for each of these values (f,u,h,d), the MRR algorithm will not return ROR for the set of values but rather {ROR for records with v1=f}, then {ROR for records with v1=d} and so on. The fix for this bug is to set the retrieval method's is_ror_scan variable to FALSE when multiple values are used to do the index lookup.
[3 Nov 2009 9:59]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/89056 3691 Jorgen Loland 2009-11-03 Bug#48093: 6.0 Server not processing equivalent IN clauses properly with Innodb tables When MRR was used to lookup multiple values in an index that return ROR ordered records, it was assumed that the returned records from MRR were also ROR ordered. This is not true since MRR returns all records retrieved for the first value, then all records for the second value and so on. Although the records are ROR ordered for each value, the end result is not ROR order. Since the optimizer thought multi-value lookups returned ROR ordered records, it tried to do index merge on the returned records. These turned out not to be ROR ordered, resulting in incorrect merging and in turn missing rows in the result set. The fix is to invalidate ROR ordering for index lookup algorithms when looking up multiple key values. @ mysql-test/r/range.result Added test for BUG#48093 @ mysql-test/t/range.test Added test for BUG#48093 and sourced have_innodb.inc so that the added test and existing tests in this file (that actually try to use InnoDB) can use this storage engine. @ sql/opt_range.cc Invalidate ROR ordering for index lookup algorithms when looking up multiple key values.
[6 Nov 2009 7:39]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/89553 3699 Jorgen Loland 2009-11-06 Bug#48093: 6.0 Server not processing equivalent IN clauses properly with Innodb tables When MRR was used to lookup multiple values in an index that return ROR ordered records, it was assumed that the returned records from MRR were also ROR ordered. This is not true since MRR returns all records retrieved for the first value, then all records for the second value and so on. Although the records are ROR ordered for each value, the end result is not ROR order. Since the optimizer thought multi-value lookups returned ROR ordered records, it tried to do index merge on the returned records. These turned out not to be ROR ordered, resulting in incorrect merging and in turn missing rows in the result set. The fix is to invalidate ROR ordering for index lookup algorithms when looking up multiple key values. @ mysql-test/r/innodb_mysql.result Added test for BUG#48093 @ mysql-test/t/innodb_mysql.test Added test for BUG#48093 @ sql/opt_range.cc Invalidate ROR ordering for index lookup algorithms when looking up multiple key values.
[13 Nov 2009 6:44]
Jørgen Løland
Pushed to 6.0-codebase-bugfixing 2009-11-06
[18 Nov 2009 3:25]
Igor Babaev
Jorgen, For the mysql-6.0-codebase tree (without your patch) you can see in debugger that when you come to this code for the second index from your test case else { /* Clustered PK scan is always a ROR scan (TODO: same as above) */ if (param->table->s->primary_key == keynr && pk_is_clustered) param->is_ror_scan= TRUE; } you have: param->table->s->primary_key: 64 keynr: 1 pk_is_clustered: true and, as a result of it, the assignment param->is_ror_scan= TRUE; is skipped This is absolutely correct because the index KEY v (v1,i) is not primary, Yet, the initial setting for param->is_ror_scan is TRUE and remains TRUE. This is the *actual* problem. Your patch actually prohibits ROR-scans by primary indexes if there are more than 1 range. This will effectively lead to performance regressions. (BTW, this bug should be seen with MRR disabled as well) Regards, Igor.
[20 Nov 2009 12:58]
Bugs System
Pushed into 6.0.14-alpha (revid:kostja@sun.com-20091120124947-yi6h2jbgw0kbciwm) (version source revid:jorgen.loland@sun.com-20091106074019-cqoxr3hvmbgqcldt) (merge vers: 6.0.14-alpha) (pib:13)
[17 Dec 2009 11:23]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/94714 3778 Jorgen Loland 2009-12-17 BUG#48093: 6.0 Server not processing equivalent IN clauses properly with Innodb tables Followup patch, addressing comments from Igor regarding previously pushed patch. In the previous patch, is_ror_scan was set to FALSE in check_quick_select() if there were multiple ranges. This patch sets is_ror_scan to FALSE for the same case a bit earlier, in sel_arg_range_seq_next(). There is no new test case since the test pushed in the previous patch still applies. @ sql/opt_range.cc Move setting of is_ror_scan= FALSE from check_quick_select() to sel_arg_range_seq_next() in case of multiple range lookup.
[15 Jan 2010 8:25]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/97003 3829 Jorgen Loland 2010-01-15 BUG#48093: 6.0 Server not processing equivalent IN clauses properly with Innodb tables Followup patch, addressing comments from Igor regarding previously pushed patch. In the previous patch, is_ror_scan was set to FALSE in check_quick_select() if there were multiple ranges. This patch sets is_ror_scan to FALSE for the same case a bit earlier, in sel_arg_range_seq_next(). There is no new test case since the test pushed in the previous patch still applies. @ sql/opt_range.cc Move setting of is_ror_scan= FALSE from check_quick_select() to sel_arg_range_seq_next() in case of multiple range lookup.
[15 Jan 2010 8:28]
Jørgen Løland
pushed to mysql-6.0-codebase-bugfixing
[21 Jan 2010 8:38]
Bugs System
Pushed into 6.0.14-alpha (revid:alik@sun.com-20100121083501-but9pj2g3zmu10md) (version source revid:alik@sun.com-20100119194323-gcog2uiox2b7wsln) (merge vers: 6.0.14-alpha) (pib:16)
[23 Jan 2010 0:24]
Paul DuBois
Noted in 6.0.14 changelog. Some IN() clauses were processed differently for InnoDB than for other storage engines.
[7 May 2010 7:33]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/107708 3140 oystein.grovlen@sun.com 2010-05-07 Bug#48093: 6.0 Server not processing equivalent IN clauses properly with Innodb tables (Backporting of jorgen.loland@sun.com-20091106074019-cqoxr3hvmbgqcldt) When MRR was used to lookup multiple values in an index that return ROR ordered records, it was assumed that the returned records from MRR were also ROR ordered. This is not true since MRR returns all records retrieved for the first value, then all records for the second value and so on. Although the records are ROR ordered for each value, the end result is not ROR order. Since the optimizer thought multi-value lookups returned ROR ordered records, it tried to do index merge on the returned records. These turned out not to be ROR ordered, resulting in incorrect merging and in turn missing rows in the result set. The fix is to invalidate ROR ordering for index lookup algorithms when looking up multiple key values. @ mysql-test/r/innodb_mysql.result Added test for BUG#48093 @ mysql-test/t/innodb_mysql.test Added test for BUG#48093 @ sql/opt_range.cc Invalidate ROR ordering for index lookup algorithms when looking up multiple key values.
[7 May 2010 10:14]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/107723 3143 oystein.grovlen@sun.com 2010-05-07 BUG#48093: 6.0 Server not processing equivalent IN clauses properly with Innodb tables (Backporting of jorgen.loland@sun.com-20100115082606-71wooovy2sfhkej4) Followup patch, addressing comments from Igor regarding previously pushed patch. In the previous patch, is_ror_scan was set to FALSE in check_quick_select() if there were multiple ranges. This patch sets is_ror_scan to FALSE for the same case a bit earlier, in sel_arg_range_seq_next(). There is no new test case since the test pushed in the previous patch still applies. @ sql/opt_range.cc Move setting of is_ror_scan= FALSE from check_quick_select() to sel_arg_range_seq_next() in case of multiple range lookup.
[16 Aug 2010 6:39]
Bugs System
Pushed into mysql-next-mr (revid:alik@sun.com-20100816062819-bluwgdq8q4xysmlg) (version source revid:alik@sun.com-20100816062612-enatdwnv809iw3s9) (pib:20)
[13 Nov 2010 16:08]
Bugs System
Pushed into mysql-trunk 5.6.99-m5 (revid:alexander.nozdrin@oracle.com-20101113155825-czmva9kg4n31anmu) (version source revid:vasil.dimov@oracle.com-20100629074804-359l9m9gniauxr94) (merge vers: 5.6.99-m4) (pib:21)
[23 Nov 2010 3:02]
Paul DuBois
Bug does not appear in any released 5.6.x version. No 5.6.1 changelog entry needed.