Bug #103711 Prepared statement don't seem to use index extensions / extended_keys

Submitted: 15 May 2021 14:35 Modified: 4 Jun 2021 20:13
Reporter: Joffrey MICHAÏE Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Prepared statements Severity:S5 (Performance)
Version:8.0.25 OS:Any
Assigned to: CPU Architecture:Any

[15 May 2021 14:35] Joffrey MICHAÏE
Description:
Hello,

while discovering https://bugs.mysql.com/bug.php?id=103710, we found out that prepared statement may not be using extended keys:

https://dev.mysql.com/doc/refman/8.0/en/index-extensions.html

I am not sure if setting optimizer_switch="prefer_ordering_index=off" is a requirement to observe this behavior.

Thanks & Regards,
Joffrey

How to repeat:

1) Start 8.0.25 instance with following (optimized for faster case creation):

[mysqld]
innodb_adaptive_flushing = 0
innodb_adaptive_hash_index = 0
innodb_buffer_pool_size = 1000M
innodb_doublewrite = 0
innodb_flush_log_at_trx_commit = 0
innodb_flush_method = nosync
innodb_log_file_size = 1000M
performance_schema = off
port = 38025
skip-log-bin
optimizer_switch="prefer_ordering_index=off"

2) Run the following script to populate Data

DROP TABLE IF EXISTS products;
CREATE TABLE `products` (
  `product_id` int unsigned NOT NULL AUTO_INCREMENT,
  `client_id` int unsigned NOT NULL,
  `creation_date` datetime NOT NULL,
  `payload1` char(255) DEFAULT '1',
  `payload2` char(255) DEFAULT '2',
  `payload3` char(255) DEFAULT '3',
  `payload4` char(255) DEFAULT '4',
  `payload5` char(255) DEFAULT '5',
  `payload6` char(255) DEFAULT '6',
  `payload7` char(255) DEFAULT '7',
  `payload8` char(255) DEFAULT '8',
  PRIMARY KEY (`product_id`),
  KEY `client_id` (`client_id`)
) ENGINE=InnoDB AUTO_INCREMENT=84370565 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO products (client_id,creation_date) SELECT 1, NOW() - INTERVAL ROUND(RAND()*100) DAY;
INSERT INTO products (client_id,creation_date) SELECT 2, NOW() - INTERVAL ROUND(RAND()*100) DAY;
INSERT INTO products (client_id,creation_date) SELECT 3, NOW() - INTERVAL ROUND(RAND()*100) DAY;
INSERT INTO products (client_id,creation_date) SELECT 4, NOW() - INTERVAL ROUND(RAND()*100) DAY;
INSERT INTO products (client_id,creation_date) SELECT 5, NOW() - INTERVAL ROUND(RAND()*100) DAY;
INSERT INTO products (client_id,creation_date) SELECT 6, NOW() - INTERVAL ROUND(RAND()*100) DAY;
INSERT INTO products (client_id,creation_date) SELECT 7, NOW() - INTERVAL ROUND(RAND()*100) DAY;
INSERT INTO products (client_id,creation_date) SELECT 8, NOW() - INTERVAL ROUND(RAND()*100) DAY;
INSERT INTO products (client_id,creation_date) SELECT 1, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 2, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 3, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 4, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 5, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 6, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 7, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 8, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 1, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 2, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 3, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 4, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 5, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 6, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 7, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 8, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
INSERT INTO products (client_id,creation_date) SELECT 1, NOW() - INTERVAL ROUND(RAND()*100) DAY FROM products;
ANALYZE TABLE products;

3) Run Script:

SET PROFILING = 1 ;
explain select * from products where client_id = 4 order by product_id desc limit 1;
select * from products where client_id = 4 order by product_id desc limit 1;
prepare my_query_e from 'explain select * from products where client_id = ? order by product_id desc limit 1';
prepare my_query from 'select * from products where client_id = ? order by product_id desc limit 1';
set @client_id = 4 ;
execute my_query_e using @client_id;
execute my_query using @client_id;
SHOW PROFILES;

#### output ####

