7.7. 索引配置

GeoMesa提供了各种可用于定制和优化给定安装的配置选项。

7.7.1. 设置架构选项

对象的静态属性 SimpleFeatureType 调用时必须设置 createSchema ,并且不能在之后更改。大多数属性都是通过用户数据值控制的,可以在 SimpleFeatureType 或在特定属性上。可以通过多种方式设置用户数据。

如果您使用字符串来指示您的 SimpleFeatureType (例如,通过命令行工具,或在使用 SimpleFeatureTypes.createType ),您可以将类型级别选项附加到字符串的末尾,如下所示:

import org.locationtech.geomesa.utils.interop.SimpleFeatureTypes;

// append the user-data values to the end of the string, separated by a semi-colon
String spec = "name:String,dtg:Date,*geom:Point:srid=4326;option.one='foo',option.two='bar'";
SimpleFeatureType sft = SimpleFeatureTypes.createType("mySft", spec);

如果您具有现有的简单要素类型,或者您没有使用 SimpleFeatureTypes.createType ,您可以直接在要素类型中设置值:

// set the hint directly
SimpleFeatureType sft = ...
sft.getUserData().put("option.one", "foo");

如果您使用TypeSafe配置文件来定义您的简单功能类型,则可以包括一个‘User-Data’键:

geomesa {
  sfts {
    "mySft" = {
      attributes = [
        { name = name, type = String             }
        { name = dtg,  type = Date               }
        { name = geom, type = Point, srid = 4326 }
      ]
      user-data = {
        option.one = "foo"
      }
    }
  }
}

7.7.2. 设置属性选项

除了架构级用户数据外,每个属性还具有与其关联的用户数据。就像模式选项一样,可以通过多种方式设置属性用户数据。

如果您使用字符串来指示您的 SimpleFeatureType (例如,通过命令行工具,或在使用 SimpleFeatureTypes.createType ),您可以将属性选项附加在属性类型之后,并用冒号分隔:

import org.locationtech.geomesa.utils.interop.SimpleFeatureTypes;

// append the user-data after the attribute type, separated by a colon
String spec = "name:String:index=true,dtg:Date,*geom:Point:srid=4326";
SimpleFeatureType sft = SimpleFeatureTypes.createType("mySft", spec);

如果您具有现有的简单要素类型,或者您没有使用 SimpleFeatureTypes.createType ,您可以直接在属性描述符中设置用户数据:

// set the hint directly
SimpleFeatureType sft = ...
sft.getDescriptor("name").getUserData().put("index", "true");

如果使用TypeSafe配置文件定义简单功能类型,则可以将用户数据键添加到属性元素:

geomesa {
  sfts {
    "mySft" = {
      attributes = [
        { name = name, type = String, index = true }
        { name = dtg,  type = Date                 }
        { name = geom, type = Point, srid = 4326   }
      ]
    }
  }
}

7.7.3. 设置索引日期属性

对于包含日期属性的模式,GeoMesa将使用该属性作为主Z3/XZ3索引的一部分。如果方案包含多个日期属性,则可以通过User-Data键指定要使用的属性 geomesa.index.dtg 。如果您不想为任何日期建立索引,可以通过键将其禁用 geomesa.ignore.dtg 。如果未指定任何内容,将使用第一个声明的日期属性。

// specify the attribute 'myDate' as the indexed date
sft1.getUserData().put("geomesa.index.dtg", "myDate");

// disable indexing by date
sft2.getUserData().put("geomesa.ignore.dtg", true);

7.7.4. 自定义索引创建

您可以指定要创建的确切索引,而不是使用默认索引。这可以用于创建更少的索引(以加快接收速度,或者因为您只使用某些查询模式),或者用于创建额外的索引(例如,在非默认几何图形或日期上)。

索引是在调用 createSchema 。如果未指定任何内容,则将创建Z2、Z3(或XZ2和XZ3,具体取决于几何体类型)和ID索引以及已定义的任何属性索引。

警告

如果禁用索引,某些查询的速度可能会慢得多。

要配置索引,您可以在简单要素类型中设置用户数据值。用户数据密钥为 geomesa.indices.enabled ,并且它应该包含一个逗号分隔的列表,该列表包含索引标识符子集,如 索引概述

除了指定要创建的索引类型外,您还可以选择指定将在每个索引中使用的确切属性,方法是将 : S在索引名之后。以下示例显示了两种索引配置。第一个配置有一个包含默认属性的Z3索引。第二个配置在不同的几何结构上有两个Z3索引,以及在名称上有一个属性索引,其中包括在dtg上的一个二级索引。

