Precis som förra året var över, kom en av våra långvariga kunder till oss eftersom en av deras sedan länge existerande PostgreSQL-frågor som involverade PostGIS-geometriberäkningar var mycket långsammare för specifika värden. Vi undersökte problemet och fick reda på hur vi skulle lösa det; Läs vidare! Det vi hittade som orsak till problemet kommer att överraska dig!
Startobservationen, rapporterad av vår kund, var att köra en fråga som involverade ST_DistanceSpheroid
tog cirka 7 millisekunder när du blev ombedd att återställa avståndet till POINT(33.681953 23.155994)
på en specifik sfäroid, men om den punkten flyttades till POINT(33.681953 23.1559941)
(en skillnad på bara 0.0000001
) sedan tog det 0,13 millisekund. 60 gånger snabbare! Vad i hela friden (en annan sfäroid!) kan vara på gång?
Till en början kunde vi inte återskapa långsamheten i våra testmiljöer. I våra händer skulle båda frågorna fungera lika snabbt, utan avmattning. Vi grävde ner till de specifika versionerna av programvara som används och tänkte att en uppdatering kan behövas. Vi använde de versioner som rapporterats av kunden:PostgreSQL 10.11, PostGIS 2.4.4, libproj 4.93. Vi återvände till grottåldrarna genom att nedgradera till de exakta versionerna, utan framgång.
Så småningom blev vi medvetna om att kunden använde Ubuntu 18.04, så vi försökte det ... och se och se, problemet reproducerades där. Det räcker med att säga att vi hoppade på möjligheten att profilera frågan i den maskinen. Vi fick detta:
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
Gibberi, säger du. Men det finns något väldigt nyfiket med den här profilen ... och du måste ignorera de första 26 raderna och fokusera på __mul linje där. Ser du att 60,82 % av "självtiden"? (Jag kan höra ljudet av insikten som du just gjort). Så varför tar det så mycket tid för vissa punkter på sfäroiden och inte andra? Och varför tar det lång tid i Ubuntu 18.04 men inte i andra maskiner? Varför löser inte uppgraderingen av PostGIS problemet?
Svaret föreslogs för mig när jag insåg vad som var uppenbart:PostGIS gör mycket trigonometri (sinus, cosinus, tangent etc) genom att anropa libm
funktioner. När vi tittade på glibc-ändringsloggarna hittade vi några optimeringar i trigonometrifunktioner:för vissa ingångar tar trigonometriberäkningar genvägar som inte kan användas för andra ingångar; och sådana genvägar har optimerats med tiden. Faktum är att glibc:en för både 2.27 och 2.29 nämner båda optimeringar i sinus/cosinus/etc-funktioner. Tydligen fanns det en gång i tiden några optimeringar från Intel som skulle ge mycket exakta resultat, men sedan insåg någon att påståendet om noggrannhet var felaktigt, så glibc inaktiverade användningen av dessa optimeringar; senare implementerades det där på ett annat men återigen snabbt sätt. Eller något liknande — för utomstående som mig är det svårt att ta reda på de exakta detaljerna.
Vi misstänkte att uppgradering till en nyare version av glibc skulle lösa problemet och lämna allt detsamma. Vår kund försökte det, och det var verkligen sant, och de var nöjda. Vi är inte riktigt säkra på vilket av dessa glibc-ändringar var ansvariga, men en sak är klar:det är alltid en bra idé att köra saker på uppdaterad programvara.
Tänk på att blödande kanter är vassa ... så var försiktig där ute.