程序员初学者频繁使用count(*),被组长批评后怒怼:性能并不拉垮

MySQL select count(*)计数很慢优化方案 这篇文章主要介绍了MySQL select count(*)计数很慢优化方案,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下在日常开发工作中,我经常会遇到需要统计总数的场景,比如:统计订单总数、统计用户总数等。一般我们会使用MySQL 的count函数进行统计,但是随着数据量逐渐增大,统计耗时也越来越长,最后竟然出现慢查询的情况,这究竟是什么原因呢?本篇文章带你一下学习一下。 阅读详情

在我们平时查询数据库表记录行数的时候,经常会使用到count()函数,比如使用count(*)、count(1)或者count(某个主键或索引列),今天我们来对比下这些用法中哪个性能最优秀!

创建短信表

比如说,你有一张短信表(sms),里面放了各种需要发送的短信信息。

sms建表sql

sms表

需要注意的是state字段,为0的时候说明这时候短信还未发送。

此时还会有一个异步线程不断的捞起未发送(state=0)的短信数据,执行发短信操作,发送成功之后state字段会被置为1(已发送)。也就是说未发送的数据会不断变少。

异步线程发送短信

假设由于某些原因,你现在需要做一些监控,比如监控的内容是,你的sms数据表里还有没有state=0(未发送)的短信,方便判断一下堆积的未发送短信大概在什么样的一个量级。

为了获取满足某些条件的行数是多少,我们一般会使用count()方法。

这时候为了获取未发送的短信数据,我们很自然就想到了使用下面的sql语句进行查询。

select count(*) from sms where state = 0;

然后再把获得数据作为打点发给监控服务。

当数据表小的时候,这是没问题的,但当数据量大的时候,比如未发送的短信到了百万量级的时候,你就会发现,上面的sql查询时间会变得很长,最后timeout报错,查不出结果了。

为什么?

我们先从count()方法的原理聊起。

count()的原理

count()方法的目的是计算当前sql语句查询得到的非NULL的行数。

我们知道mysql是分为server层和存储引擎层的。

Mysql架构

存储引擎层里可以选择各种引擎进行存储,最常见的是innodb、myisam。具体使用哪个存储引擎,可以通过建表sql里的ENGINE字段进行指定。比如这篇文章开头的建表sql里用了ENGINE=InnoDB,那这张表用的就是innodb引擎。

虽然在server层都叫count()方法,但在不同的存储引擎下,它们的实现方式是有区别的。

比如同样是读全表数据 select count(*) from sms;语句。

使用 myisam引擎的数据表里有个记录当前表里有几行数据的字段,直接读这个字段返回就好了,因此速度快得飞起。

而使用innodb引擎的数据表,则会选择体积最小的索引树,然后通过遍历叶子节点的个数挨个加起来,这样也能得到全表数据。

因此回到文章开头的问题里,当数据表行数变大后,单次count就需要扫描大量的数据,因此很可能就会出现超时报错。

那么问题就来了。

为什么innodb不能像myisam那样实现count()方法

myisam和innodb这两个引擎,有几个比较明显的区别,这个是八股文常考了。

其中最大的区别在于myisam不支持事务,而innodb支持事务。

而事务,有四层隔离级别,其中默认隔离级别就是可重复读隔离级别(RR)。

四层隔离级别

innodb引擎通过MVCC实现了可重复隔离级别,事务开启后,多次执行同样的select快照读,要能读到同样的数据。

于是我们看个例子。

为什么innodb不单独记录表行数

对于两个事务A和B,一开始sms表假设就2条数据,那事务A一开始确实是读到2条数据。事务B在这期间插入了1条数据,按道理数据库其实有3条数据了,但由于可重复读的隔离级别,事务A依然还是只能读到2条数据。

因此由于事务隔离级别的存在,不同的事务在同一时间下,看到的表内数据行数是不一致的,因此innodb,没办法,也没必要像myisam那样单纯的加个count字段信息在数据表上。

