PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Recherche sur la lenteur de PostGIS (édition 2019)

Juste à la fin de l'année dernière, l'un de nos clients de longue date est venu nous voir car l'une de ses requêtes PostgreSQL de longue date impliquant des calculs de géométrie PostGIS était beaucoup plus lente pour des valeurs spécifiques. Nous avons recherché le problème et découvert comment le résoudre; continuer à lire! Ce que nous avons trouvé comme cause du problème va vous surprendre !

L'observation de départ, rapportée par notre client, était que l'exécution d'une requête impliquant ST_DistanceSpheroid a pris environ 7 millisecondes lorsqu'on lui a demandé de renvoyer la distance à POINT(33.681953 23.155994) sur un sphéroïde spécifique, mais si ce point a été déplacé vers POINT(33.681953 23.1559941) (une différence de seulement 0.0000001 ) puis cela a pris 0,13 milliseconde. 60 fois plus rapide ! Qu'est-ce qui pourrait se passer sur Terre (un autre sphéroïde !) ?

Au départ, nous n'étions pas en mesure de reproduire la lenteur dans nos environnements de test. Entre nos mains, les deux requêtes s'exécuteraient aussi rapidement, sans ralentissement. Nous avons creusé les versions spécifiques des logiciels utilisés en pensant qu'une mise à jour pourrait être nécessaire. Nous avons utilisé les versions signalées par le client :PostgreSQL 10.11, PostGIS 2.4.4, libproj 4.93. Nous sommes retournés aux âges des cavernes en rétrogradant à ces versions précises, sans succès.

Finalement, nous avons pris conscience que le client utilisait Ubuntu 18.04, nous avons donc essayé cela… et voilà, le problème s'est reproduit là-bas. Autant dire que nous avons sauté sur l'occasion de profiler la requête dans cette machine. Nous avons ceci :

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

Charabia, dites-vous. Cependant, il y a quelque chose de très curieux à propos de ce profil… et vous devez ignorer les 26 premières lignes et vous concentrer sur le __mul ligne là. Vous voyez que 60,82 % de temps « personnel » ? (Je peux entendre le son de la réalisation que votre esprit vient de faire). Alors pourquoi faut-il autant de temps pour certains points du sphéroïde et pas pour d'autres ? Et aussi pourquoi cela prend-il longtemps dans Ubuntu 18.04 mais pas dans d'autres machines ? Pourquoi la mise à niveau de PostGIS ne résout-elle pas le problème ?

La réponse m'a été suggérée une fois que j'ai réalisé ce qui était évident :PostGIS fait beaucoup de trigonométrie (sinus, cosinus, tangente, etc.) en appelant libm les fonctions. En regardant les journaux des modifications de la glibc, nous avons trouvé quelques optimisations dans les fonctions de trigonométrie :pour certaines entrées, les calculs de trigonométrie prennent des raccourcis qui ne peuvent pas être pris pour d'autres entrées ; et ces raccourcis ont été optimisés au fil du temps. En effet, les annonces de la glibc pour les versions 2.27 et 2.29 mentionnent toutes deux des optimisations dans les fonctions sinus/cosinus/etc. Apparemment, il était une fois des optimisations d'Intel qui étaient censées fournir des résultats très précis, mais quelqu'un s'est alors rendu compte que la revendication de précision était incorrecte, alors la glibc a désactivé l'utilisation de ces optimisations; plus tard, ce truc a été réimplémenté d'une manière différente mais encore une fois rapide. Ou quelque chose comme ça - pour les étrangers comme moi, il est difficile de comprendre les détails exacts.

Nous pensions que la mise à niveau vers une version plus récente de la glibc résoudrait le problème, laissant tout le reste identique. Notre client a essayé cela, et en effet c'était vrai, et ils étaient heureux. Nous ne savons pas vraiment quel de ces changements glibc étaient responsables, mais une chose est claire :c'est toujours une bonne idée d'exécuter des choses sur un logiciel à jour.

Gardez à l'esprit que les bords saignants sont tranchants... alors faites attention.