web-dev-qa-db-ja.com

関数値に基づく再帰CTEは、Postgres 12の方が11に比べて著しく遅い

私のフォローアップ Postgres 12の一部のクエリが11のクエリよりも遅いという質問 問題を絞り込むことができたと思います。関数値に基づいた1つの再帰CTEが問題のスポットのようです。

Postgres 12.1では約150ミリ秒、Postgres 11.6では約4ミリ秒のように、Postgres 12.1ではPostgres 11.6よりもかなり長く実行されるかなり小さめのSQLクエリを分離できました。この現象をさまざまなシステムで再現できました。VirtualBoxの複数のVMで。 2つの異なる物理マシン上のDockerを介して。 (Dockerコマンドについては付録を参照してください)。しかし、奇妙なことに、それを https://www.db-fiddle.com/ で再現することはできません(違いはありません。どちらも高速です)。

さて、クエリについて。まず、この単純な関数を作成します

CREATE OR REPLACE FUNCTION public.my_test_function()
 RETURNS SETOF record
 LANGUAGE sql
 IMMUTABLE SECURITY DEFINER
AS $function$ 

SELECT 
        1::integer AS id,
        '2019-11-20'::date AS "startDate",
        '2020-01-01'::date AS "endDate"

$function$;

次に、実際のクエリ

WITH  "somePeriods" AS  (
      SELECT * FROM my_test_function() AS 
      f(id integer, "startDate" date, "endDate" date)
),

"maxRecursiveEndDate" AS (

SELECT "startDate", "endDate", id, 
( 
  WITH RECURSIVE prep("startDateParam", "endDateParam") AS (

  SELECT "startDate","endDate" FROM "somePeriods" WHERE id = od.id
  UNION
  SELECT "startDate","endDate" FROM "somePeriods", prep
  WHERE
    "startDate" <= ("endDateParam" + '1 day'::interval ) AND ("endDateParam" + '1 day'::interval ) <= "endDate"
  )
  SELECT max("endDateParam") FROM prep
) AS "endDateNew"

FROM "somePeriods" AS od

)

SELECT * FROM "maxRecursiveEndDate";

これが実際に何をしているのかは、ここではそれほど重要ではないと思います。重要な点は、RECURSIVEを含む複数のCTEが関与していることです。

私が試したこと:

  • 私はmy_test_functionなしで試しました。つまり、最初のCTEに直接値を入れました。このようにして、まったく問題はありませんでした。 12と11で同じくらい高速に実行されます。
  • Postgres 12では、MATERIALIZEDをいじってみましたが、効果がありませんでした。クエリの実行速度は以前と同じです。

これが実際にPostgres 12のバグ(またはパフォーマンスの低下)であるのか、ここで何か不足しているのかはわかりません。

付録:再現に使用したDockerコマンド

まず、両方のバージョンのイメージをプルします

docker pull postgres:12.1
docker pull postgres:11.6

次に、Postgres 12を実行します。

docker run -d --name my_postgres_12_container postgres:12.1

次に、クエリを実行します

docker exec my_postgres_12_container psql -U postgres -c "

CREATE OR REPLACE FUNCTION public.my_test_function()
 RETURNS SETOF record
 LANGUAGE sql
 IMMUTABLE SECURITY DEFINER
AS \$function\$ 

SELECT 
        1::integer AS id,
        '2019-11-20'::date AS \"startDate\",
        '2020-01-01'::date AS \"endDate\"

\$function\$;

EXPLAIN ANALYZE WITH  \"somePeriods\" AS  (
      SELECT * FROM my_test_function() AS 
      f(id integer, \"startDate\" date, \"endDate\" date)
),

\"maxRecursiveEndDate\" AS (

SELECT \"startDate\", \"endDate\", id, 
( 
  WITH RECURSIVE prep(\"startDateParam\", \"endDateParam\") AS (

  SELECT \"startDate\",\"endDate\" FROM \"somePeriods\" WHERE id = od.id
  UNION
  SELECT \"startDate\",\"endDate\" FROM \"somePeriods\", prep
  WHERE
    \"startDate\" <= (\"endDateParam\" + '1 day'::interval ) AND (\"endDateParam\" + '1 day'::interval ) <= \"endDate\"
  )
  SELECT max(\"endDateParam\") FROM prep
) AS \"endDateNew\"

FROM \"somePeriods\" AS od

)

SELECT * FROM \"maxRecursiveEndDate\";
"

Postgres 12コンテナを停止します

docker stop my_postgres_12_container

比較のためにPostgres 11を起動します

docker run -d --name my_postgres_11_container postgres:11.6

Postgres 11でクエリを実行する

docker exec my_postgres_11_container psql -U postgres -c "

CREATE OR REPLACE FUNCTION public.my_test_function()
 RETURNS SETOF record
 LANGUAGE sql
 IMMUTABLE SECURITY DEFINER
AS \$function\$ 

SELECT 
        1::integer AS id,
        '2019-11-20'::date AS \"startDate\",
        '2020-01-01'::date AS \"endDate\"

\$function\$;

EXPLAIN ANALYZE WITH  \"somePeriods\" AS  (
      SELECT * FROM my_test_function() AS 
      f(id integer, \"startDate\" date, \"endDate\" date)
),

\"maxRecursiveEndDate\" AS (

SELECT \"startDate\", \"endDate\", id, 
( 
  WITH RECURSIVE prep(\"startDateParam\", \"endDateParam\") AS (

  SELECT \"startDate\",\"endDate\" FROM \"somePeriods\" WHERE id = od.id
  UNION
  SELECT \"startDate\",\"endDate\" FROM \"somePeriods\", prep
  WHERE
    \"startDate\" <= (\"endDateParam\" + '1 day'::interval ) AND (\"endDateParam\" + '1 day'::interval ) <= \"endDate\"
  )
  SELECT max(\"endDateParam\") FROM prep
) AS \"endDateNew\"

FROM \"somePeriods\" AS od

)

SELECT * FROM \"maxRecursiveEndDate\";
"
4
cis

Pgbugsメーリングリストの関係者のおかげで、Just-in-time-compilation(役立つ背景情報が見つかります here )がPostgreSQL 12でデフォルトでオンになっていることがわかった問題。

SET jit = off;を使用してクエリを実行すると、問題が解決しました。それがないと、クエリは正常に高速に実行されます。

3
cis