Mysql性能优化:为什么你的count(*)这么慢?
导读
- 在开发中一定会用到统计一张表的行数,比如一个交易系统,老板会让你每天生成一个报表,这些统计信息少不了 sql 中的count函数。
- 但是随着记录越来越多,查询的速度会越来越慢,为什么会这样呢?Mysql内部到底是怎么处理的?
- 今天这篇文章将从Mysql内部对于
count
函数是怎样处理的? - 本文首发于作者微信公众号【码猿技术专栏】Mysql性能优化:为什么你的count(*)这么慢?,原创不易,喜欢的请支持一下,谢谢!!!
count的实现方式
- 在Mysql中的不同的存储引擎对
count
函数有不同的实现方式。 MyISAM
引擎把一个表的总行数存在了磁盘上,因此执行count(*)
的时候会直接返回这个数,效率很高(没有where
查询条件)。InnoDB
引擎并没有直接将总数存在磁盘上,在执行count(*)
函数的时候需要一行一行的将数据读出来,然后累计总数。
为什么InnoDB不将总数存起来?
说到InnoDB相信读者总会想到其支持事务的特性,事务具有隔离性,如果将总数存起来,怎么保证各个事务之间的总数的一致性呢?不明白的看图
事务A
和事务B
中的count(*)
的执行结果是不同的,因此InnoDB引擎在每个事务中返回多少行是不确定的,只能一行一行的读出来用来判断总数。
如何提升count效率
- 在
InnoDB
对于如何提升count(*)
的查询效率,网上有多种解决办法,这里主要介绍三种,并分析可行性。
show table status
show table status
这个命令能够很快的查询出数据库中每个表的行数,但是真的能够替代count(*)
吗?- 答案是不能。原因很简单,这个命令统计出来的值是一个「估值」,因此是不准确的,官方文档说误差大概在
40%-50%
。 - 因此这种方法直接pass,不准确还用它干嘛。
缓存系统存储总数
这种方法也是最容易想到的,增加一行就
+1
,删除一行就-1
,并且缓存系统读取也是很快,既简单又方便的为什么不用?缓存系统和Mysql是两个系统,比如
redis
和Mysql
这两个是典型的比较。两个系统最难的就是在高并发下无法保证数据的一致性。通过上面两张图,无论是
redis计数+1
还是insert into user
先执行,最终都会导致数据在逻辑上的不一致。第一张图会出现redis计数
少了,第二张图虽然计数正确了但是并没有查询出插入的那一行数据。在并发系统里面,我们是无法精确控制不同线程的执行时刻的,因为存在图中的这种操作序列,所以,我们说即使Redis正常工作,这个计数值还是逻辑上不精确的。
在数据库保存计数
通过缓存系统保存的分析得知了使用缓存无法保证数据在逻辑上的一致性,因此我们想到了直接使用数据库来保存,有了「事务」的支持,也就保证了数据的一致性了。
如何使用呢?很简单,直接将计数保存在一张表中
(table_name,total)
。至于执行的逻辑只需要将缓存系统中
redis计数+1
改成total
字段+1即可,如下图:由于在同一个事务中,保证了数据在逻辑上的一致性。
不同count的用法
count()
是一个聚合函数,对于返回的结果集,一行行地判断,如果count函数的参数不是NULL,累计值就加1,否则不加。最后返回累计值。count
的用法有多种,分别是count(*)
、count(字段)
、count(1)
、count(主键id)
。那么多种用法,到底有什么差别呢?当然,「前提是没有where
条件语句」。count(id)
:InnoDB引擎会遍历整张表,把每一行的id值都取出来,返回给server层。server层拿到id后,判断是不可能为空的,就按行累加。count(1)
:InnoDB引擎遍历整张表,但不取值。server层对于返回的每一行,放一个数字1
进去,判断是不可能为空的,按行累加。count(字段)
:count(*)
:不会把全部字段取出来,而是专门做了优化,不取值。count(*)
肯定不是null,按行累加。- 如果这个“字段”是定义为
not null
的话,一行行地从记录里面读出这个字段,判断不能为null,按行累加; - 如果这个字段定义允许为
null
,那么执行的时候,判断到有可能是null,还要把值取出来再判断一下,不是null才累加。
- 如果这个“字段”是定义为
- 所以结论很简单:「按照效率排序的话,
count(字段)
<count(主键id)
<count(1)
≈count(*)
,所以建议读者,尽量使用count(*)
。」 - 「注意」:这里肯定有人会问,
count(id)
不是走的索引吗,为什么查询效率和其他的差不多呢?陈某在这里解释一下,虽然走的索引,但是还是要一行一行的扫描才能统计出来总数。
总结
MyISAM
表虽然count(*)
很快,但是不支持事务;show table status
命令虽然返回很快,但是不准确;InnoDB
直接count(*)
会遍历全表(没有where条件),虽然结果准确,但会导致性能问题。- 缓存系统的存储计数虽然简单效率高,但是无法保证数据的一致性。
- 数据库保存计数很简单,也能保证数据的一致性,建议使用。
- 「思考题,读者留言区讨论」:在系统高并发的情况下,使用数据库保存计数,是先
更新计数+1
,还是先插入数据
。即是先update total+=1
还是先insert into
。
Mysql性能优化:为什么你的count(*)这么慢?的更多相关文章
- MYSQL性能优化的最佳20+条经验
MYSQL性能优化的最佳20+条经验 2009年11月27日 陈皓 评论 148 条评论 131,702 人阅读 今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数 ...
- mysql性能优化-简易版
mysql性能优化 sql语句优化 如何发现有问题的sql? 开启mysql慢查询 show variables like 'slow_query_log' set global slow_query ...
- 二十种实战调优MySQL性能优化的经验
二十种实战调优MySQL性能优化的经验 发布时间:2012 年 2 月 15 日 发布者: OurMySQL 来源:web大本营 才被阅读:3,354 次 消灭0评论 本文将为大家介 ...
- MySQL性能优化的21个最佳实践
http://www.searchdatabase.com.cn/showcontent_38045.htm MySQL性能优化的21个最佳实践 1. 为查询缓存优化你的查询 大多数的MySQL服务器 ...
- MYSQL之性能优化 ----MySQL性能优化必备25条
今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我 们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数 ...
- 使用ThinkPHP开发中MySQL性能优化的最佳21条经验
使用ThinkPHP开发中MySQL性能优化的最佳21条经验讲解,目前,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更 ...
- 涨姿势:Mysql 性能优化完全手册
涨姿势:Mysql 性能优化完全手册 深入理解MySQL服务器架构 客户端层 MySQL逻辑架构整体分为三层,最上层为客户端层,诸如:连接处理.授权认证.安全等功能均在这一层处理. 中间层 MySQL ...
- redmine在linux上的mysql性能优化方法与问题排查方案
iredmine的linux服务器mysql性能优化方法与问题排查方案 问题定位: 客户端工具: 1. 浏览器inspect-tool的network timing工具分析 2. 浏览 ...
- Mysql 性能优化教程
Mysql 性能优化教程 目录 目录 1 背景及目标 2 Mysql 执行优化 2 认识数据索引 2 为什么使用数据索引能提高效率 2 如何理解数据索引的结构 2 优化实战范例 3 认识影响结果集 4 ...
随机推荐
- [Abp vNext 源码分析] - 19. 多租户
一.简介 ABP vNext 原生支持多租户体系,可以让开发人员快速地基于框架开发 SaaS 系统.ABP vNext 实现多租户的思路也非常简单,通过一个 TenantId 来分割各个租户的数据,并 ...
- 逆向破解之160个CrackMe —— 001(上)
CrackMe--001 前置知识介绍: 160 CrackMe 是比较适合新手学习逆向破解的CrackMe的一个集合,一共160个待逆向破解的程序 CrackMe:一些公开给别人尝试破解的小程序,制 ...
- JS基础入门篇(三十五)—面向对象(二)
如果没有面向对象这种抽象概念的小伙伴,建议先看一下我写的JS基础入门篇(三十四)-面向对象(一)
- Windows环境下docker的安装与配置
Docker是一种容器技术,可以在操作系统中隔离出若干个独立的程序运行环境,这些环境既可以共享宿主机的资源,另一方面他们之间相互独立,互不影响,也不会对宿主机的环境产生影响.与虚拟化技术不同的是,Do ...
- 研究开源源码之Myrmec
好久没写博客了,自己也弄不清是懒了还是忙了.毕竟白天需要工作,晚上有时候看看资料,有时候陪家人,有时候约朋友......更加累了,可能由于累了就懒得总结了. 今天有同事问我关于代码检查文件类型的问题. ...
- 一起了解 .Net Foundation 项目 No.18
.Net 基金会中包含有很多优秀的项目,今天就和笔者一起了解一下其中的一些优秀作品吧. 中文介绍 中文介绍内容翻译自英文介绍,主要采用意译.如与原文存在出入,请以原文为准. Protobuild Pr ...
- golang 学习之路 string转换为其他类型 其他类型转换为string
将其他值转换为string 一般常用fmt.Sprintf(格式,转换的值) // 使用fmt.Sprintf 转换所有的类型为string 使用 这是第一种 // 注意在sprintf使用中需要注意 ...
- Hyperledger Fabric ChainCode开发
预览 Hyperledger Fabric的chaincode开发目前支持Go.Java.Node.js语言,下面以Go语言作为例子,我们先看下面的一个官方提供chaincode模板 ··· pack ...
- Git&sourceTree软件安装、使用说明及遇到问题解决
一.软件版本 1.Git版本为1.9.5 2.Source版本为1.5.2 二.软件安装步骤 1.Git安装步骤 1)双击Git安装文件进入下图界面,单击Next 2)继续Next 3)进入Selec ...
- [vue ]滚动视图解决ElementUI NavMenu 导航菜单过长显示的问题
记录一下工作 需求 导航菜单过长的时候会溢出,需要一个像 Tabs 标签页一样的滚动视图容器,可以左右滚动内部视图. 解决方法 由于时间问题,所以直接将 Tabs 源码抽取出来使用. 这里要做一些特殊 ...