【问题标题】:Mysql Why is the count(*) performance very fast after locking a tableMysql为什么加锁后count(*)性能非常快
【发布时间】:2021-05-27 18:09:11
【问题描述】:

如果我不锁定表,count(*) 的性能很糟糕,如下所示:

mysql> select count(*) from titles;
+----------+
| count(*) |
+----------+
|   443308 |
+----------+
1 row in set (8.79 sec)

mysql> explain select count(*) from titles;
+----+-------------+--------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table  | partitions | type  | possible_keys | key     | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | titles | NULL       | index | NULL          | PRIMARY | 209     | NULL | 442843 |   100.00 | Using index |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

看起来很正常。但是当我锁表的时候,count(*)的表现却是那么的快。

mysql> lock tables titles read;
Query OK, 0 rows affected (0.00 sec)

mysql> select count(*) from titles;
+----------+
| count(*) |
+----------+
|   443308 |
+----------+
1 row in set (0.13 sec)

锁表后count的操作会发生什么?

2021-5-29 更新:

mysql> show index from titles;
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table  | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| titles |          0 | PRIMARY    |            1 | emp_no      | A         |      301411 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| titles |          0 | PRIMARY    |            2 | title       | A         |      441772 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| titles |          0 | PRIMARY    |            3 | from_date   | A         |      442843 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| titles |          1 | idx_emp_no |            1 | emp_no      | A         |      300876 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
4 rows in set (0.04 sec)

mysql> explain select count(*) from titles;
+----+-------------+--------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+
| id | select_type | table  | partitions | type  | possible_keys | key        | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+--------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | titles | NULL       | index | NULL          | idx_emp_no | 4       | NULL | 442835 |   100.00 | Using index |
+----+-------------+--------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> select count(*) from titles;
+----------+
| count(*) |
+----------+
|   443304 |
+----------+
1 row in set (8.79 sec)

我创建了一个较小的索引。但它没有任何变化。

【问题讨论】:

  • 你能按相反的顺序试试这个吗(例如:第一个带锁的查询,然后没有锁的查询)它基本上可能是查询缓存的产物。此外,使用哪个表 ENGINE 可能会影响查询时间(INNODB MISAM)
  • @F.Igor。我试过很多次。没有什么不同的。它使用 InnoDB。也许我应该检查一下缓存是否正常?
  • 但是我没有打开查询缓存。也许当我锁定表时它会打开查询缓存?
  • @F.Igor - 如果涉及查询缓存,则不需要 130 毫秒,而更像是 1 毫秒。相反,它可能是 buffer_pool 做缓存。
  • 什么版本的mysql?

标签: mysql count


【解决方案1】:

我发现表锁和查询时间之间没有关系,但是查看Mysql手册我发现了一些线索:

  • COUNT(*) 是通过扫描最小的二级索引(如果有)来计算的,否则它使用聚集索引(主键或自动生成)
  • 锁定表可能会导致将所有记录更新到缓冲池中,因此它不需要执行额外的扫描然后快速返回
  • 您的主索引键长度为 209,这会使索引扫描变慢,并且任何 SELECT 语句都可能根据当前表和缓冲池状态进行不同的优化。 (尝试使用具有唯一行 ID 和附加索引的小型数字主键与当前主键中的所有字段)

您可以在以下链接中找到更多信息

https://dev.mysql.com/doc/refman/5.6/en/aggregate-functions.html#function_count

COUNT(expr) [over_clause]

截至 MySQL 8.0.13SELECT COUNT(*) FROM tbl_name 查询性能 InnoDB 表针对单线程工作负载进行了优化,如果有 没有额外的子句,例如 WHERE 或 GROUP BY。

InnoDB 通过遍历最小的处理SELECT COUNT(*) 语句 可用的二级索引,除非索引或优化器提示指示 优化器使用不同的索引。如果没有二级索引 目前,InnoDB 通过扫描 聚集索引。

如果索引记录,处理SELECT COUNT(*) 语句需要一些时间 不完全在缓冲池中。

如果大概的行数足够,请使用SHOW TABLE STATUS

InnoDB 处理 SELECT COUNT(*)SELECT COUNT(1) 中的操作 同样的方法。 没有性能差异

对于 MyISAM 表,COUNT(*) 经过优化,如果 SELECT 从一个表中检索,没有检索到其他列,并且 没有WHERE 子句。 此优化仅适用于 MyISAM 表,因为为此存储存储了精确的行数 引擎,可以非常快速地访问。 COUNT(1) 仅受制于 如果第一列定义为 NOT NULL,则进行相同的优化。

【讨论】:

  • 您好,我更新了我的问题。我添加了一个索引,但仍然需要超过 8 秒
  • 在哪里可以查看是否加载到缓冲池中?
【解决方案2】:

奇怪。我得到了相反的结果(使用 LOCK 慢 6 倍 ):

mysql> SELECT COUNT(*) FROM allcountries;
+----------+
| COUNT(*) |
+----------+
| 11082175 |
+----------+
1 row in set (0.26 sec)

mysql> LOCK TABLES allcountries READ;
mysql> SELECT COUNT(*) FROM allcountries;
+----------+
| COUNT(*) |
+----------+
| 11082175 |
+----------+
1 row in set (1.59 sec)

mysql> UNLOCK TABLES;

mysql> LOCK TABLES allcountries READ;
mysql> SELECT COUNT(*) FROM allcountries;
+----------+
| COUNT(*) |
+----------+
| 11082175 |
+----------+
1 row in set (1.50 sec)

mysql> UNLOCK TABLES;

mysql> SELECT COUNT(*) FROM allcountries;
+----------+
| COUNT(*) |
+----------+
| 11082175 |
+----------+
1 row in set (0.28 sec)

mysql> SELECT @@version;
+-------------------------+
| @@version               |
+-------------------------+
| 8.0.25-0ubuntu0.20.10.1 |
+-------------------------+
1 row in set (0.00 sec)

该表不是PARTITIONedinnodb_parallel_read_threads = 4.

并行执行

8.0 有一些使用多个线程进行查询的情况。以下是变更日志中的一些条目。 (不过,它并没有解释为什么 LOCK TABLES 是相关的。)

  • 8.0.14 按主键并行扫描 (cf innodb_parallel_read_threads) COUNT(*) w/o WHERE
  • 8.0.17 并行扫描分区 (cf innodb_parallel_read_threads)
  • 8.0.20 MySQL 8.0.17 中引入的并行读取线程功能的更改导致 SELECT COUNT(*) 性能下降。不必要地从磁盘读取页面。 (错误号 30766089)

innodb_parallel_read_threads 的值是多少? (SHOW VARIABLES LIKE 'innodb_parallel_read_threads';)

【讨论】:

  • 值为4。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-04
相关资源
最近更新 更多