PostgreSQL中 in 和 =any 查询比较

871 阅读4分钟

引言:

在SQL中习惯了用in做查询,对PostgreSQL中的=any的使用和了解都比较少,见同事有使用过,好奇就去查了下官方文档,文档中有这样的描述IN等价于= ANY。那么问题来了,这两种方式的执行过程和效率是一样的吗?

一、PostgreSQL查询基础

想要去探究IN= ANY的区别,首先我们要了解PostgreSQL查询的一些基本概念。

子查询

SQL中子查询分为两类,一类相关子查询,另一类是非相关子查询

相关子查询: 指在子查询语句中引用了外表的列属性,这就导致外层表每获得一个元组,子查询就要重新执行一次。

非相关子查询: 指在子查询语句是独立的,和外层的表没有直接关联,子查询可以单独执行一次,外层表可以重复利用子查询的执行结果。

通常而言,相关子查询是值得提升的,因为执行结果和父查询相关。

PostgreSQL分析相关的

PostgreSQL处理不相关子查询时,会为该子查询创建InitPlan;当处理相关子查询相关时,会为该子查询创建SubPlan

PostgreSQL查询计划中子查询被提升提升,可以在执行计划中看出EquiJoin , SemiJoin , AntiJoin等逻辑运算符,以及物理运算符HashJoinPostgreSQL和关系代数

PostgreSQL执行计划分析文档EXPLAIN,相关配置参数ANALYZE,VERBOSE,COSTS,BUFFERS,TIMING等可以自行查阅。

PostgreSQL查看版本信SELECT version()

PostgreSQL 13.3 on x86_64-apple-darwin20.4.0, compiled by Apple clang version 12.0.5 (clang-1205.0.22.9), 64-bit

二、分析PostgreSQL中in=any的查询计划

准备数据

创建测试表,模拟1万 IN 100万的操作。generate_series 函数补充

CREATE TABLE table_one(id int);

INSERT INTO table_one SELECT generate_series(1,100*10000)

执行SQL查询

先看下两种语句的执行计划

1.in的执行计划

explain select n in (select id from table_one) from generate_series(1,10000) as n

截屏2022-06-16 下午4.18.41.png

  1. =any的执行计划
explain select n = any(ARRAY(select id from table_one)) from generate_series(1,10000) as n

截屏2022-06-16 下午4.20.09.png

由此可见in用的是SubPlan=any用的是InitPlan

PostgreSQLSubPlan相关源码

截屏2022-06-16 下午4.31.37.png

截屏2022-06-16 下午4.14.24.png

work_mem的大小决定了subquery是否要装载到hash table

使用in查询。设置work_mem 为64kb

set work_mem ='64kB';

explain (analyze,verbose,timing,costs,buffers) select n in (select id from table_one) from generate_series(1,10000) as n

执行计划如下图

截屏2022-06-16 下午4.06.59.png

使用in查询。设置work_mem 为64MB

set work_mem ='64MB';

explain (analyze,verbose,timing,costs,buffers) select n in (select id from table_one) from generate_series(1,10000) as n

执行计划如下图

截屏2022-06-16 下午4.09.47.png

由此可见in查询受work_mem影响。

使用=any查询。设置work_mem 为64kb

set work_mem ='64kB';

explain (analyze,verbose,timing,costs,buffers) select n = any(ARRAY(select id from table_one)) from generate_series(1,10000) as n

执行计划如下图

截屏2022-06-23 下午2.09.28.png

使用=any查询。设置work_mem 为64MB

执行计划如下图

截屏2022-06-23 下午2.10.30.png

=any和in总结

1、=any查询不会使用hash_table,所有不会受work_mem设置大小的影响,且性能比较好。

2、in()查询 subquery_sizework_mem的大小会影响查询性能,当 subquery_size可以被work_mem装下时,查询优化器会使用hash_table

三、work_mem 拓展

本来到这里应该就结束了,但是官方文档对work_mem的描述中提到了散列值,也就是说work_mem的大小也会影响到hash_table,代码走起

截屏2022-06-23 下午3.08.55.png

work_mem 64kB hash_table

截屏2022-06-23 下午3.13.04.png

1 Buckets: 2048 Batches: 1024 Memory Usage: 50kB

2 Execution Time: 1605.638 ms

work_mem 64MB hash_table

截屏2022-06-23 下午3.15.27.png

1 Buckets: 1048576 Batches: 1 Memory Usage: 43349kB

2 Execution Time: 315.821 ms

work_mem 128MB hash_table

截屏2022-06-23 下午4.16.39.png

1 Buckets: 1048576 Batches: 1 Memory Usage: 43349kB

2 Execution Time: 313.068 ms

总结

1(Hash Semi Join),是物理优化提升

2 work_mem大的时候,查询用时短,当大到一次可以装下hash_table时,就不会再提升了

3 哈希结点显示了哈希桶(Buckets)的数量和批(Batches)数,以及被哈希表所使用的内存量(Memory Usage)的峰值(如果批数超过一,也将会涉及到磁盘空间使用,产生IO时间开销,但是并没有被显示) postgres.cn/docs/10/usi…

4 work_mem 默认是4M,最小是64KB,最大2147483647kB

PostgreSQL配置查询

PostgreSQL分析查询

参考文献:developer.aliyun.com/article/424…