Почему SELECT COUNT (*) намного медленнее, чем SELECT с предложением WHERE в MySQL? - PullRequest
1 голос
/ 24 апреля 2020

Есть таблица.

CREATE TABLE `refdata` (
    `id` VARCHAR(36) NOT NULL COLLATE 'utf8_unicode_ci',
    `uid` INT NOT NULL,
    `data` TEXT NOT NULL COLLATE 'utf8_unicode_ci',
    `ref_akid` TEXT NOT NULL COLLATE 'utf8_unicode_ci',
    `ref_version` VARCHAR(16) NOT NULL COLLATE 'utf8_unicode_ci',
    `remote_addr` VARCHAR(32) NOT NULL COLLATE 'utf8_unicode_ci',
    `fetched_at` TIMESTAMP NULL,
    `created_at` TIMESTAMP NULL,
    PRIMARY KEY (`id`) USING BTREE,
    INDEX `link_to_user` (`uid`) USING BTREE,
    CONSTRAINT `link_to_user` FOREIGN KEY (`uid`) REFERENCES `selfdb`.`admin_users` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
;

Идентификатор представляет собой UUID, и данные занимают около 1 МБ. Таким образом, таблица составляет около 1,3 ГБ с 1,3 млн. Строк. Вот результат.

mysql> SELECT COUNT(*) FROM refdata;
+----------+
| COUNT(*) |
+----------+
|  1381991 |
+----------+
1 row in set (1 min 9.49 sec)

mysql> SELECT COUNT(*) FROM refdata WHERE uid > 0;
+----------+
| COUNT(*) |
+----------+
|  1382097 |
+----------+
1 row in set (0.29 sec)

Почему предыдущий запрос намного медленнее, чем второй?

PS Там приложение работает и вставляет данные. Есть ли какие-то причины для блокировки таблицы?


Вот пояснения.

mysql> explain SELECT COUNT(*) FROM refdata;
+----+-------------+------------+------------+-------+---------------+--------------+---------+------+---------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys | key          | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+------------+------------+-------+---------------+--------------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | refdata    | NULL       | index | NULL          | link_to_user | 4       | NULL | 1387770 |   100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+--------------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain SELECT COUNT(*) FROM refdata WHERE uid > 0;
+----+-------------+------------+------------+-------+---------------+---------------+---------+------+--------+----------+--------------------------+
| id | select_type | table      | partitions | type  | possible_keys | key           | key_len | ref  | rows   | filtered | Extra                    |
+----+-------------+------------+------------+-------+---------------+---------------+---------+------+--------+----------+--------------------------+
|  1 | SIMPLE      | refdata    | NULL       | range | link_to_user  | link_to_user  | 4       | NULL | 693885 |   100.00 | Using where; Using index |
+----+-------------+------------+------------+-------+---------------+---------------+---------+------+--------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain SELECT * FROM refdata;
+----+-------------+------------+------------+------+---------------+------+---------+------+---------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra |
+----+-------------+------------+------------+------+---------------+------+---------+------+---------+----------+-------+
|  1 | SIMPLE      | refdata    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 1387771 |   100.00 | NULL  |
+----+-------------+------------+------------+------+---------------+------+---------+------+---------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> explain SELECT * FROM refdata WHERE uid > 0;
+----+-------------+------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | refdata    | NULL       | ALL  | link_to_user  | NULL | NULL    | NULL | 1387774 |    50.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)


1 Ответ

0 голосов
/ 24 апреля 2020

Это ожидаемое поведение для таблиц InnoDB.

Движок InnoDB поддерживает транзакции. Поэтому при выполнении COUNT (*) таблица должна быть полностью отсканирована, чтобы избежать подсчета строк, которые еще не зафиксированы.

Но когда вы указываете условие фильтрации с помощью WHERE, необходимо учитывать только отфильтрованные строки, и, поскольку UID в вашем случае - это индексированный столбец, который работает намного быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...