示例表

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$#^&ETHE#@#Fasrhk!!@%Q@'; insert into t10
select generate_series(1,500)
,generate_series(500,1000)
,generate_series(1000,1500)
,(random()*100)::int
,'aofjaofjwaoeev$#^&ETHE#@#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的优化的更多相关文章

  1. 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化

    第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...

  2. 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 ...

  3. 【 PostgreSQL】十条实用数据库SQL优化建议

    基于PostgreSQL,总结几条常用的查询操作的优化建议,部分也适用于Oracle等数据库. 1.选择合适的分布键 分布键选择不当会导致重分布.数据分布不均等,而数据分布不均会使SQL集中在一个se ...

  4. PostgreSQL 涉及复杂视图查询的优化案例

    一.前言 对于含有union , group by 等的视图,我们称之为复杂视图. 这类的视图会影响优化器对于视图的提升,也就是视图无法与父查询进行合并,从而影响访问路径.连接方法.连接顺序等.本文通 ...

  5. 【PostgreSQL】 前缀模糊查询级优化

    前匹配模糊 使用B-Tree来加速优化前匹配模糊查询 构造数据 新建一张商品表,插入一千万条数据. create table goods(id int, name varchar); insert i ...

  6. Google 发布的15个 Android 性能优化典范

    2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关 ...

  7. [PostgreSQL]PostgreSQL数据类型格式化函数——字符串和数值间的转换

    详情见官网:http://www.postgres.cn/docs/10/functions-formatting.html PostgreSQL中有以下格式化函数: 函数 返回类型 描述 例子 to ...

  8. 15、Jdbc的优化(BeanUtils组件)

    Jdbc的优化! BeanUtils组件 自定义一个持久层的框架 DbUtils组件 案例优化 1. BeanUtils组件 1.1    简介 程序中对javabean的操作很频繁, 所以apach ...

  9. Java虚拟机15:运行期优化

    前言 HotSpot采用的是解释器+编译器并存的架构,之前的这篇文章里面已经讲过了,本文只是把即时编译器这块再讲得具体一点而已.当然,其实本文的内容也没多大意义,90%都是概念上的东西,对于实际开发. ...

随机推荐

  1. celery介绍、架构、快速使用、包结构,celery执行异步、延迟、定时任务,django中使用celery,定时更新首页轮播图效果实现,数据加入redis缓存的坑及解决

    今日内容概要 celery介绍,架构 celery 快速使用 celery包结构 celery执行异步任务 celery执行延迟任务 celery执行定时任务 django中使用celery 定时更新 ...

  2. umi框架应用服务端SSR,实现数据预渲染

    当我们的应用使用服务端渲染的方式时,可能需要把初始化加载的数据例如推荐等不需要用户输入的内容直接渲染获取,也有利于SEO. 上一篇已经实现服务端渲染,本次实现服务端获取数据后在做渲染. 利用getIn ...

  3. mybaitis查询 (数据库与实体类字段名不相同)

    1.这是我的数据库字段名和实体类字段名 2.方法 方法一: 查询的结果标题 会跟实体类的属性一一匹配,一定要一致就算数据库字段和属性不一致,我们可以把查询结果设置一个别名,让别名=属性名 方法二:使用 ...

  4. DOM操作标签、事件绑定、jQuery框架/类库

    DOM操作标签 ''' 在起变量名的时候 如果该变量指向的是一个标签 那么建议使用 xxxEle eg:aEle\pEle\divEle\spanEle ''' # 动态创建一个a标签并添加到页面指定 ...

  5. nodejs + typescript + koa + eslint + typescript eslint + prettier + webstorm

    ESLint 安装 yarn add -D eslint 生成配置文件 yarn eslint --init cli 选项 How would you like to use ESLint? To c ...

  6. 零基础学Java第四节(字符串相关类)

    本篇文章是<零基础学Java>专栏的第四篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! String 本文章首发于公众号[编程攻略] 在Java中,我们经 ...

  7. vagrant,VirtualBox -- 安装使用

    1.安装Oracle VM VirtualBox下载 http://download.virtualbox.org/virtualbox/5.2.44/ 2.安装vagrant https://www ...

  8. Nacos源码系列—订阅机制的前因后果(下)

    点赞再看,养成习惯,微信搜索[牧小农]关注我获取更多资讯,风里雨里,小农等你,很高兴能够成为你的朋友. 项目源码地址:公众号回复 nacos,即可免费获取源码 事件发布 在上一节中我们讲解了在Noti ...

  9. 859. Buddy Strings - LeetCode

    Question 859. Buddy Strings Solution 题目大意: 两个字符串,其中一个字符串任意两个字符互换后与另一个字符串相等,只能互换一次 思路: diff 记录不同字符数 两 ...

  10. 汇编语言中loop循环编程

    (1)向内存0:200~ 0:23f依次传送数据0~63(3FH) (2)同上简化后的代码,要求九行以内