我说 SELECT COUNT(*) 会造成全表扫描,面试官让我回去等通知!

2023-11-22 23:10

本文主要是介绍我说 SELECT COUNT(*) 会造成全表扫描,面试官让我回去等通知!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章来源于码海 ,作者码海

前言

有人提到使用以下 sql 会导致慢查询

SELECT COUNT(*) FROM SomeTable
SELECT COUNT(1) FROM SomeTable

原因是会造成全表扫描,有位读者说这种说法是有问题的,实际上针对无 where_clause 的 COUNT()*,MySQL 是有优化的,优化器会选择成本最小的辅助索引查询计数,其实反而性能最高,这位读者的说法对不对呢

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

EXPLAIN SELECT COUNT(*) FROM SomeTable

结果如下

image.png

如图所示: 发现确实此条语句在此例中用到的并不是主键索引,而是辅助索引,实际上在此例中我试验了,不管是 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()
begindeclare c_id integer default 1;while c_id<=100000 doinsert 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
image

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

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

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

用了全表扫描!理论上应该用 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' 

http://www.taodudu.cc/news/show-8351413.html

相关文章:

  • mysql全表扫描会涉及到io吗_我说 SELECT COUNT(*) 会造成全表扫描,面试官让我回去等通知...
  • 【PB】使用任务栏通知区域
  • reviewboard-腾讯企业邮箱邮件通知配置
  • APNS推送通知的流程
  • Android开发实现透明通知栏
  • JN516X 如何遥控一盏灯
  • 记一次支付宝接口收不到异步通知自查方案-支付宝接口常见错误系列
  • 1.4 微信Native支付 - 内网穿透与通知、查询用户订单
  • TBBUTTON 获取通知数据的易错点
  • Android 通知栏 PendingIntent.getActivity(context, requestCode, intent, flags)的第二个参数requestCode不能写一样
  • 软件测试人员到底需不需要通过培训来提升技术能力 ?
  • TSDK:淘宝开放平台或淘宝登录爬取
  • 郁闷的周末
  • social-app-django第三方登录qq 微信 微博 集成解决方案
  • iOS开发-ShareSDk社会化分享到微信微博QQ等功能
  • 优秀员工都用的-IDEA规范编码风格和质量检测插件SonarLint
  • 华为OD机试 - 优秀学员统计(Java JS Python)
  • (二十四)优秀员工 - 10
  • 《优秀员工》之《大腕》版
  • 转:优秀员工为什么优秀
  • 你看,优秀员工是这样离职的
  • 原来,FLAG的优秀员工会被这样奖励!
  • 老板喜欢这样的员工 成为优秀员工必读的89篇文章.pdf
  • 清华大学大数据软件团队2022年度先进个人风采展示
  • select 机制 - 访问方式(三)
  • 7 年 700 篇技术文章,收获的 7 个心得
  • 不知所以然
  • 梁某某
  • 移动的宽带上不了donews
  • DONEWS是设了技术门槛?
  • 这篇关于我说 SELECT COUNT(*) 会造成全表扫描,面试官让我回去等通知!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



    http://www.chinasem.cn/article/413593

    相关文章

    easyui 验证下拉菜单select

    validatebox.js中添加以下方法: selectRequired: {validator: function (value) {if (value == "" || value.indexOf('请选择') >= 0 || value.indexOf('全部') >= 0) {return false;}else {return true;}},message: '该下拉框为必选项'}

    多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

    目录 多路转接之select 引入 介绍 fd_set 函数原型 nfds readfds / writefds / exceptfds readfds  总结  fd_set操作接口  timeout timevalue 结构体 传入值 返回值 代码 注意点 -- 调用函数 select的参数填充  获取新连接 注意点 -- 通信时的调用函数 添加新fd到

    C++ I/O多路复用 select / poll / epoll

    I/O多路复用:在网络I/O中,用 1个或1组线程 管理 多个连接描述符。             如果有至少一个描述符准备就绪,就处理对应的事件             如果没有,就会被阻塞,让出CPU给其他应用程序运行,直到有准备就绪的描述符 或 超时

    Go Select的实现

    select语法总结 select对应的每个case如果有已经准备好的case 则进行chan读写操作;若没有则执行defualt语句;若都没有则阻塞当前goroutine,直到某个chan准备好可读或可写,完成对应的case后退出。 Select的内存布局 了解chanel的实现后对select的语法有个疑问,select如何实现多路复用的,为什么没有在第一个channel操作时阻塞 从而导

    Go 语言中Select与for结合使用break

    func test(){i := 0for {select {case <-time.After(time.Second * time.Duration(2)):i++if i == 5{fmt.Println("break now")break }fmt.Println("inside the select: ")}fmt.Println("inside the for: ")}} 执行后

    【吊打面试官系列-Redis面试题】说说 Redis 哈希槽的概念?

    大家好,我是锋哥。今天分享关于 【说说 Redis 哈希槽的概念?】面试题,希望对大家有帮助; 说说 Redis 哈希槽的概念? Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽, 集群的每个节点负责一部分 hash 槽。

    独立按键单击检测(延时消抖+定时器扫描)

    目录 独立按键简介 按键抖动 模块接线 延时消抖 Key.h Key.c 定时器扫描按键代码 Key.h Key.c main.c 思考  MultiButton按键驱动 独立按键简介 ​ 轻触按键相当于一种电子开关,按下时开关接通,松开时开关断开,实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通与断开。  ​ 按键抖动 由于按键内部使用的是机

    select、poll、epoll的区别

    select、poll、epoll均为linux中的多路复用技术。3种技术出现的顺序是select、poll、epoll,3个版本反应了多路复用技术的迭代过程。我们现在开发网络应用时, 一般都会使用多路复用,很少有用一个线程来监听一个fd的,其中epoll又是最常使用的。关于epoll的实现和常见问题可以参考epoll实现原理和常见问题总结。 当我们在使用epoll的时候,会想当然的认为这种技术

    js 获取select的值 / js动态给select赋值

    正常使用的: var month =  $('#month option:selected').val();//选中的值 var a = $('#month option:selected').text();//选中的文本 var b = $('#month option:selected') .val();//选中的值 var c = $("#month").get(0).selected