PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Ricercare la lentezza di PostGIS (edizione 2019)

Proprio come l'anno scorso stava finendo, uno dei nostri clienti di lunga data è venuto da noi perché una delle loro query PostgreSQL esistenti da tempo che coinvolgono i calcoli della geometria di PostGIS era molto più lenta per valori specifici. Abbiamo studiato il problema e abbiamo scoperto come risolverlo; continuare a leggere! Quello che abbiamo trovato come causa del problema ti sorprenderà!

L'osservazione iniziale, riportata dal nostro cliente, è stata quella di eseguire una query che coinvolge ST_DistanceSpheroid ci sono voluti circa 7 millisecondi quando è stato chiesto di restituire la distanza a POINT(33.681953 23.155994) su uno sferoide specifico, ma se quel punto è stato spostato su POINT(33.681953 23.1559941) (una differenza di appena 0.0000001 ) poi ci sono voluti 0,13 millisecondi. 60 volte più veloce! Cosa diavolo sta succedendo sulla Terra (un altro sferoide!)?

Inizialmente, non siamo stati in grado di riprodurre la lentezza nei nostri ambienti di prova. Nelle nostre mani, entrambe le query funzionerebbero allo stesso modo rapidamente, senza rallentamenti. Abbiamo analizzato le versioni specifiche del software in uso pensando che potesse essere necessario un aggiornamento. Abbiamo utilizzato le versioni riportate dal cliente:PostgreSQL 10.11, PostGIS 2.4.4, libproj 4.93. Siamo tornati all'età delle caverne effettuando il downgrade a quelle versioni precise, senza successo.

Alla fine ci siamo resi conto che il cliente stava usando Ubuntu 18.04, quindi l'abbiamo provato... ed ecco, il problema si è riprodotto lì. È sufficiente dire che abbiamo colto al volo l'opportunità di profilare la query in quella macchina. Abbiamo questo:

Samples: 224K of event 'cpu-clock', Event count (approx.): 56043500000
  Children      Self  Command   Shared Object           Symbol
+   84.86%     0.00%  postgres  [unknown]               [.] 0xffffffffffffffff
+   84.59%     0.00%  postgres  postgres                [.] DirectFunctionCall4Coll
+   84.58%     0.00%  postgres  postgis-2.5.so          [.] geometry_distance_spheroid
+   84.56%     0.00%  postgres  liblwgeom-2.5.so.0.0.0  [.] lwgeom_distance_spheroid
+   84.31%     0.19%  postgres  libm-2.27.so            [.] __sincos
+   84.18%     0.00%  postgres  libm-2.27.so            [.] __cos_local (inlined)
+   84.13%     0.00%  postgres  libm-2.27.so            [.] cslow2 (inlined)
+   84.05%     0.01%  postgres  libm-2.27.so            [.] __mpcos
+   83.95%     0.32%  postgres  libm-2.27.so            [.] __c32
+   83.87%     0.00%  postgres  postgres                [.] ExecInterpExpr
+   83.75%     0.00%  postgres  postgres                [.] standard_ExecutorRun
+   83.75%     0.00%  postgres  postgres                [.] ExecutePlan (inlined)
+   83.73%     0.00%  postgres  postgres                [.] ExecProcNode (inlined)
+   83.73%     0.00%  postgres  postgres                [.] ExecScan
+   83.67%     0.00%  postgres  postgres                [.] ExecProject (inlined)
+   83.67%     0.00%  postgres  postgres                [.] ExecEvalExprSwitchContext (inlined)
+   83.65%     0.00%  postgres  postgres                [.] _SPI_execute_plan
+   83.60%     0.00%  postgres  postgres                [.] _SPI_pquery (inlined)
+   83.49%     0.01%  postgres  plpgsql.so              [.] exec_stmts
+   83.49%     0.00%  postgres  plpgsql.so              [.] exec_stmt (inlined)
+   83.49%     0.00%  postgres  plpgsql.so              [.] exec_stmt_fori (inlined)
+   83.48%     0.00%  postgres  plpgsql.so              [.] exec_stmt_perform (inlined)
+   83.48%     0.00%  postgres  plpgsql.so              [.] exec_run_select
+   83.47%     0.00%  postgres  postgres                [.] SPI_execute_plan_with_paramlist
+   81.67%     0.01%  postgres  liblwgeom-2.5.so.0.0.0  [.] edge_distance_to_point
+   81.67%     0.00%  postgres  liblwgeom-2.5.so.0.0.0  [.] 0x00007f2ce1c2c0e6
+   61.85%    60.82%  postgres  libm-2.27.so            [.] __mul
+   54.83%     0.01%  postgres  liblwgeom-2.5.so.0.0.0  [.] sphere_distance
+   27.14%     0.00%  postgres  plpgsql.so              [.] exec_stmt_block
+   26.67%     0.01%  postgres  liblwgeom-2.5.so.0.0.0  [.] geog2cart
+   19.24%     0.00%  postgres  libm-2.27.so            [.] ss32 (inlined)
+   18.28%     0.00%  postgres  libm-2.27.so            [.] cc32 (inlined)
+   12.55%     0.76%  postgres  libm-2.27.so            [.] __sub
+   11.46%    11.40%  postgres  libm-2.27.so            [.] sub_magnitudes
+    8.15%     4.89%  postgres  libm-2.27.so            [.] __add
+    4.94%     0.00%  postgres  libm-2.27.so            [.] add_magnitudes (inlined)
+    3.18%     3.16%  postgres  libm-2.27.so            [.] __acr
+    2.66%     0.00%  postgres  libm-2.27.so            [.] mcr (inlined)
+    1.44%     0.00%  postgres  liblwgeom-2.5.so.0.0.0  [.] lwgeom_calculate_gbox_geodetic
+    1.44%     0.00%  postgres  liblwgeom-2.5.so.0.0.0  [.] ptarray_calculate_gbox_geodetic

