Skip to content

Commit a5c67cb

Browse files
author
Sreeharsha Ramanavarapu
committed
Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY INDEX,
EVEN THOUGH COST IS HIGHER Issue: ------ Optimizer tries to choose between a range-access and ref-access, based on various parameters like records_per_key, CPU cost and I/O cost. When the row-estimate for ref access is inaccurate, optimizer uses the estimate from range access on the same index. In the current issue the ref access's row-estimate comes from range-access's estimate. The ref-access on a secondary index is calculated to be cheaper than the best range-access on the primary key. This will result in a bad performance. Solution (mysql-trunk): --------- Ignore the index for ref access when we have the above mentioned problem. Solution (mysql-5.7): --------- Shift from ref-access to range-access when: 1) Ref-access row estimate is overly-optimistic and it has borrowed range-access's estimate on the same index. 2) Range-access uses more index key parts than ref access. We already do this in cases where the above conditions are met and range access on the same index is the cheapest. With this fix, we shift to range-access on that index, even if it is not considered the cheapest. To do this, we run the range optimizer on that index alone. sql/sql_select.h: A new boolean 'dodgy_ref_cost' has been added to JOIN_TAB. Setting this flag means ref is using lesser number of key-parts than range and it borrows range's row estimate. sql/sql_planner.h: find_best_ref should be able to modify the flag 'dodgy_ref_cost', so removing the const. sql/sql_planner.cc: In find_best_ref, keep track of whether ref access's row-estimate is problematic and set the 'dodgy_ref_cost' flag. sql/sql_optimizer.cc: Conditions 3, 4 were earlier tested for negation. Now they are tested before 2 and false is returned. Check if the 'dodgy_ref_cost' flag is set. Then it is better we shift to range on the same index since it uses more keyparts and its row-estimate is more reliable. We do this by calling the range optimizer again on that index alone. Solution (mysql-5.6): --------- Same approach as 5.7. With the following changes: sql/sql_optimizer.cc: A new function can_switch_from_ref_to_range, similar to the one in 5.7+ has been created.
1 parent 98eedf1 commit a5c67cb

15 files changed

+1777
-43
lines changed

mysql-test/include/range.inc

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1836,4 +1836,67 @@ CREATE TABLE t1 (
18361836

18371837
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
18381838

1839-
DROP TABLE t1;
1839+
DROP TABLE t1;
1840+
1841+
--echo #
1842+
--echo # Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
1843+
--echo # INDEX, EVEN THOUGH COST IS HIGHER
1844+
--echo #
1845+
1846+
CREATE TABLE `giant_table` (
1847+
`id` int(11) NOT NULL AUTO_INCREMENT,
1848+
`one_id` int(11) NOT NULL,
1849+
`other_id` bigint(20) NOT NULL DEFAULT '0',
1850+
`some_other_id` int(11) DEFAULT 0 NOT NULL,
1851+
`something` double NOT NULL DEFAULT '0',
1852+
`comment` text COLLATE utf8_unicode_ci,
1853+
`flags` int(11) NOT NULL DEFAULT '0',
1854+
`time_created` int(11) NOT NULL DEFAULT '0',
1855+
PRIMARY KEY (`id`),
1856+
KEY `time_created` (`time_created`),
1857+
KEY `some_other_id` (`some_other_id`),
1858+
KEY `one_other_idx` (`one_id`,`other_id`),
1859+
KEY `other_id` (`other_id`,`time_created`)
1860+
) ENGINE=InnoDB AUTO_INCREMENT=101651329
1861+
DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
1862+
1863+
CREATE TABLE t1 (c1 INT);
1864+
INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
1865+
(82543190), (67538270), (77282760), (77908170),
1866+
(70923370), (68066360);
1867+
DELIMITER $;
1868+
CREATE PROCEDURE p()
1869+
BEGIN
1870+
SET @x = 1;
1871+
REPEAT
1872+
1873+
INSERT INTO giant_table(id,one_id)
1874+
SELECT c1 + @x, 0
1875+
FROM t1
1876+
WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
1877+
77282760, 77908170, 70923370, 68066360);
1878+
SET @x = @x + 1;
1879+
1880+
UNTIL @x > 30 END REPEAT;
1881+
END $
1882+
DELIMITER ;$
1883+
1884+
CALL p();
1885+
SELECT count(*) FROM giant_table;
1886+
1887+
INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
1888+
(89628210,1), (77869520,2);
1889+
INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
1890+
(61069031, 1, 1);
1891+
1892+
EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
1893+
some_other_id, flags
1894+
FROM giant_table
1895+
WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
1896+
84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
1897+
89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
1898+
70923271, 68066180)
1899+
AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
1900+
1901+
DROP PROCEDURE p;
1902+
DROP TABLE giant_table, t1;

