Bug #102079 Handler_* counters are not incremented for parallel COUNT(*)

Submitted: 28 Dec 2020 18:49 Modified: 29 Dec 2020 8:06
Reporter: Justin Swanhart Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Information schema Severity:S2 (Serious)
Version:8.0.22 OS:Any
Assigned to: CPU Architecture:Any

[28 Dec 2020 18:49] Justin Swanhart
Description:
The handler_ status counters are used for monitoring, for example, to detect full table scans, but these counters are not incremented when a COUNT(*) without a WHERE clause is used on an InnoDB table in MySQL 8.  

I imagine this problem is related to the optimized COUNT(*) implementation that is used for InnoDB (parallel implementation) on MySQL 8.  When MyISAM uses fast count(*) it does not read from the table, so it shouldn't increment status counters for COUNT(*) w/out a WHERE clause.  InnoDB, however, still does read from the table, but the counters are not updated.

How to repeat:
mysql> drop database if exists test;
Query OK, 1 row affected (0.56 sec)

mysql> create database test;
Query OK, 1 row affected (0.00 sec)

mysql> use test;
Database changed

mysql> create table handler_test(c1 int primary key) engine=innodb;
Query OK, 0 rows affected (0.15 sec)

mysql> insert into handler_test values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
Query OK, 10 rows affected (0.01 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> flush status;
Query OK, 0 rows affected (0.00 sec)

-- should increment status variables (faster count(*) does read rows!)
mysql> select count(*) from handler_test;
+----------+
| count(*) |
+----------+
|       10 |
+----------+
1 row in set (0.00 sec)

-- status variables are not changed
mysql> show session status like '%handler%read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 0     |
| Handler_read_key      | 0     |
| Handler_read_last     | 0     |
| Handler_read_next     | 0     |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 0     |
+-----------------------+-------+
7 rows in set (0.02 sec)

-- should increment status variables
mysql> select count(*) from handler_test where c1 > 0;
+----------+
| count(*) |
+----------+
|       10 |
+----------+
1 row in set (0.00 sec)

-- status variables ARE incremented
mysql> show session status like '%handler%read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 1     |
| Handler_read_key      | 1     |
| Handler_read_last     | 0     |
| Handler_read_next     | 10    |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 0     |
+-----------------------+-------+
7 rows in set (0.02 sec)

mysql> alter table handler_test engine=myisam;
Query OK, 10 rows affected (0.05 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> flush status;
Query OK, 0 rows affected (0.00 sec)

-- status variables SHOULD NOT incremented (fast count(*) reads no rows)
mysql> select count(*) from handler_test;
+----------+
| count(*) |
+----------+
|       10 |
+----------+
1 row in set (0.00 sec)

-- results are correct
mysql> show session status like '%handler%read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 0     |
| Handler_read_key      | 0     |
| Handler_read_last     | 0     |
| Handler_read_next     | 0     |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 0     |
+-----------------------+-------+
7 rows in set (0.02 sec)

-- should increment status variables
mysql> select count(*) from handler_test where c1 > 0;
+----------+
| count(*) |
+----------+
|       10 |
+----------+
1 row in set (0.00 sec)

-- status variables ARE incremented
mysql> show session status like '%handler%read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 1     |
| Handler_read_key      | 0     |
| Handler_read_last     | 0     |
| Handler_read_next     | 10    |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 0     |
+-----------------------+-------+
7 rows in set (0.02 sec)

Suggested fix:
Increment the status counters in InnoDB parallel count(*) implementation, since rows are actually read.
[28 Dec 2020 19:31] Justin Swanhart
The 'Handler_read_first' status counter should be incremented when initializing the parallel scan context.

The 'Handler_read_next' status counter should be incremented here:
storage/innobase/row/row0pread.cc:583
[28 Dec 2020 19:33] Justin Swanhart
I suppose the row should be listed as being traversed even if the row is not visible, so the Handler_read_next counter should actually be incremented before
if(!skip) {
...
}
in row0pread.cc
[29 Dec 2020 8:06] MySQL Verification Team
Hello Justin,

Thank you for the report.

regards,
Umesh
[13 Jun 2024 14:29] Shane Bester
this also affects 8.0.0-dmr-enterprise-commercial-advanced
so I don't think it's to do with parallel count(*)

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

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