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: | |
| Category: | MySQL Server: Partitions | Severity: | S1 (Critical) |
| Version: | 5.6.13 | OS: | Any |
| Assigned to: | CPU Architecture: | Any |
[12 Aug 2013 18:51]
Justin Swanhart
see support/incoming/70001.sql
[13 Aug 2013 17:28]
MySQL Verification Team
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.
Portions of this website are copyright © 2001, 2002 The PHP Group
Page generated in 0.057 sec. using MySQL 8.4.7-u6-cloud
Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the
respective copyright holders.
It is not reviewed in advance
by Oracle and does not necessarily represent the opinion of
Oracle or any other party.
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.