import org.locationtech.geomesa.utils.interop.SimpleFeatureTypes;

String spec = "name:String,dtg:Date,*start:Point:srid=4326,end:Point:srid=4326";
SimpleFeatureType sft = SimpleFeatureTypes.createType("mySft", spec);
// enable a default z3 index on start + dtg
sft.getUserData().put("geomesa.indices.enabled", "z3");
// alternatively, enable a z3 index on start + dtg, end + dtg, and an attribute index on
// name with a secondary index on dtg. note that this overrides the previous configuration
sft.getUserData().put("geomesa.indices.enabled", "z3:start:dtg,z3:end:dtg,attr:name:dtg");

看见 设置架构选项 有关设置用户数据的详细信息,请参阅。如果您使用的是GeoMesa SchemaBuilder ,您可以改为调用 indexes 方法:

import org.locationtech.geomesa.utils.geotools.SchemaBuilder

val sft = SchemaBuilder.builder()
    .addString("name")
    .addDate("dtg")
    .addPoint("geom", default = true)
    .userData
    .indices(List("id", "z3", "attr"))
    .build("mySft")

7.7.5. 配置功能ID编码

虽然功能ID可以是任何字符串,但一个常见的用例是使用UUID。UUID是由格式为十六进制字符组成的全球唯一、特殊格式的字符串 {8}-{4}-{4}-{4}-{12} 例如, 28a12c18-e5ae-4c04-ae7b-bf7cdbfaf234 。UUID也可以被认为是一个128位的数字,可以以更小的大小进行序列化。

您可以使用用户数据密钥指示要素ID是UUID geomesa.fid.uuid 。如果在调用前设置 createSchema ,则功能ID将被序列化为16个字节的数字,而不是36个字节的字符串,从而节省一些开销:

sft.getUserData().put("geomesa.fid.uuid", "true");
datastore.createSchema(sft);

如果方案已创建,则仍可追溯指明要素ID为UUID,但您 must also indicate 它们不是以那种方式使用 geomesa.fid.uuid-encoded 。在以某些格式(例如箭头)导出数据时,这仍可能会带来一些好处:

SimpleFeatureType existing = datastore.getSchema("existing");
existing.getUserData().put("geomesa.fid.uuid", "true");
existing.getUserData().put("geomesa.fid.uuid-encoded", "false");
datastore.updateSchema("existing", existing);

警告

如果您表示正在使用有效的UUID,请确保使用它们。否则,您将在写入和/或读取数据时遇到异常。

7.7.6. 配置几何图形序列化

默认情况下,几何图形使用熟知二进制(WKB)格式的修改版本进行序列化。或者,可以使用 tiny well-known binary (TWKB) 格式化。TWKB在磁盘上将更小,但不允许完全双精度浮点精度。对于点几何图形,TWKB将占用4-12个字节(取决于指定的精度),而WKB将占用18个字节。对于具有多个坐标的线串、多边形或其他几何图形,由于TWKB的增量编码方案,节省的空间会更多。

对于任何几何类型属性,都可以通过设置浮点精度来启用TWKB序列化 precision 用户数据密钥。精度表示将存储的小数位数,必须介于-7和7之间(包括-7和7)。负精度可用于表示对小数点左侧的整数进行舍入。作为参考,6位纬度/经度精度可以存储大约10厘米的分辨率。

对于具有两个以上维度的几何图形,可以分别指定Z维度和M维度的精度。通常,这些尺寸不需要以与X/Y相同的分辨率存储。默认情况下,Z以精度1存储,M以精度0存储。要更改此设置,请在X/Y精度后指定其他精度,并用逗号分隔。例如, 6,1,0 会将X/Y精度设置为6,将Z精度设置为1,将M精度设置为0。Z和M精度必须介于0和7之间,包括0和7。

TWKB序列化可以在创建新架构时设置,但也可以随时通过 updateSchema 方法。如果修改现有架构,则不会更新已写入的任何数据。

SimpleFeatureType sft = ...
sft.getDescriptor("geom").getUserData().put("precision", "4");

看见 设置属性选项 有关如何设置属性选项的详细信息。

7.7.7. 配置列组

对于支持它的后端(当前是HBase和Acumulo),可以将属性子集复制到单独的列组中。如果可能,将只扫描减少的列组以查找查询,从而避免从磁盘读取未使用的数据。对于具有大量属性的模式,这可以加快某些查询的速度,但代价是将更多数据写入磁盘。

