【PostgreSQL 】PostgreSQL 15对distinct的优化
示例表
table t_ex;
c1 | c2
----+----
2 | B
4 | C
6 | A
2 | C
4 | B
6 | B
2 | A
4 | B
6 | C
2 | C
以下SQL语句有序地返回"c1"列中唯一值:
select distinct on(c1) * from abce;
对于c2列,会根据c1的唯一性,从表中找到的第一个值。
postgres=# select distinct on(c1) * from abce;
c1 | c2
----+----
2 | B
4 | B
6 | B
(3 rows)
以下SQL语句有序地返回"c2"列中唯一值:
# select distinct on(c2) * from abce;
c1 | c2
----+----
6 | A
2 | B
4 | C
(3 rows)
最后从表中返回唯一性的记录
postgres=# select distinct * from abce;
c1 | c2
----+----
6 | C
4 | C
4 | B
2 | C
2 | A
6 | B
6 | A
2 | B
(8 rows)
那么你可能会问,在postgresql15中,distinct的增强体现在哪些方面呢?答案是:并发
在此之前,只有一个cpu或进程来计算不同的值。在postgresql15中,可以使用并发,使用多个cpu进程。
这一特性涉及好几个参数,但是,我们只聚焦在参数max_parallel_workers_per_gather。
为了演示这个改进,我们创建三个表,没有索引,填充大约5000000条记录。注意,表的列数分别为1,5,10。
Table "public.t1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
c1 | integer | | | Table "public.t5"
Column | Type | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
c1 | integer | | |
c2 | integer | | |
c3 | integer | | |
c4 | integer | | |
c5 | character varying(40) | | | Table "public.t10"
Column | Type | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
c1 | integer | | |
c2 | integer | | |
c3 | integer | | |
c4 | integer | | |
c5 | character varying(40) | | |
c6 | integer | | |
c7 | integer | | |
c8 | integer | | |
c9 | integer | | |
c10 | integer | | |
insert into t1 select generate_series(1,500); insert into t5
select generate_series(1,500)
,generate_series(500,1000)
,generate_series(1000,1500)
,(random()*100)::int
,'aofjaofjwaoeev$#^ÐE#@#Fasrhk!!@%Q@'; insert into t10
select generate_series(1,500)
,generate_series(500,1000)
,generate_series(1000,1500)
,(random()*100)::int
,'aofjaofjwaoeev$#^ÐE#@#Fasrhk!!@%Q@'
,generate_series(1500,2000)
,generate_series(2500,3000)
,generate_series(3000,3500)
,generate_series(3500,4000)
,generate_series(4000,4500); List of relations
Schema | Name | Type | Owner | Persistence | Access method | Size |
--------+------+-------+----------+-------------+---------------+--------+
public | t1 | table | postgres | permanent | heap | 173 MB |
public | t10 | table | postgres | permanent | heap | 522 MB |
public | t5 | table | postgres | permanent | heap | 404 MB |
下一步是将生成的数据dump到以下的版本中:
PG VERSION
pg96
pg10
pg11
pg12
pg13
pg14
pg15
数据导入后,使用下面的脚本生成结果:
#!/bin/bash
for v in 96 10 11 12 13 14 15
do
# run the explain analzye 5X in order to derive consistent numbers
for u in $(seq 1 5)
do
echo "--- explain analyze: pg${v}, ${u}X ---"
psql -p 100$v db01 -c "explain analyze select distinct on (c1) * from t1" > t1.pg$v.explain.txt
psql -p 100$v db01 -c "explain analyze select distinct * from t5" > t5.pg$v.explain.txt
psql -p 100$v db01 -c "explain analyze select distinct * from t10" > t10.pg$v.explain.txt
done
done
以下是结果比较,可以看到表越大,性能收获越大。
|
PG VERSION |
1 column (t1), ms |
5 column (t5), ms |
10 column (t10), ms |
|
pg96 |
3,382 |
9,743 |
20,026 |
|
pg10 |
2,004 |
5,746 |
13,241 |
|
pg11 |
1,932 |
6,062 |
14,295 |
|
pg12 |
1,876 |
5,832 |
13,214 |
|
pg13 |
1,973 |
2,358 |
3,135 |
|
pg14 |
1,948 |
2,316 |
2,909 |
|
pg15 |
1,439 |
1,025 |
1,245 |