mysql-test/r/range_all.result

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,4 +2494,60 @@ KEY(a(1))
24942494
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
24952495
1
24962496
DROP TABLE t1;
2497+
#
2498+
# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
2499+
# INDEX, EVEN THOUGH COST IS HIGHER
2500+
#
2501+
CREATE TABLE `giant_table` (
2502+
`id` int(11) NOT NULL AUTO_INCREMENT,
2503+
`one_id` int(11) NOT NULL,
2504+
`other_id` bigint(20) NOT NULL DEFAULT '0',
2505+
`some_other_id` int(11) DEFAULT 0 NOT NULL,
2506+
`something` double NOT NULL DEFAULT '0',
2507+
`comment` text COLLATE utf8_unicode_ci,
2508+
`flags` int(11) NOT NULL DEFAULT '0',
2509+
`time_created` int(11) NOT NULL DEFAULT '0',
2510+
PRIMARY KEY (`id`),
2511+
KEY `time_created` (`time_created`),
2512+
KEY `some_other_id` (`some_other_id`),
2513+
KEY `one_other_idx` (`one_id`,`other_id`),
2514+
KEY `other_id` (`other_id`,`time_created`)
2515+
) ENGINE=InnoDB AUTO_INCREMENT=101651329
2516+
DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
2517+
CREATE TABLE t1 (c1 INT);
2518+
INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
2519+
(82543190), (67538270), (77282760), (77908170),
2520+
(70923370), (68066360);
2521+
CREATE PROCEDURE p()
2522+
BEGIN
2523+
SET @x = 1;
2524+
REPEAT
2525+
INSERT INTO giant_table(id,one_id)
2526+
SELECT c1 + @x, 0
2527+
FROM t1
2528+
WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
2529+
77282760, 77908170, 70923370, 68066360);
2530+
SET @x = @x + 1;
2531+
UNTIL @x > 30 END REPEAT;
2532+
END $
2533+
CALL p();
2534+
SELECT count(*) FROM giant_table;
2535+
count(*)
2536+
270
2537+
INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
2538+
(89628210,1), (77869520,2);
2539+
INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
2540+
(61069031, 1, 1);
2541+
EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
2542+
some_other_id, flags
2543+
FROM giant_table
2544+
WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
2545+
84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
2546+
89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
2547+
70923271, 68066180)
2548+
AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
2549+
id select_type table type possible_keys key key_len ref rows Extra
2550+
1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using index condition; Using where; Using MRR
2551+
DROP PROCEDURE p;
2552+
DROP TABLE giant_table, t1;
24972553
set optimizer_switch=default;

mysql-test/r/range_icp.result

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,4 +2494,60 @@ KEY(a(1))
24942494
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
24952495
1
24962496
DROP TABLE t1;
2497+
#
2498+
# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
2499+
# INDEX, EVEN THOUGH COST IS HIGHER
2500+
#
2501+
CREATE TABLE `giant_table` (
2502+
`id` int(11) NOT NULL AUTO_INCREMENT,
2503+
`one_id` int(11) NOT NULL,
2504+
`other_id` bigint(20) NOT NULL DEFAULT '0',
2505+
`some_other_id` int(11) DEFAULT 0 NOT NULL,
2506+
`something` double NOT NULL DEFAULT '0',
2507+
`comment` text COLLATE utf8_unicode_ci,
2508+
`flags` int(11) NOT NULL DEFAULT '0',
2509+
`time_created` int(11) NOT NULL DEFAULT '0',
2510+
PRIMARY KEY (`id`),
2511+
KEY `time_created` (`time_created`),
2512+
KEY `some_other_id` (`some_other_id`),
2513+
KEY `one_other_idx` (`one_id`,`other_id`),
2514+
KEY `other_id` (`other_id`,`time_created`)
2515+
) ENGINE=InnoDB AUTO_INCREMENT=101651329
2516+
DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
2517+
CREATE TABLE t1 (c1 INT);
2518+
INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
2519+
(82543190), (67538270), (77282760), (77908170),
2520+
(70923370), (68066360);
2521+
CREATE PROCEDURE p()
2522+
BEGIN
2523+
SET @x = 1;
2524+
REPEAT
2525+
INSERT INTO giant_table(id,one_id)
2526+
SELECT c1 + @x, 0
2527+
FROM t1
2528+
WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
2529+
77282760, 77908170, 70923370, 68066360);
2530+
SET @x = @x + 1;
2531+
UNTIL @x > 30 END REPEAT;
2532+
END $
2533+
CALL p();
2534+
SELECT count(*) FROM giant_table;
2535+
count(*)
2536+
270
2537+
INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
2538+
(89628210,1), (77869520,2);
2539+
INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
2540+
(61069031, 1, 1);
2541+
EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
2542+
some_other_id, flags
2543+
FROM giant_table
2544+
WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
2545+
84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
2546+
89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
2547+
70923271, 68066180)
2548+
AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
2549+
id select_type table type possible_keys key key_len ref rows Extra
2550+
1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using index condition; Using where
2551+
DROP PROCEDURE p;
2552+
DROP TABLE giant_table, t1;
24972553
set optimizer_switch=default;