使用属性级用户数据按属性指定列组。一个属性可以属于多个列组,在这种情况下,它将被复制多次。所有属性都将属于默认列组,而不必指定任何内容。看见 设置属性选项 有关如何设置属性选项的详细信息。

使用用户数据键指定列组 column-groups ,其值为该属性的组的逗号分隔列表。建议列组名称保持简短(理想情况下是单个字符),以最大限度地减少磁盘使用量。如果列组与GeoMesa使用的默认组之一冲突,它将在创建模式时抛出异常。目前,保留的组为 d 对于HBase和 FAI ,以及 B 为了阿库库洛。

SimpleFeatureType sft = ...
sft.getDescriptor("name").getUserData().put("column-groups", "a,b");

7.7.8. 配置Z索引碎片

GeoMesa允许配置Z2/Z3/XZ2/XZ3索引被划分成的碎片(或拆分)的数量。此参数可以分别针对每个 SimpleFeatureType 。如果不指定,则GeoMesa将默认为4个分片。碎片数必须介于1和127之间。

碎片允许对表进行预拆分,这为读写提供了一些初始并行性。随着写入的数据越来越多,表通常会根据大小进行拆分,因此不再需要显式碎片。对于较小的数据集,碎片更为重要,因为表可能永远不会因为大小而拆分。将分片数量设置得太高可能会降低性能,因为每个查询需要执行更多的计算。

分片的数量在调用时设置 createSchema 。它可以使用密钥通过简单要素类型用户数据进行指定 geomesa.z2.splitsgeomesa.z3.splits 分别用于二维和三维索引。看见 设置架构选项 有关设置用户数据的详细信息,请参阅。

sft.getUserData().put("geomesa.z3.splits", "4");

7.7.9. 配置Z索引时间间隔

GeoMesa使用z曲线索引进行基于时间的查询。默认情况下,时间被拆分为长达一周的区块,并按周编制索引。如果您的查询通常比一周大得多或小得多,您可能希望以不同的间隔进行分区。GeoMesa提供了四个间隔- dayweekmonthyear 。随着时间间隔变大,必须为查询检查的分区更少,但每个时间间隔的精度将会下降。

如果您通常一次查询几个月的数据,则按月编制索引可能会提供更好的性能。或者,如果您通常一次查询几分钟的数据,则每天的索引可能会更快。默认的每周分区倾向于为大多数方案提供良好的平衡。请注意,最佳分区取决于查询模式,而不是数据的分布。

调用时设置时间间隔 createSchema 。它可以使用密钥通过简单要素类型用户数据进行指定 geomesa.z3.interval 。看见 设置架构选项 有关设置用户数据的详细信息,请参阅。

sft.getUserData().put("geomesa.z3.interval", "month");

7.7.10. 配置XZ-索引精度

GeoMesa使用扩展的z曲线索引来存储带范围的几何图形。可以通过指定用于存储几何图形的分辨率级别来自定义索引。默认情况下,分辨率级别为12。如果具有非常大的几何体,则可能需要降低此值。相反,如果您有非常小的几何图形,您可能想要提升它。

索引的分辨率级别是在调用 createSchema 。它可以使用密钥通过简单要素类型用户数据进行指定 geomesa.xz.precision 。看见 设置架构选项 有关设置用户数据的详细信息,请参阅。

sft.getUserData().put("geomesa.xz.precision", 12);

有关分辨率级别(G)的更多信息,请参见Böhm、Klump和Kriegel所著的“XZ排序:具有空间扩展的对象的空间填充曲线”。

7.7.11. 配置属性索引碎片

GeoMesa允许配置将属性索引划分成的碎片(或拆分)的数量。此参数可以分别针对每个 SimpleFeatureType 。如果不指定,则GeoMesa将默认为4个分片。碎片数必须介于1和127之间。

看见 配置Z索引碎片 了解更多关于碎片的背景知识。

分片的数量在调用时设置 createSchema 。它可以使用密钥通过简单要素类型用户数据进行指定 geomesa.attr.splits 。看见 设置架构选项 有关设置用户数据的详细信息,请参阅。

sft.getUserData().put("geomesa.attr.splits", "4");

7.7.12. 配置属性基数

GeoMesa允许将属性标记为高基数或低基数。如果设置,则此提示将用于查询规划。有关详细信息,请参阅 基数暗示

要设置属性的基数,请使用键 cardinality 在属性上,其值为 highlow

SimpleFeatureType sft = ...
sft.getDescriptor("name").getUserData().put("index", "true");
sft.getDescriptor("name").getUserData().put("cardinality", "high");

