简体   繁体   English

为什么 SELECT COUNT(*) 比 MySQL 中带有 WHERE 子句的 SELECT 慢得多?

[英]Why SELECT COUNT(*) is much slower than SELECT with a WHERE clause in MySQL?

There's the table.桌子在那儿。

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
;

The id is an UUID and the data is take about 1Mb. id 是一个 UUID,数据大约需要 1Mb。 So the table is about 1.3G with 1.3M rows.所以这个表大约是 1.3G,有 130 万行。 Here's the result.这是结果。

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)

Why the previous query is much slower than the second query?为什么前一个查询比第二个查询慢得多?

PS There an app running and insert data. PS 有一个应用程序正在运行并插入数据。 Are there some reasons about the table lock?表锁有什么原因吗?


Here are explains.这里是解释。

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)


That is the expected behavior for InnoDB tables.这是 InnoDB 表的预期行为。

InnoDB engine supports transactions.So when you perform COUNT(*), the table must be fully scanned to avoid counting rows that are not yet commited. InnoDB 引擎支持事务。因此当您执行 COUNT(*) 时,必须对表进行全扫描以避免计算尚未提交的行。

But when you specify a filtering condition with WHERE only the filtered rows need to be counted and since UID in your case is an indexed column that is much faster.但是,当您使用WHERE指定过滤条件时,只需要计算过滤的行,因为在您的情况下UID是一个更快的索引列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM