Bug #70001 Partitioned tables do not use ICP - severe performance loss after partitioning
Submitted: 12 Aug 2013 18:46 Modified: 6 Nov 2013 15:18
Reporter: Justin Swanhart Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Partitions Severity:S1 (Critical)
Version:5.6.13 OS:Any
Assigned to: CPU Architecture:Any

[12 Aug 2013 18:46] Justin Swanhart
Description:
mysql> explain SELECT cust_id AS expr$0,MAX(odometer_reading - issued_odometer) AS expr_3466088376 FROM cr AS `car_read`  JOIN cc AS `cust_car` ON( car_read.car_id = cust_car.car_id AND read_datetime between assigned_datetime AND unassigned_datetime ) \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: cust_car
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 25944
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: car_read
         type: ref
possible_keys: idx_car_read_datetime
          key: idx_car_read_datetime
      key_len: 4
          ref: test.cust_car.car_id
         rows: 168
        Extra: Using index condition
2 rows in set (0.00 sec)

mysql> explain SELECT cust_id AS expr$0,MAX(odometer_reading - issued_odometer) AS expr_3466088376 FROM car_read AS `car_read`  JOIN cust_car AS `cust_car` ON( car_read.car_id = cust_car.car_id AND read_datetime between assigned_datetime AND unassigned_datetime ) \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: cust_car
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 25554
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: car_read
         type: ref
possible_keys: idx_car_read_datetime
          key: idx_car_read_datetime
      key_len: 4
          ref: test.cust_car.car_id
         rows: 13
        Extra: Using where
2 rows in set (0.00 sec)

How to repeat:
see above.  data will be attached.

Suggested fix:
Use the same query plans for partitioned tables as regular tables.
[12 Aug 2013 18:51] Justin Swanhart
see support/incoming/70001.sql
[13 Aug 2013 17:28] Umesh Shastry
Hello Justin,

Thank you for the bug report and the test case. 
Verified as described.

Thanks,
Umesh
[6 Nov 2013 15:18] Jon Stephens
Fixed in 5.7. Documented in the 5.7.3 changelog as follows:

      Index condition pushdown did not work with partitioned tables.

Closed.
[13 Apr 2017 11:11] Rimantas Ragainis
This issue is till present on 5.7.17
Any estimations on when it will be fixed?
[23 Jun 2017 8:20] Valeriy Kravchuk
Rimantas,

Can you, please, upload a test case that shows the problem with 5.7.17? In my primitive test everything works as expected:

mysql> show create table cr\G
*************************** 1. row ***************************
       Table: cr