Incomprensibile, dici. Tuttavia, c'è qualcosa di molto curioso in questo profilo... e devi ignorare le prime 26 righe e concentrarti sul __mul linea lì. Vedi quel 60,82% del tempo "auto"? (Riesco a sentire il suono della realizzazione che la tua mente ha appena fatto). Allora perché ci vuole così tanto tempo per alcuni punti sullo sferoide e non altri? E anche perché ci vuole molto tempo in Ubuntu 18.04 ma non in altre macchine? Perché l'aggiornamento di PostGIS non risolve il problema?

La risposta mi è stata suggerita una volta che ho capito cosa era ovvio:PostGIS fa molta trigonometria (seno, coseno, tangente ecc.) chiamando libm funzioni. Guardando i log delle modifiche di glibc abbiamo trovato alcune ottimizzazioni nelle funzioni di trigonometria:per alcuni input, i calcoli di trigonometria prendono scorciatoie che non possono essere prese per altri input; e tali scorciatoie sono state ottimizzate nel tempo. In effetti, gli annunci di glibc sia per 2.27 che per 2.29 menzionano ottimizzazioni nelle funzioni seno/coseno/ecc. Apparentemente, una volta c'erano alcune ottimizzazioni da parte di Intel che avrebbero dovuto fornire risultati molto accurati, ma poi qualcuno si è reso conto che l'affermazione di accuratezza non era corretta, quindi glibc ha disabilitato l'uso di tali ottimizzazioni; in seguito, quella roba è stata reimplementata in un modo diverso ma ancora una volta veloce. O qualcosa del genere:per gli estranei come me è difficile capire i dettagli esatti.

Sospettavamo che l'aggiornamento a una versione più recente di glibc avrebbe risolto il problema, lasciando tutto il resto uguale. Il nostro cliente ci ha provato, ed in effetti era vero, ed erano felici. Non siamo davvero certi di quale di questi cambiamenti glibc erano responsabili, ma una cosa è chiara:è sempre una buona idea eseguire le cose su un software aggiornato.

Tieni presente che i bordi sanguinanti sono affilati... quindi fai attenzione là fuori.