关于在Postgresql中存储Lat / Lng坐标(列类型)

时间:2014-01-26 19:53:27

标签: performance postgresql types coordinates

我对postgresql比较新,这就是为什么我转向那些比我更有经验的人。

我将坐标存储在postgresql数据库中。

他们看起来像这样:35.21076593772987,11.22855348629825 35.210780222605616,11.22826420209139 35.210777635062875,11.228241328291957 35.210766843596794,11.228219799676775 35.210765045075604,11.228213072050166 35.21076234732945,11.228200962345223 35.21076324691649,11.228186161764323 35.21077314123606,11.228083902231146 35.210863083636866,11.227228492401766

长度范围从 800 7000

他们总是包括:

  • 数字(0-9
  • 空格(
  • 标点符号和逗号(. ,

但他们也可以包括:

  • 竖条(|

现在我将它们存储为 TEXT ,但据我所知, TEXT 存储在外部,这对性能有影响。您会建议切换到其他列类型吗?如果是这样,哪一个?

非常感谢。

3 个答案:

答案 0 :(得分:24)

为什么不使用PostGIS?

你忽略了这种数据的理想存储 - PostGIS的数据类型,特别是geography类型。

SELECT ST_GeogFromText('POINT(35.21076593772987 11.22855348629825)');

通过使用geography,您将数据存储在代表类型中,该类型支持该类型上的各种强大操作和索引。当然,这只是一个point;我强烈怀疑您的数据实际上是形状,在这种情况下,您应该使用the appropriate PostGIS geography constructor和输入格式。

使用geography的一大优势在于它是一种专门用于询问有关距离,“内部”等内容的现实问题的类型。您可以使用ST_Distance_Spheroid之类的东西来获得点之间的实际地球距离。

避免PostGIS?

如果您想避免使用PostGIS,只使用原生类型存储它,我建议使用point数组:

postgres=> SELECT ARRAY[
     point('35.21076593772987','11.22855348629825'), 
     point('35.210780222605616','11.22826420209139'), 
     point('35.210777635062875','11.228241328291957') 
];
                                                       array                                                        
--------------------------------------------------------------------------------------------------------------------
 {"(35.2107659377299,11.2285534862982)","(35.2107802226056,11.2282642020914)","(35.2107776350629,11.228241328292)"}
(1 row)

...除非您的积分实际代表形状,在这种情况下,请分别使用相应的类型 - pathpolygon

这仍然是一个有用的紧凑表示 - 实际上远远超过text - 在数据库中仍然很容易使用。

比较存储空间:

CREATE TABLE points_text AS SELECT '35.21076593772987,11.22855348629825 35.210780222605616,11.22826420209139 35.210777635062875,11.228241328291957 35.210766843596794,11.228219799676775 35.210765045075604,11.228213072050166 35.21076234732945,11.228200962345223 35.21076324691649,11.228186161764323 35.21077314123606,11.228083902231146 35.210863083636866,11.227228492401766'::text AS p

postgres=> SELECT pg_column_size(points_text.p) FROM points_text;
 pg_column_size 
----------------
            339
(1 row)

CREATE TABLE points_array AS
SELECT array_agg(point(px)) AS p from points_text, LATERAL regexp_split_to_table(p, ' ') split(px);

postgres=> SELECT pg_column_size(p) FROM points_array;
 pg_column_size 
----------------
            168
(1 row)

path更加紧凑,可能是一种更真实的方式来模拟您的数据

postgres=> SELECT pg_column_size(path('35.21076593772987,11.22855348629825 35.210780222605616,11.22826420209139 35.210777635062875,11.228241328291957 35.210766843596794,11.228219799676775 35.210765045075604,11.228213072050166 35.21076234732945,11.228200962345223 35.21076324691649,11.228186161764323 35.21077314123606,11.228083902231146 35.210863083636866,11.227228492401766'));
 pg_column_size 
----------------
             96
(1 row)

除非它是封闭的形状,在这种情况下使用polygon

不...

无论哪种方式,请不要只将其模型化为文本。当你试图解决诸如“我如何确定这个点是否落在本栏中路径的x距离内”之类的问题时,它会让你后来哭泣。 PostGIS使这种事情变得简单,但只有在您首先明智地存储数据时才会这样做。

请参阅this closely related question,其中讨论只是推送text字段内容的正当理由。

也不要过于担心内联和外线存储。你无法做到这一点,只有在你正确掌握了数据模型的语义后,才应该处理它。

答案 1 :(得分:5)

从性能的角度来看,

All of the character types(TEXT,VARCHAR,CHAR)的行为类似。它们通常存储在表格行中,除非它们非常大,在这种情况下它们可以存储在单独的文件中(称为TOAST文件)。

原因如下:

  1. 表行必须能够适合数据库页面大小(默认为8kb)

  2. 在内联存储的行中有一个非常大的字段会使访问表中其他字段的速度变慢。想象一个包含两列的表 - 文件名和文件内容 - 您想要找到一个特定的文件。如果您将文件内容存储为内联,那么您必须扫描每个文件以找到您想要的文件。 (忽略此示例可能存在的索引的影响)。

  3. 可以找到TOAST存储的详细信息here。请注意,外线存储不是唯一的策略 - 数据可能会被压缩和/或存储在线外。

    当一行超过阈值(默认为2kb)时,TOAST-ing开始,所以很可能你的行会受到影响,因为你声明它们可以达到7000个字符(尽管它可能是大多数它们只是压缩而不是存储在线外。)

    您可以使用命令ALTER TABLE ... SET STORAGE来影响表格的处理方式。

    此存储策略适用于您可能用于存储所描述数据类型的所有数据类型。需要更好地了解您的应用程序,为其他策略提供可靠的建议,但这里有一些想法:

    • 重新分解数据可能更好 - 而不是将所有坐标存储到一个大字符串中并在应用程序中处理它,将它们作为单独的行存储在引用的表中。因为在任何情况下,您的应用程序都将数据拆分并解析为坐标对以供使用,让数据库为您执行此操作是有道理的。

      如果需要选择或更新每个坐标集中的数据子集而不是总是在单个操作中使用或更新,或者如果这样做允许您更有效地索引数据,那么这将是一个好主意。

    • 由于我们讨论的是协调数据,您可以考虑使用PostGIS,这是PostgreSQL的扩展,专门用于处理这类数据。它还包括允许您过滤行的运算符,例如,在边界框的内部或外部。

答案 2 :(得分:2)

不要专注于这些数字是坐标的事实。相反,请注意它们是在非常有限的范围内的数字串,并且大致相同。您很可能对这些数字如何变化感兴趣(如果我只是将这些坐标插入地图中,看起来像是突尼斯海岸外物体的轨迹)。

我建议您将数字转换为双精度(53位精度~10 ^ 15中的9位 - 接近数字的LSD),并从系列中的第一个值中减去每个值。这将导致存储的数字小得多,并且相对准确度更高。您可以将差异存储为长整数,可能(适当地相乘),但将它们保持为双倍会更快。

如果您只是采用每个“轨迹”(我只是将GPS点的集合称为轨迹,我不知道它们是否代表您的情况)并给它一个唯一的ID,那么您可以拥有包含列的表格:

unique ID  |  trajectory ID  |     latitude      |      longitude
   1              1            11.2285534862982     35.2107802226056
   2              1            11.2282642020913     35.2107776350628
   3              1            11.2282413282919     35.2107668435967
   4              1            11.2282197996767     35.2107650450756
   5              1            11.2282130720501     35.2107623473294
   6              1            11.2282009623452     35.2107632469164
   7              1            11.2281861617643     35.2107731412360
   8              1            11.2280839022311     35.2108630836368

从文本到字符串的转换比你想象的慢很多 - 它需要很多操作。如果您最终将数据用作数字,我强烈建议将它们存储为数字......