Create Table: CREATE TABLE `cr` (
  `id` int(11) NOT NULL,
  `r1` int(11) DEFAULT NULL,
  `r2` int(11) DEFAULT NULL,
  `dt1` datetime DEFAULT NULL,
  `dt2` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> show create table cc\G
*************************** 1. row ***************************
       Table: cc
Create Table: CREATE TABLE `cc` (
  `id` int(11) NOT NULL,
  `cr_id` int(11) DEFAULT NULL,
  `rt` datetime DEFAULT NULL,
  `other` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `k2` (`cr_id`,`rt`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> select count(*) from cc;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from cr;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> explain select cc.*, max(r2 - r1) from cr join cc on (cr.id = cc.cr_id and rt between dt1 and dt2);
+----+-------------+-------+------------+------+---------------+------+---------+------------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref        | rows | filtered | Extra                 |
+----+-------------+-------+------------+------+---------------+------+---------+------------+------+----------+-----------------------+
|  1 | SIMPLE      | cr    | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL       |    1 |   100.00 | NULL                  |
|  1 | SIMPLE      | cc    | NULL       | ref  | k2            | k2   | 5       | test.cr.id |    1 |   100.00 | Using index condition |
+----+-------------+-------+------------+------+---------------+------+---------+------------+------+----------+-----------------------+
2 rows in set, 1 warning (0.00 sec)

So, we have ICP used. Now, let me partition the cc table in a simple way:

mysql> alter table cc partition by hash(id) partitions 4;
Query OK, 0 rows affected (1.58 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select cc.*, max(r2 - r1) from cr join cc on (cr.id = cc.cr_id and rt between dt1 and dt2);
+----+-------------+-------+-------------+------+---------------+------+---------+------------+------+----------+-----------------------+
| id | select_type | table | partitions  | type | possible_keys | key  | key_len | ref        | rows | filtered | Extra                 |
+----+-------------+-------+-------------+------+---------------+------+---------+------------+------+----------+-----------------------+
|  1 | SIMPLE      | cr    | NULL        | ALL  | PRIMARY       | NULL | NULL    | NULL       |    1 |   100.00 | NULL                  |
|  1 | SIMPLE      | cc    | p0,p1,p2,p3 | ref  | k2            | k2   | 5       | test.cr.id |    1 |   100.00 | Using index condition |
+----+-------------+-------+-------------+------+---------------+------+---------+------------+------+----------+-----------------------+
2 rows in set, 1 warning (0.27 sec)

mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.17    |
+-----------+
1 row in set (0.00 sec)

As you can see, ICP is again used.
[23 Jun 2017 9:11] Rimantas Ragainis
Sure. I used the same table structure for two tables - one is partitioned by date ("test_partitioned")
and the other one isn't ("test_solid"). See SQL queries bellow:

CREATE TABLE `test_partitioned` (
  `service_id` int(10) unsigned NOT NULL,
  `uid` varchar(32) NOT NULL DEFAULT '',
  `date` date NOT NULL DEFAULT '0000-00-00',
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `ord_rev` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`service_id`,`uid`,`ord_rev`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=ascii ROW_FORMAT=COMPRESSED
/*!50100 PARTITION BY RANGE (TO_DAYS(`date`))
(PARTITION p20161001 VALUES LESS THAN (736634) ENGINE = InnoDB,
 PARTITION p20161101 VALUES LESS THAN (736664) ENGINE = InnoDB,
 PARTITION p20161201 VALUES LESS THAN (736695) ENGINE = InnoDB,
 PARTITION p20170101 VALUES LESS THAN (736726) ENGINE = InnoDB,
 PARTITION p20170201 VALUES LESS THAN (736754) ENGINE = InnoDB,
 PARTITION p20170301 VALUES LESS THAN (736785) ENGINE = InnoDB,
 PARTITION p20170401 VALUES LESS THAN (736815) ENGINE = InnoDB,
 PARTITION p20170501 VALUES LESS THAN (736846) ENGINE = InnoDB) */;

INSERT INTO `test_partitioned` (`service_id`, `uid`, `date`, `created`, `ord_rev`)
VALUES
	(1,'user123','2016-12-12','2016-12-12 07:38:07',9223223884026074696),
	(1,'user123','2017-01-04','2017-01-04 13:55:37',9223223683041068956),
	(1,'user123','2017-02-14','2017-02-14 11:17:38',9223223329748968486);

CREATE TABLE `test_solid` (
  `service_id` int(10) unsigned NOT NULL,
  `uid` varchar(32) NOT NULL DEFAULT '',
  `date` date NOT NULL DEFAULT '0000-00-00',
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `ord_rev` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`service_id`,`uid`,`ord_rev`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=ascii ROW_FORMAT=COMPRESSED;

INSERT INTO `test_solid` (`service_id`, `uid`, `date`, `created`, `ord_rev`)
VALUES
	(1,'user123','2017-02-14','2017-02-14 11:17:38',9223223329748968486),
	(1,'user123','2017-01-04','2017-01-04 13:55:37',9223223683041068956),
	(1,'user123','2016-12-12','2016-12-12 07:38:07',9223223884026074696);

mysql> select * from test_partitioned where service_id=1;
+------------+---------+------------+---------------------+---------------------+
| service_id | uid     | date       | created             | ord_rev             |
+------------+---------+------------+---------------------+---------------------+
|          1 | user123 | 2016-12-12 | 2016-12-12 07:38:07 | 9223223884026074696 |
|          1 | user123 | 2017-01-04 | 2017-01-04 13:55:37 | 9223223683041068956 |
|          1 | user123 | 2017-02-14 | 2017-02-14 11:17:38 | 9223223329748968486 |
+------------+---------+------------+---------------------+---------------------+
3 rows in set (0.00 sec)

mysql> select * from test_solid where service_id=1;
+------------+---------+------------+---------------------+---------------------+
| service_id | uid     | date       | created             | ord_rev             |
+------------+---------+------------+---------------------+---------------------+
|          1 | user123 | 2017-02-14 | 2017-02-14 11:17:38 | 9223223329748968486 |
|          1 | user123 | 2017-01-04 | 2017-01-04 13:55:37 | 9223223683041068956 |
|          1 | user123 | 2016-12-12 | 2016-12-12 07:38:07 | 9223223884026074696 |
+------------+---------+------------+---------------------+---------------------+
3 rows in set (0.01 sec)

As you can see from my example, the data from "test_solid" only is returned ordered ascending by primary key as expected.