数据库查询优化问题,有没有老哥懂的?

2023-02-01 17:05:41 +08:00
 cy1027

SELECT id FROM table_name;--一秒钟出结果,我的表大概三十万数据 select count(id) from table_name;--超过二十秒,没等它跑完 select count() from table_name;--同上 id 是主键,数据库里有很多列,有大量字符串,大概占几个 g 的磁盘。 我比较不理解,为什么只查 id 可以很快,但是对 id 计数就会很慢,如果说 count ()要扫描全表,那 count ( id )不应该只比第一个稍微慢一丢丢么?

2261 次点击
所在节点    程序员
41 条回复
netnr
2023-02-01 19:23:55 +08:00
还有一种可能,都慢,第一种方式走缓存了
akira
2023-02-01 21:09:01 +08:00
navicat 会自动加 limit ,所以你前面那个是秒出。
littlewing
2023-02-01 21:21:01 +08:00
SELECT id FROM table_name;--一秒钟出结果,我的表大概三十万数据
select count(id) from table_name;--超过二十秒

不可能,不科学,SELECT id FROM table_name 也是把 30 万条数据每一条都查出来,怎么可能差别那么大
cstj0505
2023-02-02 09:59:11 +08:00
@encro 支持事务 mvcc 数据库 count 基本无解的,因为需要扫描每一行数据的更新状态,对当前查询是否可见,如果被别的事务修改了还要去查找历史版本
cy1027
2023-02-02 11:07:45 +08:00
MySQL 8.0 ,innodb 的引擎,实在不好意思,本人确实对数据库不太了解,不知道检测需要从哪些指标切入,感谢指点
cy1027
2023-02-02 11:09:01 +08:00
@leahoop 你怎么不看一看我的评论呢,navicat 查出来的结果是 40 万条,我不希望因为这些没有意义的讨论浪费时间,还请认真一点
cy1027
2023-02-02 11:11:32 +08:00
@encro 我这种用法直接用命令行不也是一样的结果吗?跟 navicat 没关系吧
cy1027
2023-02-02 11:15:15 +08:00
@littlewing 这就是我的疑问呀,我不明白的就是这个地方
cy1027
2023-02-02 11:18:44 +08:00
@cstj0505 确实,因为查询的时候我的数据库还在更新,其实我只是需要一个大概的数值来检查更新进度就可以了,那这样我感觉直接查所有 id 然后在程序中对 id 计数会比较满足需求,直接用 count 可能就会因为更新问题导致查询变慢,应该这样理解吧
wangxin3
2023-02-02 13:42:51 +08:00
如果查询没有 where 条件的话,而且是大表,我建议自己在 db 中缓存 count 计数,insert 一条,count+1 ,insert 和 count+1 的 update 在同一事物中一并提交。
如果有 where 条件的话,上覆盖索引。
再者 count(*)是最快的
cy1027
2023-02-02 14:03:42 +08:00
@wangxin3 没有 where 条件。你说的这个记录是在另外一个表中记录吗?
cy1027
2023-02-02 14:04:58 +08:00
@wangxin3 还有,我这里实测就是 count (*)没有查所有 id 快啊,你直接再来给个结论是有什么依据吗?
wangxin3
2023-02-02 16:52:46 +08:00
@cy1027 #31 原文:“@wangxin3 没有 where 条件。你说的这个记录是在另外一个表中记录吗?”
======
回复:是的
wangxin3
2023-02-02 16:54:22 +08:00
@cy1027 #32 原文:“@wangxin3 还有,我这里实测就是 count (*)没有查所有 id 快啊,你直接再来给个结论是有什么依据吗?”
======
回复:count(主键 id),InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。
但是 count (*),InnoDB 引擎是作过特殊处理的,不取值,直接按行累加。相较于 count (主键 id )少了取每一行 id 的操作。
wangxin3
2023-02-02 16:58:23 +08:00
@cy1027 #32 原文:“@wangxin3 还有,我这里实测就是 count (*)没有查所有 id 快啊,你直接再来给个结论是有什么依据吗?”
======
回复:可能我理解错了 select count(*) from table; 和 select id form table;不存在比较的意义。不是一个层级的东西。
encro
2023-02-03 13:22:26 +08:00
@cy1027


就是 navicat 自动给你加了 limit...
然后他的统计是来自 infomation 表的大概统计,如果你仔细看前面有一个约等于符号,我没有记错的话。

你直接用命令行 select * 看看,一片跑一边看 show full processlist ,几十万数据 send data 状态都要几秒吧。

数据库的耗时有几个阶段:

查询解析:分析关系,走哪个索引?
取数据:内存(走了索引)或磁盘(没走索引)?
预处理结果:排序或格式化等,走了内存或者临时表?
发送数据:发数据到客户端,结果集大肯定慢了

结合慢查询,processlist 和 explain 可以发现并解决问题,如果是我前面的问题,基本无解。

如前面所说,我刚才试了下,在我的阿里云小 rds ( 1 核)上,900 多万数据无论是 select * 还是 id ,都需要一分多钟,两者走的是同样的索引,所以实际项目我都是加了条件限制的,默认只查当个月数据,用户加日期建立了组合索引,不让用户直接翻页到最后一页。
encro
2023-02-03 13:26:45 +08:00
@wangxin3

对不起,我用的 mysql8.17 没有这个功能。
explain
两个走的是一样的索引。
同时走了一个 tinyint 的索引。
你可以在线上试一下看。
encro
2023-02-03 13:28:35 +08:00
@wangxin3

这个特性好像是 20 年前的 myisam 引擎的,不是 innodb 引擎的。
encro
2023-02-03 13:36:18 +08:00
pg 100 万数据,select count(*)和 select count(id)也都是一样的索引,一样的 200ms ,刚试过的。

所以:

不要相信关系性数据库 count 能快,是要实际排序计算的。

不要相信数据库工程师很菜,不会自动分析走哪个索引,明明有更优化的索引不走,走个慢的。
cy1027
2023-02-03 13:52:33 +08:00
@wangxin3 感谢回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/912315

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX