it-swarm.com.de

Warum ist log (most ()) so langsam?

Wir haben eine komplexe Abfrage, die sehr langsam war. Ich habe es geschafft, die Abfrage auf eine einfache Reproduktion zu reduzieren. Es scheint, dass die Kombination von greatest und log die Ursache ist, aber ich verstehe nicht warum.

Hier ist ein vollständiges SQL-Fiddle-Beispiel zum Ausführen der Abfragen - und Sie können auch View the execution Plans Der Abfragen (klicken Sie auf den Link unten im Abfrageergebnis auf der SQL-Fiddle-Seite) )

Also hier ist die langsame Abfrage:

select count(value)
from (
         SELECT  log(greatest(1e-9, x)) as value
         from (select generate_series(1, 20000, 1) as x) as d
     ) t;

Wir generieren nur eine Reihe von 20.000 Zahlen und verwenden log(greatest()). Diese Abfrage dauert ungefähr 1,5 Sekunden.

Ich dachte, dass das Berechnen des Protokolls lange dauern kann, aber die folgende Abfrage ist auch schnell ( ~ 5ms ):

select count(value)
from (
         SELECT  log(x) as value
         from (select generate_series(1, 20000, 1) as x) as d
     ) t;

Nur als Test habe ich greatest und log ausgetauscht - das ist auch schnell ( ~ 5ms ):

select count(value)
from (
         SELECT  greatest(1e-9, log(x)) as value
         from (select generate_series(1, 20000, 1) as x) as d
     ) t;

Die QUERY PLANS Für alle 3 Abfragen sind gleich:

Aggregate (cost=22.51..22.52 rows=1 width=8)
-> Result (cost=0.00..5.01 rows=1000 width=4)

Kann jemand erklären, warum die erste Abfrage so langsam ist - und vielleicht kennt jemand eine Problemumgehung?

Mehr Details

langsame Plattformen

Bei all diesen Ergebnissen erhalte ich ähnliche Ergebnisse (die erste Abfrage ist um eine Größenordnung langsamer):

  • SQL Fiddle verwendet S. 9.6
  • mein lokaler PC mit ähnlichen Ergebnissen: Win10 64bit, S. 11.5 läuft in Docker
  • remote-Server: Ubuntu 18.04 64-Bit mit S. 11.5 in Docker
  • rextester.com

anzahl

Wenn ich count(value) in count(*) oder count(1) (Nummer eins) ändere, ist die Abfrage schnell

  • dies hilft mir jedoch nicht weiter, da die Produktionsabfrage nicht einmal eine Zählung enthält
  • ich frage mich jedenfalls, warum es in diesem Fall einen Unterschied gibt (die Daten enthalten keine Nullwerte).
3
TmTron

Sie rufen hier zwei verschiedene Protokollfunktionen auf: log(numeric,numeric) und log(double precision), und die erste ist viel langsamer als die zweite.

Beachten Sie, wie sich die Funktionsaufrufe in der folgenden EXPLAIN (ANALYZE, VERBOSE) unterscheiden, die mit PostgreSQL 11.5 (Linux Ubuntu) ausgeführt wird:

Langsame Version:

explain (analyze, verbose) select count(value)
from (
         SELECT  log(greatest(1e-9, x)) as value
         from (select generate_series(1, 20000, 1) as x) as d
     ) t;
                                              QUERY PLAN                                               
-------------------------------------------------------------------------------------------------------
 Aggregate  (cost=25.02..25.03 rows=1 width=8) (actual time=1174.349..1174.349 rows=1 loops=1)
   Output: count(log('10'::numeric, GREATEST(0.000000001, ((generate_series(1, 20000, 1)))::numeric)))
   ->  ProjectSet  (cost=0.00..5.02 rows=1000 width=4) (actual time=0.004..1.310 rows=20000 loops=1)
         Output: generate_series(1, 20000, 1)
         ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1)
 Planning Time: 0.123 ms
 Execution Time: 1174.385 ms

Schnelle Version:

explain (analyze, verbose) select count(value)
from (
         SELECT  log(greatest(1e-9::float, x)) as value
         from (select generate_series(1, 20000, 1) as x) as d
     ) t;
                                                  QUERY PLAN                                                   
---------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=25.02..25.03 rows=1 width=8) (actual time=6.693..6.693 rows=1 loops=1)
   Output: count(log(GREATEST('1e-09'::double precision, ((generate_series(1, 20000, 1)))::double precision)))
   ->  ProjectSet  (cost=0.00..5.02 rows=1000 width=4) (actual time=0.004..2.561 rows=20000 loops=1)
         Output: generate_series(1, 20000, 1)
         ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1)
 Planning Time: 0.096 ms
 Execution Time: 6.731 ms

greatest() ist nicht verantwortlich: Wenn Sie die Abfrage nur mit log(x) betrachten und x in numeric umwandeln, ist sie mit oder ohne greatest().

6
Daniel Vérité