那如果不可避免要使用count(),有没有办法让它快一点?

各种count()方法的原理

count()的括号里,可以放各种奇奇怪怪的东西,想必大家应该看过,比如放个星号*,放个1,放个索引列啥的。

我们来分析下他们的执行流程。

count方法的大原则是server层会从innodb存储引擎里读来一行行数据,并且只累计非null的值。但这个过程,根据count()方法括号内的传参,有略有不同。

count(*)

server层拿到innodb返回的行数据,不对里面的行数据做任何解析和判断,默认取出的值肯定都不是null,直接行数+1。

count(1)

server层拿到innodb返回的行数据,每行放个1进去,默认不可能为null,直接行数+1.

count(某个列字段)

由于指明了要count某个字段,innodb在取数据的时候,会把这个字段解析出来返回给server层,所以会比count(1)和count(*)多了个解析字段出来的流程。

  • 如果这个列字段是主键id,主键是不可能为null的,所以server层也不用判断是否为null,innodb每返回一行,行数结果就+1.

  • 如果这个列是普通索引字段,innodb一般会走普通索引,每返回一行数据,server层就会判断这个字段是否为null,不是null的情况下+1。当然如果建表sql里字段定义为not null的话,那就不用做这一步判断直接+1。

  • 如果这个列没有加过索引,那innodb可能会全表扫描,返回的每一行数据,server层都会判断这个字段是否为null,不是null的情况下+1。同上面的情况一样,字段加了not null也就省下这一步判断了。

理解了原理后我们大概可以知道他们的性能排序是

count(*) ≈ count(1) > count(主键id) > count(普通索引列) > count(未加索引列)

所以说count(*),已经是最快的了。

知道真相的我眼泪掉下来。

那有没有其他更好的办法?

允许粗略估计行数的场景

我们回过头来细品下文章开头的需求,我们只是希望知道数据库里还有多少短信是堆积在那没发的,具体是1k还是2k其实都是差不多量级,等到了百万以上,具体数值已经不重要了,我们知道它现在堆积得很离谱,就够了。因此这个场景,其实是允许使用比较粗略的估计的。

那怎么样才能获得粗略的数值呢?

还记得我们平时为了查看sql执行计划用的explain命令不。

其中有个rows,会用来估计接下来执行这条sql需要扫描和检查多少行。它是通过采样的方式计算出来的,虽然会有一定的偏差,但它能反映一定的数量级。

explain里的rows

有些语言的orm里可能没有专门的explain语法,但是肯定有执行raw sql的功能,你可以把explain语句当做raw sql传入,从返回的结果里将rows那一列读出来使用。

一般情况下,explain的sql如果能走索引,那会比不走索引的情况更准 。单个字段的索引会比多个字段组成的复合索引要准。索引区分度越高,rows的值也会越准。

这种情况几乎满足大部分的监控场景。但总有一些场景,它要求必须得到精确的行数,这种情况该怎么办呢?

必须精确估计行数的场景

这种场景就比较头疼了,但也不是不能做。

我们可以单独拉一张新的数据库表,只为保存各种场景下的count。