mysql-test/r/range_icp_mrr.result

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,4 +2494,60 @@ KEY(a(1))
24942494
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
24952495
1
24962496
DROP TABLE t1;
2497+
#
2498+
# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
2499+
# INDEX, EVEN THOUGH COST IS HIGHER
2500+
#
2501+
CREATE TABLE `giant_table` (
2502+
`id` int(11) NOT NULL AUTO_INCREMENT,
2503+
`one_id` int(11) NOT NULL,
2504+
`other_id` bigint(20) NOT NULL DEFAULT '0',
2505+
`some_other_id` int(11) DEFAULT 0 NOT NULL,
2506+
`something` double NOT NULL DEFAULT '0',
2507+
`comment` text COLLATE utf8_unicode_ci,
2508+
`flags` int(11) NOT NULL DEFAULT '0',
2509+
`time_created` int(11) NOT NULL DEFAULT '0',
2510+
PRIMARY KEY (`id`),
2511+
KEY `time_created` (`time_created`),
2512+
KEY `some_other_id` (`some_other_id`),
2513+
KEY `one_other_idx` (`one_id`,`other_id`),
2514+
KEY `other_id` (`other_id`,`time_created`)
2515+
) ENGINE=InnoDB AUTO_INCREMENT=101651329
2516+
DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
2517+
CREATE TABLE t1 (c1 INT);
2518+
INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
2519+
(82543190), (67538270), (77282760), (77908170),
2520+
(70923370), (68066360);
2521+
CREATE PROCEDURE p()
2522+
BEGIN
2523+
SET @x = 1;
2524+
REPEAT
2525+
INSERT INTO giant_table(id,one_id)
2526+
SELECT c1 + @x, 0
2527+
FROM t1
2528+
WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
2529+
77282760, 77908170, 70923370, 68066360);
2530+
SET @x = @x + 1;
2531+
UNTIL @x > 30 END REPEAT;
2532+
END $
2533+
CALL p();
2534+
SELECT count(*) FROM giant_table;
2535+
count(*)
2536+
270
2537+
INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
2538+
(89628210,1), (77869520,2);
2539+
INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
2540+
(61069031, 1, 1);
2541+
EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
2542+
some_other_id, flags
2543+
FROM giant_table
2544+
WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
2545+
84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
2546+
89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
2547+
70923271, 68066180)
2548+
AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
2549+
id select_type table type possible_keys key key_len ref rows Extra
2550+
1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using index condition; Using where; Using MRR
2551+
DROP PROCEDURE p;
2552+
DROP TABLE giant_table, t1;
24972553
set optimizer_switch=default;

mysql-test/r/range_mrr.result

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,4 +2494,60 @@ KEY(a(1))
24942494
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
24952495
1
24962496
DROP TABLE t1;
2497+
#
2498+
# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
2499+
# INDEX, EVEN THOUGH COST IS HIGHER
2500+
#
2501+
CREATE TABLE `giant_table` (
2502+
`id` int(11) NOT NULL AUTO_INCREMENT,
2503+
`one_id` int(11) NOT NULL,
2504+
`other_id` bigint(20) NOT NULL DEFAULT '0',
2505+
`some_other_id` int(11) DEFAULT 0 NOT NULL,
2506+
`something` double NOT NULL DEFAULT '0',
2507+
`comment` text COLLATE utf8_unicode_ci,
2508+
`flags` int(11) NOT NULL DEFAULT '0',
2509+
`time_created` int(11) NOT NULL DEFAULT '0',
2510+
PRIMARY KEY (`id`),
2511+
KEY `time_created` (`time_created`),
2512+
KEY `some_other_id` (`some_other_id`),
2513+
KEY `one_other_idx` (`one_id`,`other_id`),
2514+
KEY `other_id` (`other_id`,`time_created`)
2515+
) ENGINE=InnoDB AUTO_INCREMENT=101651329
2516+
DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
2517+
CREATE TABLE t1 (c1 INT);
2518+
INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
2519+
(82543190), (67538270), (77282760), (77908170),
2520+
(70923370), (68066360);
2521+
CREATE PROCEDURE p()
2522+
BEGIN
2523+
SET @x = 1;
2524+
REPEAT
2525+
INSERT INTO giant_table(id,one_id)
2526+
SELECT c1 + @x, 0
2527+
FROM t1
2528+
WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
2529+
77282760, 77908170, 70923370, 68066360);
2530+
SET @x = @x + 1;
2531+
UNTIL @x > 30 END REPEAT;
2532+
END $
2533+
CALL p();
2534+
SELECT count(*) FROM giant_table;
2535+
count(*)
2536+
270
2537+
INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
2538+
(89628210,1), (77869520,2);
2539+
INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
2540+
(61069031, 1, 1);
2541+
EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
2542+
some_other_id, flags
2543+
FROM giant_table
2544+
WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
2545+
84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
2546+
89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
2547+
70923271, 68066180)
2548+
AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
2549+
id select_type table type possible_keys key key_len ref rows Extra
2550+
1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using where; Using MRR
2551+
DROP PROCEDURE p;
2552+
DROP TABLE giant_table, t1;
24972553
set optimizer_switch=default;