来看看不同版本之间的执行计划:
PG96 QUERY PLAN, TABLE T1
-------------------------------------------------------------------------------
Unique (cost=765185.42..790185.42 rows=500 width=4) (actual time=2456.805..3381.230 rows=500 loops=1)
-> Sort (cost=765185.42..777685.42 rows=5000000 width=4) (actual time=2456.804..3163.600 rows=5000000 loops=1)
Sort Key: c1
Sort Method: external merge Disk: 68432kB
-> Seq Scan on t1 (cost=0.00..72124.00 rows=5000000 width=4) (actual time=0.055..291.523 rows=5000000 loops=1)
Planning time: 0.161 ms
Execution time: 3381.662 ms
PG15 QUERY PLAN, TABLE T1
---------------------------------------------------------------------------
Unique (cost=557992.61..582992.61 rows=500 width=4) (actual time=946.556..1411.421 rows=500 loops=1)
-> Sort (cost=557992.61..570492.61 rows=5000000 width=4) (actual time=946.554..1223.289 rows=5000000 loops=1)
Sort Key: c1
Sort Method: external merge Disk: 58720kB
-> Seq Scan on t1 (cost=0.00..72124.00 rows=5000000 width=4) (actual time=0.038..259.329 rows=5000000 loops=1)
Planning Time: 0.229 ms
JIT:
Functions: 1
Options: Inlining true, Optimization true, Expressions true, Deforming true
Timing: Generation 0.150 ms, Inlining 31.332 ms, Optimization 6.746 ms, Emission 6.847 ms, Total 45.074 ms
Execution Time: 1438.683 ms
当DISTINCT列的数量增加时,真正的差异出现了,如查询表 t10 所示。 可以看到并行化在起作用!
PG96 QUERY PLAN, TABLE T10
-------------------------------------------------------------------------------------------
Unique (cost=1119650.30..1257425.30 rows=501000 width=73) (actual time=14257.801..20024.271 rows=50601 loops=1)
-> Sort (cost=1119650.30..1132175.30 rows=5010000 width=73) (actual time=14257.800..19118.145 rows=5010000 loops=1)
Sort Key: c1, c2, c3, c4, c5, c6, c7, c8, c9, c10
Sort Method: external merge Disk: 421232kB
-> Seq Scan on t10 (cost=0.00..116900.00 rows=5010000 width=73) (actual time=0.073..419.701 rows=5010000 loops=1)
Planning time: 0.352 ms
Execution time: 20025.956 ms
PG15 QUERY PLAN, TABLE T10
------------------------------------------------------------------------------------------- HashAggregate (cost=699692.77..730144.18 rows=501000 width=73) (actual time=1212.779..1232.667 rows=50601 loops=1)
Group Key: c1, c2, c3, c4, c5, c6, c7, c8, c9, c10
Planned Partitions: 16 Batches: 17 Memory Usage: 8373kB Disk Usage: 2976kB
-> Gather (cost=394624.22..552837.15 rows=1002000 width=73) (actual time=1071.280..1141.814 rows=151803 loops=1)
Workers Planned: 2
Workers Launched: 2
-> HashAggregate (cost=393624.22..451637.15 rows=501000 width=73) (actual time=1064.261..1122.628 rows=50601 loops=3)
Group Key: c1, c2, c3, c4, c5, c6, c7, c8, c9, c10
Planned Partitions: 16 Batches: 17 Memory Usage: 8373kB Disk Usage: 15176kB
Worker 0: Batches: 17 Memory Usage: 8373kB Disk Usage: 18464kB
Worker 1: Batches: 17 Memory Usage: 8373kB Disk Usage: 19464kB
-> Parallel Seq Scan on t10 (cost=0.00..87675.00 rows=2087500 width=73) (actual time=0.072..159.083 rows=1670000 loops=3)
Planning Time: 0.286 ms
JIT:
Functions: 31
Options: Inlining true, Optimization true, Expressions true, Deforming true
Timing: Generation 3.510 ms, Inlining 123.698 ms, Optimization 200.805 ms, Emission 149.608 ms, Total 477.621 ms
Execution Time: 1244.556 ms
提高性能:
postgres运行时参数max_parallel_workers_per_gather来提高性能。新初始化的集群中的默认值为2。
如下表所示,由于测试硬件本身的能力有限,它很快成为收益递减的原因。
在postgresql 15中:
|
max_parallel_workers_per_gather |
1 column (t1) |
5 column (t5) |
10 column (t10) |
|
2 |
1,439 |
1,025 |
1,245 |
|
3 |
1,464 |
875 |
1,013 |
|
4 |
1,391 |
858 |
977 |
|
6 |
1,401 |
846 |
1,045 |
|
8 |
1,428 |
856 |
993 |

