PostgreSQL 外部排序性能

Ale*_*lex 2 postgresql sorting

我试图找出外部排序性能,它依赖于 WORK_MEM 值。我已经测试过增加 WORK_MEM 并不总是加速外部排序(下面的例子,在 PostgreSQL 9.6 上测试过)。是否有关于设置 WORK_MEM 的任何指导,说 WORK_MEM 的值是使用外部排序对 4GB 表进行排序的最佳值?是否可以从表大小和 WORK_MEM 设置中估计 IO 的数量?

我知道我可以研究 tuplesort.c 和底层算法,有现成的结果吗?

create table t1
as
select
  c as key
, cast('value' as char(5)) as val
from generate_series(1, 100000000) c;

--4223 MB
select pg_size_pretty(pg_total_relation_size('t1'));


\timing

--time - 146725.833 ms
SET WORK_MEM = '128MB';
create table t_128 as select * from t1 order by key;

--time - 148889.655 ms
SET WORK_MEM = '1024MB';
create table t_1024 as select * from t1 order by key;
Run Code Online (Sandbox Code Playgroud)

Kas*_*dry 7

是的。您可以在执行时看到外部排序的输出EXPLAIN ANALYZE(并根据EXPLAIN 文档添加 BUFFERS 和其他内容,因此您实际上不必估计是否可以运行这些。使用您的示例:

postgres@[local]:5432:postgres:=# 创建表 t1
postgres-# as
postgres-#选择
postgres-# c 作为键
postgres-# , cast('value' as char(5)) as val
postgres-# from generate_series(1, 100000000) c;
选择 100000000
时间:162609.861 毫秒
postgres@[local]:5432:postgres:=# SET work_mem="128MB";
放
时间:0.317 毫秒
postgres@[local]:5432:postgres:=# EXPLAIN (ANALYZE, BUFFERS) create table t_128 as select * from t1 order by key;
                                                         查询计划                                                         
-------------------------------------------------- -------------------------------------------------- ------------------------
 排序(成本=13476557.48..13672503.59行=78378445宽度=28)(实际时间=38358.696..66282.785行=100000000循环=1)
   排序键:key
   排序方式:外部合并磁盘:1954200kB
   缓冲区:共享命中=2083 读取=538461 脏=508619,临时读取=244397 写入=244397
   -> t1 上的 Seq 扫描(成本=0.00..1324325.45 行=78378445 宽度=28)(实际时间=0.108..17185.459 行=100000000 循环=1)
         缓冲区:共享命中=2080 读取=538461 脏=508619
 规划时间:74.660 毫秒
 执行时间:206961.514 ms
(8 行)

时间:207239.156 毫秒
postgres@[local]:5432:postgres:=# SET WORK_MEM = '1024MB';
放
时间:0.192 毫秒
postgres@[local]:5432:postgres:=# EXPLAIN (ANALYZE, BUFFERS) create table t_1024 as select * from t1 order by key;
                                                         查询计划                                                         
-------------------------------------------------- -------------------------------------------------- ------------------------
 排序(成本=16537253.59..16787253.81行=100000088宽度=10)(实际时间=30379.785..51937.967行=100000000循环=1)
   排序键:key
   排序方式:外部合并磁盘:1954944kB
   缓冲区:共享命中=540541,临时读取=244382 写入=244382
   -> t1 上的 Seq 扫描(成本=0.00..1540541.88 行=100000088 宽度=10)(实际时间=0.013..7570.784 行=100000000 循环=1)
         缓冲区:共享命中=540541
 规划时间:0.121 ms
 执行时间:172406.698 毫秒
(8 行)

时间:172527.526 毫秒
postgres@[本地]:5432:postgres:=# 

我们可以看到正在进行的排序是外部(磁盘上),它们占用大约 2GB。现在磁盘比内存慢很多,所以如果我们让它在内存中完成整个排序,可能会有更多的加速。

现在,如果我们走一条非常可笑的路线,我们可以更改排序特征,如下所示:

postgres@[local]:5432:postgres:=# SET work_mem='20GB';
放
时间:0.188 毫秒
postgres@[local]:5432:postgres:=# EXPLAIN (ANALYZE, BUFFERS) create table t_20g as select * from t1 order by key;
                                                         查询计划                                                         
-------------------------------------------------- -------------------------------------------------- ------------------------
 排序(成本=14828266.59..15078266.81行=100000088宽度=10)(实际时间=20532.705..31367.756行=100000000循环=1)
   排序键:key
   排序方式:快速排序内存:7833229kB
   缓冲区:共享命中=540541
   -> t1 上的 Seq 扫描(成本=0.00..1540541.88 行=100000088 宽度=10)(实际时间=0.013..7758.434 行=100000000 循环=1)
         缓冲区:共享命中=540541
 规划时间:0.044 ms
 执行时间:139461.981 毫秒
(8 行)

时间:139503.860 毫秒
postgres@[本地]:5432:postgres:=# 

这将 128MB 版本的排序内存的总更新减少了 67499.533 毫秒(或约 67.5 秒),差异约为 32% 左右。这从外部合并(在磁盘上排序的中间结果)更改为内存中的快速排序。

通过将BUFFERS参数添加到来显示为执行此查询而执行的 IO EXPLAIN,它为您提供命中(从共享块缓存读取)、读取(从磁盘读取)、脏(更改)和写入(写入磁盘)。

最佳选择work_mem取决于很多因素。最简单的方法是log_temp_files将其用作调整work_mem每个查询的起点,并将其设置为最大临时文件大小的 2-4 倍。尽管该设置适用于每个查询(也适用于排序和哈希表),因此除非必须,否则不要将其设置得太大。在像这样的非常大的东西上设置每个查询可能更合适。

tuplesort.c确实详细介绍了它的排序方式,如果您想比 中显示的更深入EXPLAIN ANALYZE,那么您可以使用 dtrace/systemtap/eBPF 或其他动态跟踪工具查看内容,以更深入地向您展示 PostgreSQL 如何正在排序。或者您可以根据您感兴趣的内容在自定义编译版本中添加一堆 ereport 行。

如果您对其他测试及其结果感兴趣,您还可以work_mem在这篇文章理解 postgresql.conf : work_mem 中获得有关效果的更多详细信息。

  • 请记住,太高的 work_mem 如果它运行您的机器内存不足可能是灾难性的。它是每一种。不是每个连接、每个数据库或每个用户。我的经验是,一旦种类比 1G 大几倍,再大一点就没有任何好处了。即,将 130GB 溢出到磁盘的报告服务器在使用超过 1G 的 work_mem 时不会明显更快。 (2认同)