Mysql为什么锁定表后计数(*)性能非常快

Mysql为什么锁定表后计数(*)性能非常快,mysql,count,Mysql,Count,如果我不锁定表,则计数(*)的性能很差,如下所示: mysql>从标题中选择count(*); +----------+ |计数(*)| +----------+ | 443308 | +----------+ 一行一组(8.79秒) mysql>解释从标题中选择计数(*); +----+-------------+--------+------------+-------+---------------+---------+---------+------+--------+-------

如果我不锁定表,则计数(*)的性能很差,如下所示:

mysql>从标题中选择count(*);
+----------+
|计数(*)|
+----------+
|   443308 |
+----------+
一行一组(8.79秒)
mysql>解释从标题中选择计数(*);
+----+-------------+--------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
|id |选择|类型|表格|分区|类型|可能的|键|键|列|参考|行|过滤|额外|
+----+-------------+--------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
|1 |简单|标题|空|索引|空|主| 209 |空| 442843 | 100.00 |使用索引|
+----+-------------+--------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
设置1行,1条警告(0.00秒)
看起来很正常。但是当我锁上桌子时,计数(*)的表现太快了

mysql>锁定表标题读取;
查询正常,0行受影响(0.00秒)
mysql>从标题中选择计数(*);
+----------+
|计数(*)|
+----------+
|   443308 |
+----------+
一行一组(0.13秒)
锁定表后,count的操作会发生什么变化

更新日期:2021-5-29:

mysql>显示标题索引;
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
|表|非唯一|键|名称|索引中的顺序|列|名称|排序规则|基数|子|部分|压缩|空|索引|类型|注释|索引|注释|可见|表达式|
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
|标题| 0 |初级| 1 | emp | U no | A | 301411 |空|空| BTREE | | |是|空|
|标题| 0 |小学| 2 |标题| A | 441772 |空|空| b树|空|是|空|
|标题| 0 |小学| 3 |自| U日期| A | 442843 |空|空| BTREE | |是|空|
|标题| 1 | idx | u emp | no | 1 | emp | no | A | 300876 | NULL | NULL | BTREE | | YES | NULL|
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
一组4行(0.04秒)
mysql>解释从标题中选择计数(*);
+----+-------------+--------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+
|id |选择|类型|表格|分区|类型|可能的|键|键|列|参考|行|过滤|额外|
+----+-------------+--------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+
|1 |简单|标题|空|索引|空| idx | U emp |编号| 4 |空| 442835 | 100.00 |使用索引|
+----+-------------+--------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+
设置1行,1条警告(0.00秒)
mysql>从标题中选择计数(*);
+----------+
|计数(*)|
+----------+
|   443304 |
+----------+
一行一组(8.79秒)

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

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

该表未分区<代码>innodb_并行_读取_线程=4

并行执行

8.0有一些情况下使用多个线程进行查询。以下是更改日志中的一些条目。(尽管如此,它并没有解释为什么
锁表
是相关的。)

  • 8.0.14通过主键(cf innodb_Parallel_read_threads)并行扫描计数(*)w/o,其中
  • 8.0.17分区的并行扫描(cf innodb_Parallel_read_线程)
  • 8.0.20对MySQL 8.0.17中引入的并行读取线程功能的更改导致SELECT COUNT(*)性能下降。不必要地从磁盘读取页面。(Bug#30766089)

innodb\u parallel\u read\u线程的值是多少?(
显示变量,如'innodb_parallel_read_threads';

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

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

计数(expr)[over_条款]

从MySQL 8.0.13开始,
从tbl_name
查询性能中选择COUNT(*) for InnoDB表针对单线程工作负载进行了优化(如果有) 没有额外的子句,例如WHERE或GROUP BY

InnoDB通过遍历最小的 可用二级索引,除非
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)