Bug #119066 MySQL 8.0.40+ is about 2-3x times slower than 8.0.39 in particular index scan scenario
Submitted: 24 Sep 13:06 Modified: 24 Sep 13:59
Reporter: Przemyslaw Malkowski Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:8.0.40+ OS:Any
Assigned to: CPU Architecture:Any
Tags: regression

[24 Sep 13:06] Przemyslaw Malkowski
Description:
When a SELECT query uses a compound key for an equality check with sorting by another column, and the filtered value matches all rows, the query becomes much slower since MySQL version 8.0.40. 

Interestingly, the difference is even bigger with the FORCE KEY hint, even though the plan is not changed.
Funny fact - estimated rows are also different when the hint is used.

The EXPLAIN plan is identical across all versions, and looks like this:

mysql [localhost:8040] {msandbox} (test) > explain SELECT b, dt FROM test.test_101 FORCE KEY(i1) WHERE b = 1000 ORDER BY dt ASC LIMIT 1;
+----+-------------+----------+------------+------+---------------+------+---------+-------+-------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref   | rows  | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+-------+-------+----------+-------------+
|  1 | SIMPLE      | test_101 | NULL       | ref  | i1            | i1   | 4       | const | 12635 |   100.00 | Using index |
+----+-------------+----------+------------+------+---------------+------+---------+-------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql [localhost:8040] {msandbox} (test) > explain SELECT b, dt FROM test.test_101 WHERE b = 1000 ORDER BY dt ASC LIMIT 1;
+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | test_101 | NULL       | ref  | i1            | i1   | 4       | const | 6317 |   100.00 | Using index |
+----+-------------+----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql [localhost:8040] {msandbox} (test) > show create table test.test_101\G
*************************** 1. row ***************************
       Table: test_101
Create Table: CREATE TABLE `test_101` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `a` smallint NOT NULL,
  `dt` datetime NOT NULL,
  `b` int unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `i1` (`b`,`dt`)
) ENGINE=InnoDB AUTO_INCREMENT=12418 DEFAULT CHARSET=utf8mb3
1 row in set (0.00 sec)

mysql [localhost:8040] {msandbox} (test) > select count(*), count(distinct(b)) from test_101;
+----------+--------------------+
| count(*) | count(distinct(b)) |
+----------+--------------------+
|    12417 |                  1 |
+----------+--------------------+
1 row in set (0.00 sec)

mysql [localhost:8040] {msandbox} (test) > select count(*) from test.test_101 where b = 1000;
+----------+
| count(*) |
+----------+
|    12417 |
+----------+
1 row in set (0.01 sec)

How to repeat:
Prepare sandboxes with different versions, with all default settings:

$ dbdeployer deploy single 8.0.39
Database installed in $HOME/sandboxes/msb_8_0_39
run 'dbdeployer usage single' for basic instructions'
. sandbox server started

$ dbdeployer deploy single 8.0.40
Database installed in $HOME/sandboxes/msb_8_0_40
run 'dbdeployer usage single' for basic instructions'
. sandbox server started

$ dbdeployer deploy single 8.0.43
Database installed in $HOME/sandboxes/msb_8_0_43
run 'dbdeployer usage single' for basic instructions'
. sandbox server started

Load the example table:

$ msb_8_0_39/use test < test_101.sql
$ msb_8_0_40/use test < test_101.sql
$ msb_8_0_43/use test < test_101.sql

Run benchmark - 10k times the same query, in a single session:

$ for ver in 39 39 40 40 43 43; do echo "MySQL version 8.0.$ver"; mysqlslap --socket=/tmp/mysql_sandbox80$ver.sock --user=msandbox --password=msandbox --create-schema=test --concurrency=1 --query="SELECT b, dt FROM test.test_101 FORCE KEY(i1) WHERE b = 1000 ORDER BY dt DESC LIMIT 1" --number-of-queries=10000 --iterations=1; done

MySQL version 8.0.39
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
	Average number of seconds to run all queries: 0.848 seconds
	Minimum number of seconds to run all queries: 0.848 seconds
	Maximum number of seconds to run all queries: 0.848 seconds
	Number of clients running queries: 1
	Average number of queries per client: 10000

MySQL version 8.0.39
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
	Average number of seconds to run all queries: 0.849 seconds
	Minimum number of seconds to run all queries: 0.849 seconds
	Maximum number of seconds to run all queries: 0.849 seconds
	Number of clients running queries: 1
	Average number of queries per client: 10000

MySQL version 8.0.40
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
	Average number of seconds to run all queries: 3.547 seconds
	Minimum number of seconds to run all queries: 3.547 seconds
	Maximum number of seconds to run all queries: 3.547 seconds
	Number of clients running queries: 1
	Average number of queries per client: 10000

MySQL version 8.0.40
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
	Average number of seconds to run all queries: 3.529 seconds
	Minimum number of seconds to run all queries: 3.529 seconds
	Maximum number of seconds to run all queries: 3.529 seconds
	Number of clients running queries: 1
	Average number of queries per client: 10000

MySQL version 8.0.43
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
	Average number of seconds to run all queries: 3.134 seconds
	Minimum number of seconds to run all queries: 3.134 seconds
	Maximum number of seconds to run all queries: 3.134 seconds
	Number of clients running queries: 1
	Average number of queries per client: 10000

MySQL version 8.0.43
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
	Average number of seconds to run all queries: 3.158 seconds
	Minimum number of seconds to run all queries: 3.158 seconds
	Maximum number of seconds to run all queries: 3.158 seconds
	Number of clients running queries: 1
	Average number of queries per client: 10000

Suggested fix:
Apparently, some side regression was introduced in 8.0.40, which affects this special case terribly, and it should be addressed.
[24 Sep 13:07] Przemyslaw Malkowski
Table dump for reproducible test case; random data

Attachment: test_101.sql.gz (application/gzip, text), 112.20 KiB.

[24 Sep 13:20] Przemyslaw Malkowski
Btw, the very same test is about ~40% faster on 5.7.44:

$ mysqlslap --socket=/tmp/mysql_sandbox5744.sock --user=msandbox --password=msandbox --create-schema=test --concurrency=1 --query="SELECT b, dt FROM test.test_101 FORCE KEY(i1) WHERE b = 1000 ORDER BY dt DESC LIMIT 1" --number-of-queries=10000 --iterations=1;
mysqlslap: [Warning] Using a password on the command line interface can be insecure.
Benchmark
	Average number of seconds to run all queries: 0.593 seconds
	Minimum number of seconds to run all queries: 0.593 seconds
	Maximum number of seconds to run all queries: 0.593 seconds
	Number of clients running queries: 1
	Average number of queries per client: 10000
[24 Sep 13:59] Przemyslaw Malkowski
In the last comment, I meant that 5.7.44 is ~40% faster than 8.0.39.
But when we compare 8.0.40 to 5.7.44, it's even ~6x times slower :(