sql count(1) count(*)区别_PostgreSQL的count(1)真的比count(*)快么?
作者简介
张连壮,多年PostgreSQL数据库内核研发经验,高可用/数据复制方面经验较为丰富,目前主要从事分布式数据库Citus相关工作,CitusDB中国【站主】专注于Citus技术分享的全信息平台,欢迎大家参与到Citus知识传播和Citus使用推广的活动中!!!
背景
count(* )在PostgreSQL上经常被抱怨执行非常慢。而在一段时间中我收到过很多count(1 )比count(* )快的说法,并将这种理解此应用于我的工作中。
为什么count(*)这么慢?
l 大多数人都会接受以下SQL很慢:
SELECT count(*)
FROM /* 复杂查询 */;
这是一个复杂的查询,PostgreSQL必须知道它需要将多少行的数据用于计算。
l 但如果以下情况很慢,很多人都会感到震惊:
SELECT count(*) FROM /* 大表 */;
由于表中没有存储“行数”(像在MySQL中的MyISAM一样),PostgreSQL计算行的唯一方法是获得全部数据。
因此,count(*)通常会执行表的顺序扫描,这个代价非常昂贵。
问题出在 count( * )中的 * 么?
select * from 中的 * 将扩展表的所有列,因此,许多人认为使用count( * )效率低下,应该写count(id)或count(1)代替。但是count( * )中的 * 与select * 中的 * 是完全不同的,count * 中的 * 仅仅代表row并不会展开它,写入count(1)与count( * )是相同的效果。但是count(id)有些不同,它只计算id是NOT NULL的行数。因此避免count( * )没有任何用处,反而count( * ) 的速度还会更快。
源码解读
l 查看count函数定义
test=# select proname, pronargs, prosrc from pg_proc where proname='count';
proname | pronargs | prosrc
---------+----------+-----------------
count | 0 | aggregate_dummy
count | 1 | aggregate_dummy
(2 rows)
l 什么? 0个参数的count?
test=# select count() from lzzhang;
ERROR: count(*) must be used to call a parameterless aggregate function
执行报错,说好的0个参数呢?
查看语法,仅保留主要代码。
* (*) - normal agg with no args
* (aggr_arg,...) - normal agg with args
* (ORDER BY aggr_arg,...) - ordered-set agg with no direct args
* (aggr_arg,... ORDER BY aggr_arg,...) - ordered-set agg with direct args
*
* The zero-argument case is spelled with '*' for consistency with COUNT(*).
aggr_args: '(' '*' ')'
{
$$ = list_make2(NIL, makeInteger(-1));
}
count( * )的 * 唯一的作用仅仅是作为count函数0个参数的标识使用!!!
l count查询
我们来看看几种count的情况
test=# select * from lzzhang;
id | id1 | id2
----+-----+-----
1 | |
1 | 1 |
1 | 1 | 1
2 | |
2 | |
3 | |
3 | |
3 | |
(8 rows)
test=# select count(*) from lzzhang;
count
-------
8
(1 row)
test=# select count(1) from lzzhang;
count
-------
8
(1 row)
test=# select count('const_string') from lzzhang;
count
-------
8
(1 row)
test=# select count(id) from lzzhang;
count
-------
8
(1 row)
test=# select count(id1) from lzzhang;
count
-------
2
(1 row)
这里我们简单分成三种类型的count
1. 列名(id/id1)-只计算非null的数据
2. 无参(* )–计算全部数据
3. 常量(1/const_string)–计算全部数据
count只计算非null的数据。
三种方式在ExecInterpExpr函数中的处理
列名:
EEO_CASE(EEOP_OUTER_FETCHSOME)
{
slot_getsomeattrs(outerslot, op->d.fetch.last_var);
EEO_NEXT();
}
只计算非null的数据
常量:
EEO_CASE(EEOP_CONST)
{
*op->resnull = op->d.constval.isnull;
*op->resvalue = op->d.constval.value;
EEO_NEXT();
}
常量当然不会是null,所以1/const_string会计算全部数据。
无参:
EEO_CASE(EEOP_AGG_STRICT_TRANS_CHECK)
{
AggState *aggstate;
AggStatePerGroup pergroup;
aggstate = op->d.agg_strict_trans_check.aggstate;
pergroup = &aggstate->all_pergroups
[op->d.agg_strict_trans_check.setoff]
[op->d.agg_strict_trans_check.transno];
if (unlikely(pergroup->transValueIsNull))
EEO_JUMP(op->d.agg_strict_trans_check.jumpnull);
EEO_NEXT();
}
检查之后就直接计算。
所以 * 并不会比1快, 反而 * 比1 会减少cpu的计算,速度更快!现在cpu的计算速度很快了,我的单核每秒可以计算6.5亿次,所以 * 和 1的时间几乎是一样的。
l 计算cpu单核运算力的小程序
[lzzhang@lzzhang-pc ~]$ cat test.c
#include
#include
#include
#include
unsigned long long counter=1;
void signal_handler(int sig)
{
printf("counter %llu \n", counter);
fflush(stdout);
exit(0);
}
int main(int argc, char *argv[])
{
signal(SIGALRM, signal_handler);
alarm(1);
while(counter++);
return 0;
}
[lzzhang@lzzhang-pc ~]$ gcc test.c
[lzzhang@lzzhang-pc ~]$ ./a.out
counter 657214986
提升PostgreSQL Count 性能
仅使用索引扫描
扫描一个小索引比全表扫描会减少很多代价,但是,由于PostgreSQL是多版本并发控制,在PostgreSQL中并不是这么简单的可以完成。每个行版本(“tuple”)包含可见的数据快照的信息。但是这些信息不会存储在索引中。因此,计算索引中的条目通常是不够的,因为PostgreSQL必须访问表元组以确保索引条目可见。
为了缓解这个问题,PostgreSQL引入了visibility map,这是一种数据结构,用于存储表块中的所有元组是否对所有人可见。
如果大多数表块都是可见的,则索引扫描不需要访问表元组来确定可见性。这种索引扫描称为“index only scan”,因此扫描索引以计算行数通常更快。
vacuum会更新visibility map,因此如果要使用索引来加速count * 的计算,请确保autovacuum在表上运行得足够频繁。
真的需要吗count(* )么?
有时最好的解决方案是寻找替代方案。
如果您不需要精确计数可以使用近似值。在这种情况下,您可以使用PostgreSQL用于查询计划的估计值:
SELECT reltuples::bigint
FROM pg_catalog.pg_class
WHERE relname = 'mytable';
这个值由autovacuum和autoanalyze更新,所以它永远不会超过10%的误差。您可以减少autovacuum_analyze_scale_factor以便autoanalyze更频繁地运行。
使用辅助表
我们创建mytable_count用于记录表的行数,并在mytable修改数据时使用触发器更新mytable_count的值。
START TRANSACTION;
CREATE TABLE mytable_count(c bigint);
CREATE FUNCTION mytable_count() RETURNS trigger
LANGUAGE plpgsql AS
$$BEGIN
IF TG_OP = 'INSERT' THEN
UPDATE mytable_count SET c = c + 1;
RETURN NEW;
ELSIF TG_OP = 'DELETE' THEN
UPDATE mytable_count SET c = c - 1;
RETURN OLD;
ELSE
UPDATE mytable_count SET c = 0;
RETURN NULL;
END IF;
END;$$;
CREATE TRIGGER mytable_count_mod AFTER INSERT OR DELETE ON mytable
FOR EACH ROW EXECUTE PROCEDURE mytable_count();
-- TRUNCATE triggers must be FOR EACH STATEMENT
CREATE TRIGGER mytable_count_trunc AFTER TRUNCATE ON mytable
FOR EACH STATEMENT EXECUTE PROCEDURE mytable_count();
-- initialize the counter table
INSERT INTO mytable_count
SELECT count(*) FROM mytable;
COMMIT;
我们在单个事务中执行所有操作,以便不会“丢失”任何数据,因为CREATE TRIGGER是SHARE ROW EXCLUSIVE的锁,这可以防止所有并发修改,当然,缺点是所有并发数据修改必须等到SELECT count(* )完成。
这为我们提供了一个非常快速替代count(* )的方案,但代价是减慢了对表的数据修改性能。即使这个计数表不停的更新,也没有“表膨胀”的危险,因为这些都是HOT更新。
PostgreSQL中文社区欢迎广大技术人员投稿
投稿邮箱:press@postgres.cn
sql count(1) count(*)区别_PostgreSQL的count(1)真的比count(*)快么?相关推荐
- sql中 count(*),count(1)以及count(字段)的区别
前言 记得很早以前就听说,在使用count的时候要用count(1)而不要用count(*),因为使用count(*)的时候会对所有的列进行扫描,相比而言count(1)不用扫描所有列,所以count ...
- 关于数据库优化1——关于count(1),count(*),和count(列名)的区别,和关于表中字段顺序的问题...
1.关于count(1),count(*),和count(列名)的区别 相信大家总是在工作中,或者是学习中对于count()的到底怎么用更快.一直有很大的疑问,有的人说count(*)更快,也有的人说 ...
- mysql下count(*)和count(1)的区别
2019独角兽企业重金招聘Python工程师标准>>> 今天看公司项目发现了一个奇怪sql写法 select count(8) from .... 这也许是开发人员不小心或者是习惯把 ...
- mysql select count() count(1)_select count()和select count(1)的区别和执行方式讲解
select count()和select count(1)的区别和执行方式讲解 发布时间:2020-09-06 13:26:14 来源:脚本之家 阅读:227 作者:CODETC 在SQL Serv ...
- mysql count里select_select count()和select count(1)的区别和执行方式讲解
在SQL Server中Count(*)或者Count(1)或者Count([列])或许是最常用的聚合函数.很多人其实对这三者之间是区分不清的.本文会阐述这三者的作用,关系以及背后的原理. 往常我经常 ...
- MySQL的count(*) ,count(1),count(id)的区别
数据库查询相信很多人都不陌生,所有经常有人调侃程序员就是CRUD专员,这所谓的CRUD指的就是数据库的增删改查. 在数据库的增删改查操作中,使用最频繁的就是查询操作.而在所有查询操作中,统计数量操作更 ...
- count(*) count(1)与count(字段)的区别
话不多说 MySql官方文档聚合函数地址 首先count函数的简单介绍: 上翻译: 1.COUNT(expr) ,返回SELECT语句检索的行中expr的值不为NULL的数量.结果是一个BIGINT值 ...
- count(*)和count(1)的区别
个人总结如下: 一.count(*)和count(1)查询速度 使用count函数,当要统计的数量比较大时,发现count(*)花费的时间比较多,相对来说count(1)花费的时间比较少. 1.如果你 ...
- Mysql中的count()与sum()区别
Mysql中的count()与sum()区别 首先创建个表说明问题 CREATE TABLE `result` ( `name` varchar(20) default NULL, `subject` ...
- MySQL COUNT函数优化及count(1)/count(*)/count(列名)的区别
count函数优化 使用近似值: 在某些应用场景中,不需要完全精确的值,可以参考使用近似值来代替,比如可以使用explain来获取近似的值.其实在很多OLAP的应用中,需要计算某一个列值的基数,有一个 ...
最新文章
- 融合机器人技术和神经科学的神经工程未来与挑战
- ActiveSync合作关系对话框的配置
- popover带箭头弹框
- 记录 之 遇到的 lamda 表达式和功能理解
- gitlab 构建tag_GitLab常用命令 分支 Tag 配置 操作
- 4. 2D绘制与控件绘制
- 获取程序下基目录下的文件的
- 配置管理小报110221:在linux上用真实帐号发mail的方法
- 杭电2531Catch him
- dpg learning 和q_深度学习和强化学习之间的差别有多大?
- 用户画像数据建模方法
- 文件同步工具 GoodSync Enterprise 破解
- 多x多y的origin图_3本纯爱文推荐:医疗师 x 小狼狗,漫漫何其多经典
- 计算机考试没来得及关掉文档,计算机二级考试挽回受损WORD文档的方法
- SurfacePro6解决亮度自动调节问题
- BZOJ5287 HNOI2018毒瘤
- 产业园区需要塑造的“六”大品牌
- TypesScript类型注解
- 记一次内网kafka映射到外网端口遇到小问题
- logistic模型预测人口python_基于logistic回归stats模型的概率预测置信区间
热门文章
- QT的QGLFunctions类的使用
- c++设计模式编程基础
- 怎么用matlab处理数据,如何用Matlab处理.wfm格式的数据
- Flink-Table StreamTableEnvironment基础知识
- 06_1.Pytorch中如何表示字符串、word embedding、One - hot、Embedding(Word2vec、BERT、Glove)【学习总结】
- 创建一个存储函数,返回指定员工的姓名,薪水和年收入
- 20-javamail
- 远程服务器概念,远程服务
- 装箱---一个工厂制造的产品形状都是长方体,它们的高度都是 h,长和宽都相等,一共有六个型号,他们的长宽分别为 1*1, 2*2, 3*3, 4*4, 5*5, 6*6.
- C++const类型的引用参数