SELECT COUNT(*)会不会导致全表扫描引起慢查询

news2025/2/27 15:03:26

SELECT COUNT(*)会不会导致全表扫描引起慢查询呢?

 

SELECT COUNT(*) FROM SomeTable  

网上有一种说法,针对无 where_clause 的 COUNT(*),MySQL 是有优化的,优化器会选择成本最小的辅助索引查询计数,其实反而性能最高,这种说法对不对呢

针对这个疑问,我首先去生产上找了一个千万级别的表使用  EXPLAIN 来查询了一下执行计划

 

EXPLAIN SELECT COUNT(*) FROM SomeTable  

结果如下

图片

图片

如图所示: 发现确实此条语句在此例中用到的并不是主键索引,而是辅助索引,实际上在此例中我试验了,不管是 COUNT(1),还是 COUNT(*),MySQL 都会用成本最小 的辅助索引查询方式来计数,也就是使用 COUNT(*) 由于 MySQL 的优化已经保证了它的查询性能是最好的!随带提一句,COUNT(*)是 SQL92 定义的标准统计行数的语法,并且效率高,所以请直接使用COUNT(*)查询表的行数!

所以这种说法确实是对的。但有个前提,在 MySQL 5.6 之后的版本中才有这种优化。

那么这个成本最小该怎么定义呢,有时候在 WHERE 中指定了多个条件,为啥最终 MySQL 执行的时候却选择了另一个索引,甚至不选索引?

本文将会给你答案,本文将会从以下两方面来分析

  • SQL 选用索引的执行成本如何计算

  • 实例说明

SQL 选用索引的执行成本如何计算

就如前文所述,在有多个索引的情况下, 在查询数据前,MySQL 会选择成本最小原则来选择使用对应的索引,这里的成本主要包含两个方面。

  • IO 成本: 即从磁盘把数据加载到内存的成本,默认情况下,读取数据页的 IO 成本是 1,MySQL 是以页的形式读取数据的,即当用到某个数据时,并不会只读取这个数据,而会把这个数据相邻的数据也一起读到内存中,这就是有名的程序局部性原理,所以 MySQL 每次会读取一整页,一页的成本就是 1。所以 IO 的成本主要和页的大小有关

  • CPU 成本:将数据读入内存后,还要检测数据是否满足条件和排序等 CPU 操作的成本,显然它与行数有关,默认情况下,检测记录的成本是 0.2。

实例说明

为了根据以上两个成本来算出使用索引的最终成本,我们先准备一个表(以下操作基于 MySQL 5.7.18)

 

CREATE TABLE `person` (  
  `id` bigint(20) NOT NULL AUTO_INCREMENT,  
  `name` varchar(255) NOT NULL,  
  `score` int(11) NOT NULL,  
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
  PRIMARY KEY (`id`),  
  KEY `name_score` (`name`(191),`score`),  
  KEY `create_time` (`create_time`)  
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;  

这个表除了主键索引之外,还有另外两个索引, name_score 及 create_time。然后我们在此表中插入 10 w 行数据,只要写一个存储过程调用即可,如下:

 

CREATE PROCEDURE insert_person()  
begin  
    declare c_id integer default 1;  
    while c_id<=100000 do  
    insert into person values(c_id, concat('name',c_id), c_id+100, date_sub(NOW(), interval c_id second));  
    set c_id=c_id+1;  
    end while;  
end  

插入之后我们现在使用 EXPLAIN 来计算下统计总行数到底使用的是哪个索引

 

EXPLAIN SELECT COUNT(*) FROM person  

图片

图片

从结果上看它选择了 create_time 辅助索引,显然 MySQL 认为使用此索引进行查询成本最小,这也是符合我们的预期,使用辅助索引来查询确实是性能最高的!

我们再来看以下 SQL 会使用哪个索引

 

SELECT * FROM person WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18'   

图片

图片

用了全表扫描!理论上应该用 name_score 或者 create_time 索引才对,从 WHERE 的查询条件来看确实都能命中索引,那是否是使用 SELECT * 造成的回表代价太大所致呢,我们改成覆盖索引的形式试一下

 

SELECT create_time FROM person WHERE NAME >'name84059' AND create_time > '2020-05-23 14:39:18'   

结果 MySQL 依然选择了全表扫描!这就比较有意思了,理论上采用了覆盖索引的方式进行查找性能肯定是比全表扫描更好的,为啥 MySQL 选择了全表扫描呢,既然它认为全表扫描比使用覆盖索引的形式性能更好,那我们分别用这两者执行来比较下查询时间吧

 

-- 全表扫描执行时间: 4.0 ms  
SELECT create_time FROM person WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18'   
  
-- 使用覆盖索引执行时间: 2.0 ms  
SELECT create_time FROM person force index(create_time) WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18'   

从实际执行的效果看使用覆盖索引查询比使用全表扫描执行的时间快了一倍!说明 MySQL 在查询前做的成本估算不准!我们先来看看 MySQL 做全表扫描的成本有多少。

前面我们说了成本主要 IO 成本和 CPU 成本有关,对于全表扫描来说也就是分别和聚簇索引占用的页面数和表中的记录数。执行以下命令

 

SHOW TABLE STATUS LIKE 'person'  

图片

图片

可以发现

  1. 行数是 100264,我们不是插入了 10 w 行的数据了吗,怎么算出的数据反而多了,其实这里的计算是估算 ,也有可能这里的行数统计出来比 10 w 少了,估算方式有兴趣大家去网上查找,这里不是本文重点,就不展开了。得知行数,那我们知道 CPU 成本是 100264 * 0.2 = 20052.8

  2. 数据长度是 5783552,InnoDB 每个页面的大小是 16 KB,可以算出页面数量是 353。

也就是说全表扫描的成本是 20052.8 + 353 = 20406

这个结果对不对呢,我们可以用一个工具验证一下。在 MySQL 5.6 及之后的版本中,我们可以用 optimizer trace 功能来查看优化器生成计划的整个过程 ,它列出了选择每个索引的执行计划成本以及最终的选择结果,我们可以依赖这些信息来进一步优化我们的 SQL。

optimizer_trace 功能使用如下

 

SET optimizer_trace="enabled=on";  
SELECT create_time FROM person WHERE NAME >'name84059' AND create_time > '2020-05-23 14:39:18';  
SELECT * FROM information_schema.OPTIMIZER_TRACE;  
SET optimizer_trace="enabled=off";  

执行之后我们主要观察使用 name_scorecreate_time 索引及全表扫描的成本。

先来看下使用 name_score 索引执行的的预估执行成本:

 

{  
    "index": "name_score",  
    "ranges": [  
      "name84059 <= name"  
    ],  
    "index_dives_for_eq_ranges": true,  
    "rows": 25372,  
    "cost": 30447  
}  

可以看到执行成本为 30447,高于我们之前算出来的全表扫描成本:20406。所以没选择此索引执行

注意:这里的 30447 是查询二级索引的 IO 成本和 CPU 成本之和,再加上回表查询聚簇索引的 IO 成本和 CPU 成本之和。

再来看下使用 create_time 索引执行的的预估执行成本:

 

{  
    "index": "create_time",  
    "ranges": [  
      "0x5ec8c516 < create_time"  
    ],  
    "index_dives_for_eq_ranges": true,  
    "rows": 50132,  
    "cost": 60159,  
    "cause": "cost"  
}  

可以看到成本是 60159,远大于全表扫描成本 20406,自然也没选择此索引。

再来看计算出的全表扫描成本:

 

{  
    "considered_execution_plans": [  
      {  
        "plan_prefix": [  
        ],  
        "table": "`person`",  
        "best_access_path": {  
          "considered_access_paths": [  
            {  
              "rows_to_scan": 100264,  
              "access_type": "scan",  
              "resulting_rows": 100264,  
              "cost": 20406,  
              "chosen": true  
            }  
          ]  
        },  
        "condition_filtering_pct": 100,  
        "rows_for_plan": 100264,  
        "cost_for_plan": 20406,  
        "chosen": true  
      }  
    ]  
}  

注意看 cost:20406,与我们之前算出来的完全一样!这个值在以上三者算出的执行成本中最小,所以最终 MySQL 选择了用全表扫描的方式来执行此 SQL。

实际上 optimizer trace 详细列出了覆盖索引,回表的成本统计情况,有兴趣的可以去研究一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1087367.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

支付宝企业转账到个人账号[新接口版](php源码,亲测)

前言 之前专栏写过一篇企业支付宝转账到个人的文章,里面用的是老接口,官方已经不再维护。最近有人找到帮忙使用新接口实现这个功能,看了下文档以及官方的sdk,为了这一个接口,我还要去下载官方庞大的sdk,而且php低版本的还不支持composer,就很离谱,经过一天的研究,把单…

科技为饮食带来创新,看AI如何打造智能营养时代

在当今社会&#xff0c;快节奏的生活方式、便捷的食品选择以及现代科技的快速发展正深刻地重塑着我们对健康的认知和实践&#xff0c;它已经不再仅仅是一个话题&#xff0c;而是一个备受关注的社会焦点。在这个纷繁复杂的交汇点上&#xff0c;AI技术的介入为我们开辟了前所未有…

项目管理包含哪些内容?

做好项目管理不是一件容易的事儿&#xff0c;只有掌握了正确的技巧&#xff0c;才能事半功倍地完成项目。 下面就按照项目管理的流程来讲一讲项目管理的主要内容包括哪些、如何做好项目管理。 项目管理是指运用系统的理论方法&#xff0c;在有限的条件和资源下&#xff0c;对…

Google SGE 正在添加人工智能图像生成器,现已推出:从搜索中的生成式 AI 中获取灵感的新方法

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

学信息系统项目管理师第4版系列25_项目绩效域(上)

1. 绩效评估是指以员工与组织的共同发展为目标&#xff0c;通过正式的结构化的制度或方法&#xff0c;评价和测量在一定的周期内团队或员工个人的工作行为和工作成果&#xff0c;全面了解员工的发展潜力 1.1. 【高23上选13】 2. 干系人绩效域 2.1. 涉及与干系人相关的活动和…

react native app开发环境搭建

Reactjs是一个响应式的、多场景的web前后端应用开发框架&#xff0c;react native是一种使用reactjs开发框架与native移动设备应用开发技术混合开发的技术&#xff0c;使用react native技术开发出的应用支持跨设备、多终端&#xff0c;本文主要描述react native开发环境搭建。 …

1.Cesium For Unity插件安装

1.Unity安装 先安装Unity Hub,然后在里面安装Unity。 2.新建工程 新建项目 选择项目类型 3. 导入Cesium包 打开新建的项目&#xff0c;在菜单栏选择Edit->Project Settings->Package Manager.并添加Cesium的包信息。 Name: Cesium URL: https://unity.pkg.cesium.com Sco…

CRM系统:快速实现外勤出差人员远程访问企业提升工作效率!

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 快速实现外勤出差人员远程访问企业CRM系统前言1. 无需公网IP&#xff0c;高效低成本实现CRM系统远程访问1.1 下…

推荐5款小众软件,感兴趣自行下载

​ 今天推荐5款十分小众的软件&#xff0c;知道的人不多&#xff0c;但是每个都是非常非常好用的&#xff0c;有兴趣的小伙伴可以自行搜索下载。 1.重复文件清理——Duplicate Cleaner ​ Duplicate Cleaner 是一款专业的重复文件清理工具&#xff0c;可以快速扫描电脑上的重…

【深度学习】DDPM,Diffusion,概率扩散去噪生成模型,原理解读

看过来看过去&#xff0c;唯有此up主&#xff0c;非常牛&#xff1a; Video Explaination(Chinese) 1. DDPM Introduction q q q - 一个固定&#xff08;或预定义&#xff09;的正向扩散过程&#xff0c;逐渐向图像添加高斯噪声&#xff0c;直到最终得到纯噪声。 p θ p_θ p…

JavaScript基础入门

javaScript基础知识 $的作用 如果在jquery框架里面的话它代表jquery本身。 其它时候它只是一个变量名&#xff0c;仅此而已。 比如 var $ function(id) { return document.getElementById(id); }; 那么现在 就代表一个函数了&#xff0c;直接 就代表一个函数了&#xff0c;直…

centos下安装elasticsearch-head

1、安装npm sudo yum install npm 2、下载elasticsearch-head cd /home/packages sudo git clone https://github.com/mobz/elasticsearch-head.git 3、将npm镜像换为国内镜像 npm config set registry http://registry.npm.taobao.org/ 4、安装phantomjs sudo npm install p…

亚马逊英国儿童玩具合规政策合规标准是什么?如何办理?

亚马逊儿童玩具合规政策更新 重点 名称 日期 政策实施日期 2023年8月16日 产品下架日期 2023年10月15日 儿童玩具 英国 01 本政策适用的儿童玩具 02 亚马逊儿童玩具政策 03 办理流程 本政策适用的儿童玩具 儿童玩具是指供 14 岁或以下儿童在学习或玩耍时使用的商…

第14章总结:lambda表达式与处理

14.1&#xff1a; lambada表达式 14.1.1&#xff1a;lambada表达式简介 无参数 package fourteen; interface SayhHi{ String say();//抽象方法接口 } public class NoParamDemo { public static void main(String[] args) { //无参数 …

JMeter三种常用的逻辑控制器

一. 如果&#xff08;if&#xff09;控制器 1.1 使用js语法来判断&#xff1a; ## 1.2 使用jexl3函数来判断性能好&#xff1a; 二. 循环控制器 三. ForEach控制器 ForEach控制器一般和用户自定义变量或者正则表达式提取器一起使用&#xff0c;其在用户自定义变量或者从正…

C++ (Chapter 2)

C(二) 1.缺省参数 在C中,在函数定义的时候,可以为形参指定一个默认值,也称作缺省值.如果在调用这个函数时没有传参,那么函数在执行的时候就采用该实参的缺省值,否则使用实参的值. 例如: #include<iostream> using namespace std; void Add(int x, int y 10) {cout &…

结合源码聊一聊为何线上RocketMQ偶尔出现system busy

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 RocketMQ 版本 5.1.0 背景 继之前研究过的RocketMQ发送消息还有这种坑&#xff1f;遇到SYSTEM_BUSY不重试&#xff1f; 今天我们来分析分析RocketMQ什么情况下…

从概念到现实:ChatGPT 和 Midjourney 的设计之旅

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 在现代技术的世界中&…

媒体基础:打开多模态大模型的新思路

编者按&#xff1a;2023年是微软亚洲研究院建院25周年。25年来&#xff0c;微软亚洲研究院探索并实践了一种独特且有效的企业研究院的新模式&#xff0c;并以此为基础产出了诸多对微软公司和全球社会都有积极影响的创新成果。一直以来&#xff0c;微软亚洲研究院致力于创造具有…

【Js】数据处理

一、对象 1&#xff09;、Object. hasOwnProperty&#xff08;&#xff09; hasOwnProperty() 方法会返回一个布尔值&#xff0c;指示对象自身属性中&#xff08;非继承属性&#xff09;是否具有指定的属性&#xff0c; 如果 object 具有带指定名称的属性&#xff0c;则 hasOwn…