CREATE TABLE `count_table` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `cnt_what` char(20) NOT NULL DEFAULT '' COMMENT '各种需要计算的指标',
  `cnt` tinyint NOT NULL COMMENT 'cnt指标值',
  PRIMARY KEY (`id`),
  KEY `idx_cnt_what` (`cnt_what`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4;

count_table表保存各种场景下的count

当需要获取某个场景下的cout值时,可以使用下面的sql进行直接读取,快得飞起。

select cnt from count_table where cnt_what = "未发送的短信数量"; 

那这些count的结果值从哪来呢?

这里分成两种情况。

实时性要求较高的场景

如果你对这个cnt计算结果的实时性要求很高,那你需要将更新cnt的sql加入到对应变更行数的事务中。

比如我们有两个事务A和B,分别是增加未发送短信和减少未发送短信。

将更改表行数的操作放入到事务里

这样做的好处是事务内的cnt行数依然符合隔离级别,事务回滚的时候,cnt的值也会跟着回滚。

坏处也比较明显,多个线程对同一个cnt进行写操作,会触发悲观锁,多个线程之间需要互相等待。对于高频写的场景,性能会有折损。

实时性没那么高的场景

如果实时性要求不高的话,比如可以一天一次,那你可以通过全表扫描后做计算。

举个例子,比如上面的短信表,可以按id排序,每次取出1w条数据,记下这一批里最大的id,然后下次从最大id开始再拿1w条数据出来,不断循环。

对于未发送的短信,就只需要在捞出的那1w条数据里,筛选出state=0的条数。

batch分批获取短信表

当然如果有条件,这种场景最好的方式还是消费binlog将数据导入到hive里,然后在hive里做查询,不少公司也已经有现成的组件可以做这种事情,不用自己写脚本,岂不美哉。

mysql同步hive

总结

  • mysql用count方法查全表数据,在不同的存储引擎里实现不同,myisam有专门字段记录全表的行数,直接读这个字段就好了。而innodb则需要一行行去算。

  • 性能方面 count(*) ≈ count(1) > count(主键id) > count(普通索引列) > count(未加索引列),但哪怕是性能最好的count(*),由于实现上就需要一行行去算,所以数据量大的时候就是不给力。

  • 如果确实需要获取行数,且**可以接受不那么精确的行数(只需要判断大概的量级)**的话,那可以用explain里的rows,这可以满足大部分的监控场景,实现简单。

  • 如果要求行数准确,可以建个新表,里面专门放表行数的信息。

  • 如果对实时性要求比较高的话,可以将更新行数的sql放入到对应事务里,这样既能满足事务隔离性,还能快速读取到行数信息。

  • 如果对实时性要求不高,接受一小时或者一天的更新频率,那既可以自己写脚本遍历全表后更新行数信息。也可以将通过监听binlog将数据导入hive,需要数据时直接通过hive计算得出。

SQL优化总结,分页优化、关联优化、count(*)优化等 1、分页查询优化 这种改写得满 足以下两个条件: 主键自增且连续 结果是按照主键排序的 连续主键排序的分页查询: select * from employees limit 90000,5; 该 SQL 表示查询从第 90001开始的五行数据,没添加单独 order by,表示通过主键排序。我们再看表 employees ,因 为主键是自增并且连续的,所以可以改写成按照主键去查询从第 90001开始的五行数据 该 SQL 表示查询从第 90001开始的五行数据,没添加单独 order by,表示通过 阅读详情

相关推荐

可执行规则工程:从原始数据到业务动作的七步落地法

可执行规则(Actionable Rules)是将原始数据转化为业务决策的关键桥梁,其本质不是统计模型的副产品,而是业务逻辑的结构化压缩。它需满足可识别、可干预、可验证三大硬性条件,依赖字段语义化、时间粒度对齐、增量置信度校准等工程化手段实现稳定落地。相比黑箱预测模型,可执行规则更强调与现有系统兼容、动作原子化封装及人机协同闭环,广泛应用于智能运维、精准营销、风险控制等实时决策场景。本文聚焦规则从日志到指令的完整工业化路径,尤其深入解析actionable rules的业务可行性约束与rule mining

weixin_33912638的博客 456

MySQL使用count(*) 很慢

count(*)慢

卷心菜投手 7628

银行级机器学习部署:从模型上线到系统性风控的工程实践

机器学习模型部署不是代码打包上线的技术动作,而是面向高可用、低延迟、强合规生产环境的系统性工程。其核心在于应对数据漂移、特征失效、跨系统依赖断裂等不确定性风险,关键技术包括可解释模型选型、特征时效性分级、幂等重试与有感知降级、全链路时间统一及决策审计日志。在金融场景中,模型必须满足监管可追溯、业务可干预、故障可自愈三大刚性要求。本文聚焦银行级ML系统落地中的真实挑战与工程解法,覆盖部署集成、性能压测、漂移监控、合规验证与治理审计全链路。

dy_f的专栏 385

为什么 mysql 的 count() 方法这么慢?

兄弟们。浅浅的炫个富吧。说出来你们可能不信。手机你们有吗?我有。短信,知道吧?一条一毛钱,我天天发。你敢想吗?所以说,年轻人,有钱是真的好。今天,我们就以短信为话题聊起。短信,它又叫SMS。比如说,你有一张短信表(sms),里面放了各种需要发送的短信信息。sms建表sqlsms表需要注意的是state字段,为0的时候说明这时候短信还未发送。此时还会有一个异步线程不断的捞起...

EDDYCJY的博客 3895

银行级机器学习系统:从模型上线到生产稳定的全链路工程实践

机器学习模型部署不是代码打包上线,而是将算法嵌入复杂业务系统的工程化过程。其核心在于理解数据如何流动、服务如何响应、故障如何收敛——这涉及特征管道的时效性保障、跨系统协议的严格契约、毫秒级延迟下的确定性行为,以及数据漂移与业务结果之间的因果映射。在强监管、高可用、低容错的金融场景中,模型必须具备可审计的决策链、可回滚的版本控制、可解释的输出结构和可协同的异常SOP。真正的MLOps能力,体现在当`last_30d_transaction_count`空值率飙升至47%时,系统能否自动降级并触发精准告警;也体

434

需求设计文档:软件工程中的认知对齐与风险控制协议

需求设计文档不是产品交付给开发的说明书,而是保障多方认知一致、约束技术实现、承载验收依据的工程契约。其核心原理在于对抗需求传递过程中的信息熵增,通过结构化描述用户场景、交互状态、数据规则与非功能指标,实现业务意图到系统行为的无损转译。技术价值体现在降低返工率、前置暴露边界风险、量化验收标准并明确协作责任。典型应用场景覆盖中大型敏捷团队的需求评审、跨职能UAT对齐、合规性交付及高可用系统建设。本文深入剖析一份可落地的设计文档必须包含的七大刚性模块及其工程实践方法。

dianlu5775的博客 366

程序员新人频繁使用count(*),被组长批评后怒怼:性能并不拉垮!

mysql用count方法查全表数据,在不同的存储引擎里实现不同,myisam有专门字段记录全表的行数,直接读这个字段就好了。而innodb则需要一行行去算。性能方面count(*) ≈ count(1) > count(主键id) > count(普通索引列) > count(未加索引列),但哪怕是性能最好的count(*),由于实现上就需要一行行去算,所以数据量大的时候就是不给力。如果确实需要获取行数,且可以接受不那么精确的行数(只需要判断大概的量级)

IT界那些事儿 1万+

企业级AI编码落地的17道质检关卡与安全等高线

AI代码生成已从概念验证迈入企业生产环境,其核心挑战并非模型能否写出代码,而在于生成结果能否通过严苛的工程化质量门禁。围绕agentic coding和enterprise AI两大热词,本文揭示从单点智能到系统智能演进中必须跨越的可靠性断层:静态扫描、安全审计、合规校验、可观测性保障等17项刚性流程,本质是将大模型的不确定性纳入可审计、可回滚、可归责的软件交付体系。技术价值体现在降低AI引入带来的运维熵增与合规风险,典型应用于金融核心系统改造、医疗SaaS迭代及工业IoT固件升级等强监管、高可用场景。最终

weixin_33682790的博客 983

Python字符串格式化全解析:从%到f-string的四种方法对比与实践

字符串格式化是编程中的基础操作,其核心原理是将变量值动态嵌入到静态文本模板中。在Python中,这一技术经历了从C语言风格的%格式化到现代f-string的演进,每种方法在性能、安全性和可读性上各有侧重。对于开发者而言,掌握不同格式化方法的技术价值在于能够根据场景选择最优方案:f-string凭借其内联表达式和卓越性能成为现代Python开发的首选,尤其适合日志生成和API构建等高频操作;而string.Template则因其严格限制表达式执行,在用户自定义模板和国际化等安全敏感场景中不可替代。本文通过对比

weixin_30555515的博客 438

第51讲:SQL优化之COUNT查询的优化

MyISAM引擎会把一个表的总行记录在磁盘中,当执行count(*)的时候会直接从磁盘中读取总数然后进行返回,效率很高,当时如果使用count统计某个字段不为NULL的数据总数时,MyISAM也会很慢。count是一个聚合函数,对于返回的结果集进行一行行的判断,如果count函数取到的一行数据不为NULL,那么累加值就加1,否则不计数,最后返回累积的值。按照执行效率,这四种方式的效率从低到高依次是:count(字段)

江晓龙的博客 2万+

【MySQL】MySQL count(*) count(1) 实现方式以及各种 count 对比

在这个文章之前,我一直用count(1) 查询所有数据,以前我们都是说 count(*) 是最慢的。但是这个博客恰恰相反。对于来说,InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。对于 count(1) 来说,InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。

九师兄 837

C++多进程编程实战:从fork到共享内存构建并行日志分析器

在计算机科学中,并发编程是提升程序性能、充分利用多核处理器资源的核心技术。其基本原理在于允许程序同时执行多个任务,从而将计算密集型或I/O密集型工作的效率提升数个量级。从技术价值看,并发模型能显著优化响应时间、吞吐量和资源利用率,是现代高性能服务器、科学计算和数据处理系统的基石。常见的实现方式包括多线程与多进程,前者共享内存、通信高效但稳定性相互影响;后者内存隔离、稳定性高,是构建高可靠系统的首选。在C++开发中,多进程编程通过POSIX接口(如fork、pipe)实现,尤其适用于日志分析、批量数据处理等需

weixin_30800807的博客 404

喷涂工艺SCADA上位机:Winform实现的生产级exe系统

SCADA系统是工业自动化中实现数据采集、监控报警、历史追溯与报表输出的核心技术体系;Winform作为成熟稳定的桌面GUI框架,在资源受限、网络不稳、兼容性要求高的车间现场仍具不可替代性。其原理在于依托.NET Framework原生运行时,通过GDI+轻量渲染、内存映射缓存、S7CommPlus协议直连PLC等机制,保障毫秒级响应与断网续传能力。技术价值体现在交付即用(单文件exe)、免环境依赖、低硬件门槛与高鲁棒性,广泛适用于汽车零部件、涂装产线、中小型离散制造等场景。本文详解一个连续稳定运行14个月

weixin_33849215的博客 386

数据科学学习是马拉松:四阶段配速模型与实战避坑指南

数据科学不是算法堆砌,而是融合统计思维、工程能力与业务理解的系统性实践。其本质是一场需科学配速的长期旅程——从数据感知、工具驯化,到问题建模与价值校准,每个阶段都存在典型认知断层与技术陷阱。尤其在特征工程中易陷入量纲主导偏差,在模型评估时盲目依赖AUC而忽视业务成本不对称,在部署环节低估Flask的并发瓶颈。真实项目更常因时间泄露、特征穿越或监控盲区导致‘高分模型’失效。本文基于87名转行学员与12个落地项目经验,提炼可复用的阶段判断标准、三层穿透式工具解剖法及生产级监控体系,助力学习者跨越从‘会跑’到‘跑

weixin_30698527的博客 325

Anaconda安装教程(超详细版)

目录 一、Anaconda简介 二、运行环境 三、安装Anaconda 四、手动配置环境变量(重点) 五、测试Anaconda环境是否配置成功 一、Anaconda简介 Anaconda,一个开源的Python发行版本,可用于管理Python及其相关包,包含了conda、Python等180多个科学包及其依赖项。 二、运行环境 Windows11 (Window10用户亦可参考,仅环境变量打开位置不同)。 三、安装Anaconda (1)百度“Anaconda”或者输入网址

m0_61607990的博客 22万+

一阶电路基础知识

一阶电路(First-order circuit)是指由一个电感(L)或一个电容(C)和一个电阻(R)组成的电路。根据电感或电容的位置,一阶电路可以分为RC电路、RL电路、LC电路、RLC电路。一阶电路常用于滤波、放大、时序控制等各种电子设备和系统中。通过调整电阻、电容或电感的数值,可以改变电路的频率响应、滞后/提前相位等特性,实现不同的功能和性能一阶、二阶电路区别一阶电路由一个电容或一个电感与一个电阻组成,而二阶电路则由两个电容或两个电感与一个电阻组成。

weixin_45365488的博客 1万+

EER建模实战:MySQL中子类型、弱实体与复合属性的语义落地

EER(增强型实体关系模型)是面向业务复杂性的数据建模语言,它超越传统ER图,通过子类型/超类型、弱实体、复合属性和多值属性等语义构造,精准表达‘同中有异、异中有同’的现实业务结构。其核心原理在于将业务约束前置为可验证的数据契约,而非依赖应用层补丁。技术价值体现在提升数据一致性、降低表结构变更频次、规避JOIN性能陷阱,并支撑高可靠扩展。典型应用场景包括多形态客户体系(如Free/Pro/Enterprise)、强依赖型业务实体(如订单明细)、地理与联系方式等查询敏感型复合结构。本文聚焦EER四大关键构造在

weixin_30376083的博客 431

Vue3 视频播放器完整指南 – @videojs-player/vue 从入门到精通

全面解析 Vue3 生态中最强大的视频播放解决方案 @videojs-player/vue。从基础安装到高级功能,涵盖自动播放、直播流、字幕支持等核心特性。提供完整的代码示例和最佳实践,助你快速构建专业的视频播放应用。

前端开发-前端技术分享 1427

Autonomy Loops:反思→评估→校正→执行的自主性闭环操作系统

Autonomy Loops是一种面向工程落地的自主性闭环方法论,其核心并非依赖AI或大模型,而是通过结构化、可验证的四阶段循环(Reflection→Evaluation→Correction→Execution)实现人机协同的持续优化。它源于对‘自动化幻觉’的反思,强调将人的判断力嵌入标准化动作:用可识别的触发条件启动反思,以基准线+容忍带+否决点构建评估坐标系,以单变量、可回滚、带标记的方式实施校正,并通过前置检查、人工确认、后置验证完成闭环认证。该范式已广泛应用于系统运维、CI/CD治理、产品AB迭

weixin_34331102的博客 339

第3章: MMU Interval Notifier 区间通知器的核心原理⭐

摘要:区间级内存通知机制分析 本文分析了Linux内核中区间级内存通知机制(mmu_interval_notifier)的设计原理与实现。该机制针对传统全局通知(mmu_notifier)存在的性能开销大、无法精确控制等问题,通过引入区间树(Interval Tree)实现精确匹配,仅当内存变化与注册区间重叠时才触发回调。核心优势包括:1)O(log n + k)的高效查找性能;2)支持独立管理各内存区间;3)实现高并发处理不同区间的失效事件。

图形/计算GPU的驱动与虚拟化技术分享园地 2027

Token 费用爆炸?教你用 Semantic Cache(语义缓存)把 API 成本降低 80%

本文介绍了语义缓存(Semantic Cache)技术在大模型应用中的关键作用。传统Redis缓存基于精确匹配,无法处理用户提问的语义相似性,导致重复调用昂贵的LLM API。语义缓存通过将问题向量化并在向量空间中进行相似度匹配,可识别30%-50%的重复提问,直接返回历史答案。文章详细解析了技术原理(Embedding+向量搜索)、架构设计,并提供了基于Python和ChromaDB的50行核心实现代码。同时指出了生产环境中的三大挑战:阈值设定、隐私保护和多轮对话处理,并给出相应解决方案。该技术能以极低成

BUG猿的博客 1056
上一篇: 满地坑,细数List的10个坑
下一篇: excel百万数据如何导入导出?
java小姜在线冲
博客等级 码龄4年 53粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值