--------------
SET PROFILING = 1
--------------
--------------
explain select * from products where client_id = 4 order by product_id desc limit 1
--------------
+----+-------------+----------+------------+------+---------------+-----------+---------+-------+-------+----------+---------------------+
| id | select_type | table    | partitions | type | possible_keys | key       | key_len | ref   | rows  | filtered | Extra               |
+----+-------------+----------+------------+------+---------------+-----------+---------+-------+-------+----------+---------------------+
|  1 | SIMPLE      | products | NULL       | ref  | client_id     | client_id | 4       | const | 30264 |   100.00 | Backward index scan |
+----+-------------+----------+------------+------+---------------+-----------+---------+-------+-------+----------+---------------------+
--------------
select * from products where client_id = 4 order by product_id desc limit 1
--------------

+------------+-----------+---------------------+----------+----------+----------+----------+----------+----------+----------+----------+
| product_id | client_id | creation_date       | payload1 | payload2 | payload3 | payload4 | payload5 | payload6 | payload7 | payload8 |
+------------+-----------+---------------------+----------+----------+----------+----------+----------+----------+----------+----------+
|   84419697 |         4 | 2021-03-07 16:06:51 | 1        | 2        | 3        | 4        | 5        | 6        | 7        | 8        |
+------------+-----------+---------------------+----------+----------+----------+----------+----------+----------+----------+----------+
--------------
prepare my_query_e from 'explain select * from products where client_id = ? order by product_id desc limit 1'
--------------
--------------
prepare my_query from 'select * from products where client_id = ? order by product_id desc limit 1'
--------------
--------------
set @client_id = 4
--------------
--------------
execute my_query_e using @client_id
--------------
+----+-------------+----------+------------+------+---------------+-----------+---------+-------+-------+----------+----------------+
| id | select_type | table    | partitions | type | possible_keys | key       | key_len | ref   | rows  | filtered | Extra          |
+----+-------------+----------+------------+------+---------------+-----------+---------+-------+-------+----------+----------------+
|  1 | SIMPLE      | products | NULL       | ref  | client_id     | client_id | 4       | const | 30264 |   100.00 | Using filesort |
+----+-------------+----------+------------+------+---------------+-----------+---------+-------+-------+----------+----------------+
--------------
execute my_query using @client_id
--------------
+------------+-----------+---------------------+----------+----------+----------+----------+----------+----------+----------+----------+
| product_id | client_id | creation_date       | payload1 | payload2 | payload3 | payload4 | payload5 | payload6 | payload7 | payload8 |
+------------+-----------+---------------------+----------+----------+----------+----------+----------+----------+----------+----------+
|   84419697 |         4 | 2021-03-07 16:06:51 | 1        | 2        | 3        | 4        | 5        | 6        | 7        | 8        |
+------------+-----------+---------------------+----------+----------+----------+----------+----------+----------+----------+----------+
--------------
SHOW PROFILES
--------------
+----------+------------+---------------------------------------------------------------------------------------------------------------+
| Query_ID | Duration   | Query                                                                                                         |
+----------+------------+---------------------------------------------------------------------------------------------------------------+
|        1 | 0.00032750 | explain select * from products where client_id = 4 order by product_id desc limit 1                           |
|        2 | 0.00021225 | select * from products where client_id = 4 order by product_id desc limit 1                                   |
|        3 | 0.00008350 | prepare my_query_e from 'explain select * from products where client_id = ? order by product_id desc limit 1' |
|        4 | 0.00004675 | prepare my_query from 'select * from products where client_id = ? order by product_id desc limit 1'           |
|        5 | 0.00002400 | set @client_id = 4                                                                                            |
|        6 | 0.00017000 | execute my_query_e using @client_id                                                                           |
|        7 | 0.04502325 | execute my_query using @client_id                                                                             |
+----------+------------+---------------------------------------------------------------------------------------------------------------+

Observe Explain plan (using filesort) and query time compared to when not using prepared statement.

Suggested fix:
Prepared statements should benefit from same optimizations as normal queries.
[18 May 2021 13:18] MySQL Verification Team
Hello Joffrey MICHAÏE,

Thank you for the report and test case.
Verified as described with 8.0.25 build.

Thanks,
Umesh
[4 Jun 2021 20:13] Jon Stephens
Documented fix as follows in the MySQL 8.0.26 changelog:

  Prepared statements did not always make use of index extensions
  (see 'Using Index Extensions' [link]).

See also BUG#103710.

Closed.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.054 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.