7.7.13. 配置分区索引

为了帮助处理大型数据集,GeoMesa可以根据每个功能的属性将每个索引划分为单独的表。将多个表用于单个索引可以简化集群的管理,例如,通过使删除旧数据变得简单。

在创建简单要素类型时,必须通过用户数据指定分区,然后才能调用 createSchema 。要指示分区方案,请使用键 geomesa.table.partition 。当前唯一有效的值是 time ,以指示基于时间的分区:

sft.getUserData().put("geomesa.table.partition", "time");

请注意,要启用分区,架构必须包含默认日期字段。

启用分区后,每个索引将由多个物理表组成。根据Z间隔对表进行分区(请参见 配置Z索引时间间隔 )。表在需要时动态创建。

分区表仍可以进行预拆分,如中所述 配置索引拆分 。对于Z3拆分,最小/最大日期配置由分区自动确定,不需要指定。

当查询必须扫描多个表时,默认情况下将按顺序扫描表。要改为并行扫描表,请设置系统属性 geomesa.partition.scan.parallel=true 。请注意,启用后,跨多个分区的查询可能会给系统带来很大的负载。

GeoMesa命令行工具提供了管理分区的函数;请参阅 manage-partitions 了解更多细节。

7.7.14. 配置索引拆分

当计划摄取大量数据时,如果预先知道分布,那么在写入之前预先拆分表可能很有用。这从一开始就提供了跨集群的并行性,并且不依赖于实现触发器(通常根据大小拆分表)。

拆分是通过实现 org.locationtech.geomesa.index.conf.TableSplitter 界面。

7.7.14.1. 指定表拆分器

在创建简单要素类型时,可以通过用户数据指定表拆分器,然后再调用 createSchema

要指示表拆分器类,请使用键 table.splitter.class

sft.getUserData().put("table.splitter.class", "org.example.CustomSplitter");

要指示给定表拆分器的任何选项,请使用键 table.splitter.options

sft.getUserData().put("table.splitter.options", "foo,bar,baz");

看见 设置架构选项 有关设置用户数据的详细信息,请参阅。

7.7.14.2. 默认表拆分器

一般来说, table.splitter.class 可以省略。如果是这样,GeoMesa将使用默认实现,该实现允许使用 table.splitter.options 。如果未指定选项,则所有表通常会创建4个拆分(基于分片的数量)。默认ID索引拆分假设要素ID是随机分布的UUID。

For the default splitter, table.splitter.options should consist of comma-separated entries, in the form key1:value1,key2:value2. Entries related to a given index should start with the index identifier, e.g. one of id, z3, z2 or attr (xz3 and xz2 indices use z3 and z2, respectively).

索引

选择权

描述

Z3/XZ3

z3.min

数据的最小日期

z3.max

数据的最大日期

z3.bits

要拆分的前导位数

Z2/XZ2

z2.bits

要拆分的前导位数

ID

id.pattern

拆分图案

属性

attr.<attribute>.pattern

给定属性的拆分模式

7.7.14.2.1. Z3/XZ3拆分

日期用于根据Z3时间前缀(通常为周)进行拆分。它们在表格中指定 yyyy-MM-dd 。如果指定了最小日期,但未指定最大日期,则默认为当前日期。在日期之后,可以根据位数拆分Z值(请注意,由于索引格式,不能在没有日期的情况下指定位)。例如,指定两位将创建拆分00、01、10和11。 <number of z shards> * <number of time periods> * 2 ^ <number of bits>

7.7.14.2.2. Z2/XZ2拆分

如果给出了任何选项,则必须指定位数。例如,指定两位将创建拆分00、01、10和11。 <number of z shards> * 2 ^ <number of bits>

7.7.14.2.3. ID和属性拆分

拆分由图案定义。对于ID索引,模式(S)应用于单个特征ID。对于属性索引,通过将属性名称指定为选项的一部分,可以单独配置索引的每个属性。例如,给定架构 name:String:index=true,*geom:Point:srid=4326 vt.的. name 可以使用配置属性拆分 attr.name.pattern

模式由一个或多个用方括号括起来的单个字符或范围组成。有效字符可以是数字0到9中的任意一个,也可以是大写或小写的字母a到z中的任意一个。范围是由破折号分隔的两个字符。每组方括号对应于一个字符,允许嵌套拆分。对于数字类型,负号可以用前导负号指定。