关于索引:如本查询计划中所示,应用索引时未实现性能改进。
PG15,表T10,max_parallel_workers_per_gather=4:
QUERY PLAN
-----------------------------------------------------------------------------------
Unique (cost=0.43..251344.40 rows=501000 width=73) (actual time=0.060..1240.729 rows=50601 loops=1)
-> Index Only Scan using t10_c1_c2_c3_c4_c5_c6_c7_c8_c9_c10_idx on t10 (cost=0.43..126094.40 rows=5010000 width=73) (actual time=0.058..710.780 rows=5010000 loops=1)
Heap Fetches: 582675
Planning Time: 0.596 ms
JIT:
Functions: 1
Options: Inlining false, Optimization false, Expressions true, Deforming true
Timing: Generation 0.262 ms, Inlining 0.000 ms, Optimization 0.122 ms, Emission 2.295 ms, Total 2.679 ms
Execution Time: <strong>1249.391 ms</strong>
跨多个CPU运行DISTINCT是性能能力的一大进步。
但是请记住,当增加max_parallel_workers_per_gather的数量并接近硬件的限制时,性能下降的风险。
在正常情况下,查询计划器可能会决定使用索引而不是运行并行工作程序。
解决此问题的一种方法是考虑禁用运行时参数,例如enable_indexonlyscan和enable_indexscan。
最后,不要忘记运行EXPLAIN ANALYZE以了解发生了什么。
【PostgreSQL 】PostgreSQL 15对distinct的优化的更多相关文章
- 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化
第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...
- How to get the free disk space in PostgreSQL (PostgreSQL获取磁盘空间)
Get the current free disk space in PostgreSQL PostgreSQL获取磁盘空间 from eshizhan Here has a simple way t ...
- 【 PostgreSQL】十条实用数据库SQL优化建议
基于PostgreSQL,总结几条常用的查询操作的优化建议,部分也适用于Oracle等数据库. 1.选择合适的分布键 分布键选择不当会导致重分布.数据分布不均等,而数据分布不均会使SQL集中在一个se ...
- PostgreSQL 涉及复杂视图查询的优化案例
一.前言 对于含有union , group by 等的视图,我们称之为复杂视图. 这类的视图会影响优化器对于视图的提升,也就是视图无法与父查询进行合并,从而影响访问路径.连接方法.连接顺序等.本文通 ...
- 【PostgreSQL】 前缀模糊查询级优化
前匹配模糊 使用B-Tree来加速优化前匹配模糊查询 构造数据 新建一张商品表,插入一千万条数据. create table goods(id int, name varchar); insert i ...
- Google 发布的15个 Android 性能优化典范
2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关 ...
- [PostgreSQL]PostgreSQL数据类型格式化函数——字符串和数值间的转换
详情见官网:http://www.postgres.cn/docs/10/functions-formatting.html PostgreSQL中有以下格式化函数: 函数 返回类型 描述 例子 to ...
- 15、Jdbc的优化(BeanUtils组件)
Jdbc的优化! BeanUtils组件 自定义一个持久层的框架 DbUtils组件 案例优化 1. BeanUtils组件 1.1 简介 程序中对javabean的操作很频繁, 所以apach ...
- Java虚拟机15:运行期优化
前言 HotSpot采用的是解释器+编译器并存的架构,之前的这篇文章里面已经讲过了,本文只是把即时编译器这块再讲得具体一点而已.当然,其实本文的内容也没多大意义,90%都是概念上的东西,对于实际开发. ...
随机推荐
- 2003031121-浦娟-python数据分析第三周作业-第一次作业
项目 内容 课程班级博客链接 https://edu.cnblogs.com/campus/pexy/20sj 作业链接 https://edu.cnblogs.com/campus/pexy/20s ...
- Linux vs Unix - Linux与Unix到底有什么不同?
来自:Linux迷链接:https://www.linuxmi.com/linux-vs-unix.html Linux和Unix这两个术语可以互换地用来指同一操作系统.这在很大程度上是由于他们惊人的 ...
- 详解 Java 17 中新推出的密封类
Java 17推出的新特性Sealed Classes经历了2个Preview版本(JDK 15中的JEP 360.JDK 16中的JEP 397),最终定稿于JDK 17中的JEP 409.Seal ...
- (Bezier)贝塞尔曲在路径规划的运用
前言 之前被安排了活,一个局部区域机器运动控制的工作,大致是一个机器位于一个极限区域时候,机器要进入一个特殊的机制,使得机器可以安全的走出来.其中用到了bezier曲线进行优化路径,今天写一下,正好也 ...
- 【Java8新特性】Optional 类
概述 Optional 类是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. Optional 是个容器:它可以保存类型T的值,或者 ...
- 【算法】计数排序(Counting Sort)(八)
计数排序(Counting Sort) 计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中. 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范 ...
- Vue问题
vue问题 #(1)vouter的addRoutes方法---用户权限 //自定义添加路由方法,防止重复添加路由 #使用后 路由结构 const user = () => import('../ ...
- C++primer第一章
第一章 : 开始 1.1 编写一个简单的C++程序 要点:每个函数有且只能拥有一个main函数,且main的默认返回是一个int类型. 函数定义:返回类型,函数名,形参列表,函数体 1.1.1编译 运 ...
- Unity实现”对象池管理器“
前言:警告!这可能是坨屎,空闲时间写成,仅作娱乐 在Unity中生成或销毁一个物体会占用较大的资源,如果是制作FPS射击游戏,子弹生成更是雪上加霜.所以我自己写了一个PoolManager,不能和网上 ...
- 关于『进击的Markdown』:第一弹
关于『进击的Markdown』:第一弹 建议缩放90%食用 比起隐蔽物下的狙击手,Markdown更像冲锋陷阵的阵头兵 简单.粗暴.直接.而且好上手 各位晚上好! 早饭吃了吗您 我 今 天 没 吃 M ...