Bug #75510 MySQL chooses index scan over full table scan
Submitted: 15 Jan 2015 0:36 Modified: 16 Jan 2015 9:53
Reporter: Justin Swanhart Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:5.6.21 OS:Any
Assigned to: CPU Architecture:Any

[15 Jan 2015 0:36] Justin Swanhart
Description:
The optimizer is supposed to choose a full table scan if the index is extremely skewed.  It doesn't appear to be doing that though, and uses index access instead.

How to repeat:
create table a(a int,key(a)) engine=innodb;
insert into a values (1);
insert into a select * from a; -- repeat until you insert 500K+ rows
insert into a values (2);

mysql> explain select a from a where a = 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 549376
        Extra: Using index
1 row in set (0.00 sec)

mysql> explain select a from a where a = 2 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 1
        Extra: Using index
1 row in set (0.00 sec)

Why does the database choose ref access for the first query (a=1)?  

I tried adding another column, suspecting covering index might be to blame, but that didn't change the plan, except to remove 'using index'.

Suggested fix:
unknown
[15 Jan 2015 4:19] Justin Swanhart
Note, that without an additional column, the example does a covering index scan, which is correct, so you need to do the following to observe the bug:

alter table a add b int default 1;

explain select a,b from a where a = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 549376
        Extra: NULL
1 row in set (0.00 sec)

So you can see it uses ref access instead of full table scan.

Compare times:

-- MySQL does not use index on a
mysql> select a,b from a ignore index(a) where a = 1 ;
...
1048576 rows in set (1.05 sec)

-- MySQL uses index on a
mysql> select a,b from a where a = 1 ;
...
1048576 rows in set (1.93 sec)

Here is the explain for the ignore index:
mysql> explain select a,b from a ignore index(a) where a = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1098752
        Extra: Using where
1 row in set (0.00 sec)
[16 Jan 2015 9:53] MySQL Verification Team
Hello Justin Swanhart,

Thank you for the report and test case.

Thanks,
Umesh
[16 Jan 2015 9:55] MySQL Verification Team
// 5.6.24

+-------------------------+---------------------------------------------------------+
| Variable_name           | Value                                                   |
+-------------------------+---------------------------------------------------------+
| innodb_version          | 5.6.24                                                  |
| protocol_version        | 10                                                      |
| slave_type_conversions  |                                                         |
| version                 | 5.6.24-enterprise-commercial-advanced                   |
| version_comment         | MySQL Enterprise Server - Advanced Edition (Commercial) |
| version_compile_machine | x86_64                                                  |
| version_compile_os      | linux-glibc2.5                                          |
+-------------------------+---------------------------------------------------------+
7 rows in set (0.00 sec)

mysql> create table a(a int,key(a)) engine=innodb;
Query OK, 0 rows affected (0.01 sec)

mysql> insert into a values (1);
Query OK, 1 row affected (0.01 sec)

mysql> insert into a select * from a;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 4 rows affected (0.01 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 16 rows affected (0.00 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 32 rows affected (0.01 sec)
Records: 32  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 64 rows affected (0.00 sec)
Records: 64  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 128 rows affected (0.00 sec)
Records: 128  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 256 rows affected (0.00 sec)
Records: 256  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 512 rows affected (0.01 sec)
Records: 512  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 1024 rows affected (0.01 sec)
Records: 1024  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 2048 rows affected (0.01 sec)
Records: 2048  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 4096 rows affected (0.03 sec)
Records: 4096  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 8192 rows affected (0.05 sec)
Records: 8192  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 16384 rows affected (0.11 sec)
Records: 16384  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 32768 rows affected (0.19 sec)
Records: 32768  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 65536 rows affected (0.37 sec)
Records: 65536  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 131072 rows affected (0.72 sec)
Records: 131072  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 262144 rows affected (1.51 sec)
Records: 262144  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 524288 rows affected (2.86 sec)
Records: 524288  Duplicates: 0  Warnings: 0

mysql>  insert into a values (2);
Query OK, 1 row affected (0.00 sec)

mysql> explain select a from a where a = 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 523498
        Extra: Using index
1 row in set (0.00 sec)

mysql> explain select a from a where a = 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 523498
        Extra: Using index
1 row in set (0.00 sec)

mysql> explain select a from a where a = 2 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 1
        Extra: Using index
1 row in set (0.00 sec)

mysql> alter table a add b int default 1;
Query OK, 0 rows affected (4.55 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select a,b from a where a = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 523441
        Extra: NULL
1 row in set (0.00 sec)

mysql> select a,b from a ignore index(a) where a = 1 ;

+------+------+
1048576 rows in set (0.64 sec)

mysql> select a,b from a where a = 1 ;
+------+------+
1048576 rows in set (1.49 sec)

mysql>
mysql> explain select a,b from a ignore index(a) where a = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1046883
        Extra: Using where
1 row in set (0.00 sec)
[16 Jan 2015 9:57] MySQL Verification Team
// 5.7.6

mysql> show variables like '%version%';
+-------------------------+---------------------------------------------------------+
| Variable_name           | Value                                                   |
+-------------------------+---------------------------------------------------------+
| innodb_version          | 5.7.6                                                   |
| protocol_version        | 10                                                      |
| slave_type_conversions  |                                                         |
| version                 | 5.7.6-m16-enterprise-commercial-advanced                |
| version_comment         | MySQL Enterprise Server - Advanced Edition (Commercial) |
| version_compile_machine | x86_64                                                  |
| version_compile_os      | Linux                                                   |
+-------------------------+---------------------------------------------------------+
7 rows in set (0.00 sec)

mysql> create table a(a int,key(a)) engine=innodb;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into a values (1);
Query OK, 1 row affected (0.00 sec)

mysql> insert into a select * from a;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 16 rows affected (0.00 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 32 rows affected (0.00 sec)
Records: 32  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 64 rows affected (0.00 sec)
Records: 64  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 128 rows affected (0.00 sec)
Records: 128  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 256 rows affected (0.00 sec)
Records: 256  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 512 rows affected (0.00 sec)
Records: 512  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 1024 rows affected (0.01 sec)
Records: 1024  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 2048 rows affected (0.02 sec)
Records: 2048  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 4096 rows affected (0.04 sec)
Records: 4096  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 8192 rows affected (0.09 sec)
Records: 8192  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 16384 rows affected (0.17 sec)
Records: 16384  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 32768 rows affected (0.32 sec)
Records: 32768  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 65536 rows affected (0.62 sec)
Records: 65536  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 131072 rows affected (1.26 sec)
Records: 131072  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 262144 rows affected (2.61 sec)
Records: 262144  Duplicates: 0  Warnings: 0

mysql> insert into a select * from a;
Query OK, 524288 rows affected (5.22 sec)
Records: 524288  Duplicates: 0  Warnings: 0

mysql> insert into a values (2);
Query OK, 1 row affected (0.00 sec)

mysql> explain select a from a where a = 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
   partitions: NULL
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 523673
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.00 sec)

mysql> explain select a from a where a = 2 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
   partitions: NULL
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.00 sec)

mysql> alter table a add b int default 1;
Query OK, 0 rows affected (1.88 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select a,b from a where a = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
   partitions: NULL
         type: ref
possible_keys: a
          key: a
      key_len: 5
          ref: const
         rows: 523446
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

mysql> select a,b from a ignore index(a) where a = 1 ;
+------+------+
1048576 rows in set (0.64 sec)

mysql> select a,b from a where a = 1 ;

+------+------+
1048576 rows in set (1.51 sec)

mysql> explain select a,b from a ignore index(a) where a = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1046892
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)