mysql-test/r/range_mrr_cost.result

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,4 +2494,60 @@ KEY(a(1))
24942494
SELECT 1 FROM t1 WHERE a <> 'a' OR a <> "";
24952495
1
24962496
DROP TABLE t1;
2497+
#
2498+
# Bug #23259872: OPTIMIZER CHOOSES TO USE NON PRIMARY
2499+
# INDEX, EVEN THOUGH COST IS HIGHER
2500+
#
2501+
CREATE TABLE `giant_table` (
2502+
`id` int(11) NOT NULL AUTO_INCREMENT,
2503+
`one_id` int(11) NOT NULL,
2504+
`other_id` bigint(20) NOT NULL DEFAULT '0',
2505+
`some_other_id` int(11) DEFAULT 0 NOT NULL,
2506+
`something` double NOT NULL DEFAULT '0',
2507+
`comment` text COLLATE utf8_unicode_ci,
2508+
`flags` int(11) NOT NULL DEFAULT '0',
2509+
`time_created` int(11) NOT NULL DEFAULT '0',
2510+
PRIMARY KEY (`id`),
2511+
KEY `time_created` (`time_created`),
2512+
KEY `some_other_id` (`some_other_id`),
2513+
KEY `one_other_idx` (`one_id`,`other_id`),
2514+
KEY `other_id` (`other_id`,`time_created`)
2515+
) ENGINE=InnoDB AUTO_INCREMENT=101651329
2516+
DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
2517+
CREATE TABLE t1 (c1 INT);
2518+
INSERT INTO t1 VALUES (66136540), (68983250), (89627210), (77869520),
2519+
(82543190), (67538270), (77282760), (77908170),
2520+
(70923370), (68066360);
2521+
CREATE PROCEDURE p()
2522+
BEGIN
2523+
SET @x = 1;
2524+
REPEAT
2525+
INSERT INTO giant_table(id,one_id)
2526+
SELECT c1 + @x, 0
2527+
FROM t1
2528+
WHERE c1 IN (66136540, 68985250, 89627210, 77869520 , 82543190, 67538270,
2529+
77282760, 77908170, 70923370, 68066360);
2530+
SET @x = @x + 1;
2531+
UNTIL @x > 30 END REPEAT;
2532+
END $
2533+
CALL p();
2534+
SELECT count(*) FROM giant_table;
2535+
count(*)
2536+
270
2537+
INSERT INTO giant_table (id,one_id) VALUES (66136539, 0), (68983258,1),
2538+
(89628210,1), (77869520,2);
2539+
INSERT INTO giant_table (id,one_id, some_other_id) VALUES(84673401, 0, 1),
2540+
(61069031, 1, 1);
2541+
EXPLAIN SELECT id, something, comment, time_created, one_id, other_id,
2542+
some_other_id, flags
2543+
FROM giant_table
2544+
WHERE id IN (66136539, 68983258, 89628210, 77869520, 82543198, 67538272,
2545+
84673401, 61069031, 68214385, 77282865, 76991297, 64569216,
2546+
89481638, 74534074, 70396537, 80076375, 63308530, 77908270,
2547+
70923271, 68066180)
2548+
AND (giant_table.flags & 0x01) = 0 AND giant_table.some_other_id = 0;
2549+
id select_type table type possible_keys key key_len ref rows Extra
2550+
1 SIMPLE giant_table range PRIMARY,some_other_id some_other_id 8 NULL 20 Using where
2551+
DROP PROCEDURE p;
2552+
DROP TABLE giant_table, t1;
24972553
set optimizer_switch=default;

0 commit comments

Comments
 (0)