例如,模式 [0-9] 将根据数字0到9创建10个拆分。模式 [0-9][0-9] 会造成100个裂口。这种模式 [-][0-9] 将根据数字-9到0创建10个拆分。这种模式 [0-9a-f] 将根据小写十六进制字符创建16个拆分。这种模式 [0-9A-F] 会对大写字符执行同样的操作。

对于数据热点,可以通过在键后面添加附加选项2、3等来指定多个模式。例如,如果大多数 name 值以字母开头 ft ,拆分可以指定为 attr.name.pattern:[a-z],attr.name.pattern2:[f][a-z],attr.name.pattern3:[t][a-z]

对于数字类型的属性,只有数字才被视为有效字符。由于词汇化,正常的数字前缀将无法正常工作。例如,如果数据具有8000-9000范围内的数字,则指定 [8-9][0-9] 将不会正确拆分数据。相反,应该添加尾随零以达到适当的长度,例如 [8-9][0-9][0][0]

7.7.14.2.4. 完整示例

import org.locationtech.geomesa.utils.interop.SimpleFeatureTypes;

String spec = "name:String:index=true,age:Int:index=true,dtg:Date,*geom:Point:srid=4326";
SimpleFeatureType sft = SimpleFeatureTypes.createType("foo", "spec");
sft.getUserData().put("table.splitter.options",
    "id.pattern:[0-9a-f],attr.name.pattern:[a-z],z3.min:2018-01-01,z3.max:2018-01-31,z3.bits:2,z2.bits:4");

7.7.15. 配置查询拦截器

GeoMesa提供了在通过查询拦截器和警卫执行查询之前将自定义逻辑应用于查询的机会。有关它们的用法和配置的完整讨论,请访问 查询拦截器和守卫

7.7.16. 配置缓存的统计信息

GeoMesa将在摄取过程中收集和存储属性的汇总统计数据,然后可用于查找和/或查询规划。可使用密钥通过简单要素类型用户数据启用或禁用统计数据生成 geomesa.stats.enable 。看见 设置架构选项 有关设置用户数据的详细信息,请参阅。

备注

缓存的统计数据目前仅适用于Acumulo和Redis数据存储

如果启用,则始终收集默认几何体、默认日期和任何索引属性的统计信息。看见 基于成本的战略 了解更多详细信息。此外,还可以使用键为统计信息标记其他属性 keep-stats 在各个属性上,如中所述 设置属性选项 。这将导致收集这些属性的以下统计信息:

  • 最小/最大(边界)

  • TOP-K

只有类型为 StringIntegerLongFloatDoubleDateGeometry 可以被标记为统计数据。

例如:

// set the hint directly
SimpleFeatureType sft = ...
sft.getDescriptor("name").getUserData().put("keep-stats", "true");

看见 分析命令通过GeoMesa API访问统计数据 获取有关读取缓存的统计信息的信息。

7.7.17. 配置临时优先级

对于一些大型的基于时间的数据集,利用时态谓词的索引几乎总是比不利用时态谓词的索引查询速度更快。可以通过设置用户数据键来配置模式以确定时态谓词的优先级 geomesa.temporal.priority

sft.getUserData().put("geomesa.temporal.priority", "true");

这可以在调用之前进行配置 createSchema ,或通过调用 updateSchema

7.7.18. 配置所需的可见性

GeoMesa支架 数据安全 通过使用可见性标签来保护对敏感数据的访问。为了帮助防止数据溢出,可以将架构配置为拒绝任何不包含有效可见性的写入。要启用此设置,请使用User-Data键 geomesa.vis.required

sft.getUserData().put("geomesa.vis.required", "true");

这可以在调用之前进行配置 createSchema ,或通过调用 updateSchema

请注意,此配置将防止在正常写入路径中丢失可见性标签,但仍可以通过较旧的客户端、批量加载或直接访问底层数据库来写入未标记的数据。

在Acumulo数据存储中,设置此配置也会设置Acumulo ReqVisFilter 这将防止在查询中返回任何未标记的数据。

7.7.19. 混合几何图形类型

常见的陷阱是在创建模式时不必要地指定通用几何类型。由于GeoMesa依赖于几何体类型进行索引决策,因此这可能会对性能产生负面影响。

如果默认几何类型为 Geometry (即同时支持点和非点功能),则必须显式启用“混合”索引模式。所有其他几何图形类型 (PointLineStringPolygon 等)不受影响。

调用时必须声明混合几何 createSchema 。它可以使用密钥通过简单要素类型用户数据进行指定 geomesa.mixed.geometries 。看见 设置架构选项 有关设置用户数据的详细信息,请参阅。

sft.getUserData().put("geomesa.mixed.geometries", "true");