查询数据网格缓存


Red Hat Data Grid 8.5

在 Data Grid cache 中查询您的数据

Red Hat Customer Content Services

摘要

借助 Data Grid,您可以使用嵌入式和远程缓存执行查询,以高效并快速查找您的数据集中的值。

Red Hat Data Grid

Data Grid 是一个高性能分布式内存数据存储。

无架构数据结构
将不同对象存储为键值对的灵活性。
基于网格的数据存储
旨在在集群中分发和复制数据。
弹性扩展
动态调整节点数量,以便在不中断服务的情况下满足需求。
数据互操作性
从不同端点在网格中存储、检索和查询数据。

Data Grid 文档

红帽客户门户网站中提供了 Data Grid 的文档。

Data Grid 下载

访问红帽客户门户上的 Data Grid 软件下载

注意

您必须有一个红帽帐户才能访问和下载数据中心软件。

使开源包含更多

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。我们从这四个术语开始:master、slave、黑名单和白名单。由于此项工作十分艰巨,这些更改将在即将推出的几个发行版本中逐步实施。详情请查看 CTO Chris Wright 的信息

第 1 章 索引数据网格缓存

Data Grid 可以在您的缓存中创建值索引,以提高查询性能,从而比非索引查询更快地提供结果。索引还允许您在查询中使用全文本搜索功能。

注意

Data Grid 使用 Apache Lucene 技术在缓存中索引值。

1.1. 配置 Data Grid 以索引缓存

在缓存配置中启用索引,并指定在创建索引时数据网格应包含哪些实体。

在使用查询时,您应该始终将 Data Grid 配置为索引缓存。索引对您的查询提供了显著的性能,可让您更快地了解您的数据。

流程

  1. 在缓存配置中启用索引。

    <distributed-cache>
      <indexing>
        <!-- Indexing configuration goes here. -->
      </indexing>
    </distributed-cache>
    提示

    配置中添加 indexing 元素可启用索引,而无需包含 enabled=true 属性。

    对于添加此元素的远程缓存,也会隐式将编码配置为 ProtoStream。

  2. 使用 indexed-entity 元素指定要索引的实体。

    <distributed-cache>
      <indexing>
        <indexed-entities>
          <indexed-entity>...</indexed-entity>
        </indexed-entities>
      </indexing>
    </distributed-cache>
protobuf 信息
  • 将 schema 中声明的消息指定为 indexed-entity 元素的值,例如:

    <distributed-cache>
      <indexing>
        <indexed-entities>
          <indexed-entity>org.infinispan.sample.Car</indexed-entity>
          <indexed-entity>org.infinispan.sample.Truck</indexed-entity>
        </indexed-entities>
      </indexing>
    </distributed-cache>

    此配置通过 book_sample 软件包名称对模式中的 Book 消息进行索引。

    package book_sample;
    
    /* @Indexed */
    message Book {
    
        /* @Text(projectable = true) */
        optional string title = 1;
    
        /* @Text(projectable = true) */
        optional string description = 2;
    
        // no native Date type available in Protobuf
        optional int32 publicationYear = 3;
    
        repeated Author authors = 4;
    }
    
    message Author {
        optional string name = 1;
        optional string surname = 2;
    }
Java 对象
  • 指定包含 @Indexed 注解的每个类的完全限定名称(FQN)。

XML

<distributed-cache>
  <indexing>
    <indexed-entities>
      <indexed-entity>book_sample.Book</indexed-entity>
    </indexed-entities>
  </indexing>
</distributed-cache>

ConfigurationBuilder

import org.infinispan.configuration.cache.*;

ConfigurationBuilder config=new ConfigurationBuilder();
config.indexing().enable().storage(FILESYSTEM).path("/some/folder").addIndexedEntity(Book.class);

1.1.1. 索引配置

Data Grid 配置控制索引的存储和构建方式。

索引存储

您可以配置 Data Grid 存储索引的方式:

  • 在主机文件系统中,这是默认的,并在重启之间保留索引。
  • 在 JVM 堆内存中,这意味着索引重启后不会保留。
    您应该只针对小数据集将索引存储在 JVM 堆内存中。

File system

<distributed-cache>
  <indexing storage="filesystem" path="${java.io.tmpdir}/baseDir">
    <!-- Indexing configuration goes here. -->
  </indexing>
</distributed-cache>

JVM 堆内存

<distributed-cache>
  <indexing storage="local-heap">
    <!-- Additional indexing configuration goes here. -->
  </indexing>
</distributed-cache>

索引路径

当 storage 为 'filesystem' 时,指定索引的文件系统路径。该值可以是相对或绝对路径。相对路径相对于配置的全局持久位置创建,或者在禁用全局状态时到当前工作目录。

默认情况下,缓存名称用作索引路径的相对路径。

重要

在设置自定义值时,请确保使用相同的索引实体在缓存之间没有冲突。

索引启动模式

当 Data Grid 启动时,它可以执行操作以确保索引与缓存中的数据一致。默认情况下,缓存启动时不会进行索引操作,但您可以将 Data Grid 配置为:

  • 缓存启动时清除索引。

    • Data Grid 同步执行清晰(purge)操作。缓存只有在清除完成后才可用。
  • 当缓存启动时重新索引缓存。

    • Data Grid 异步执行 reindex 操作。根据缓存的大小,重新索引操作可能需要更长的时间来完成。
  • 自动清除或重新索引缓存。

    • 如果数据是易失性,并且索引是持久的,则数据网格会在启动时清除缓存。
    • 如果数据持久,并且索引是易失性,则 Data Grid 会在其启动时重新索引缓存。

缓存启动时清除索引

<distributed-cache>
  <indexing storage="filesystem" startup-mode="purge">
    <!-- Additional indexing configuration goes here. -->
  </indexing>
</distributed-cache>

当缓存启动时重建索引

<distributed-cache>
  <indexing storage="local-heap" startup-mode="reindex">
    <!-- Additional indexing configuration goes here. -->
  </indexing>
</distributed-cache>

索引模式

indexing-mode 控制如何将缓存操作传播到索引中。

auto
Data Grid 会立即对缓存应用任何更改到索引中。这是默认的模式。
manual
只有在显式调用 reindex 操作时,Data Grid 更新索引。配置 手动模式,例如,当您要对索引执行批处理更新时。

indexing-mode 设置为 manual

<distributed-cache>
  <indexing indexing-mode="manual">
    <!-- Additional indexing configuration goes here. -->
  </indexing>
</distributed-cache>
索引读取器

索引读取器是一个内部组件,提供对索引的访问来执行查询。随着索引内容的变化,Data Grid 需要刷新读取器,以便搜索结果最新。您可以为索引读取器配置刷新间隔。默认情况下,Data Grid 会在索引自上次刷新后更改索引前读取索引。

<distributed-cache>
  <indexing storage="filesystem" path="${java.io.tmpdir}/baseDir">
    <!-- Sets an interval of one second for the index reader. -->
    <index-reader refresh-interval="1000"/>
    <!-- Additional indexing configuration goes here. -->
  </indexing>
</distributed-cache>
索引写器

索引写器是一个内部组件,它构造由一个或多个片段(sub-indexes)组成的索引,可随着时间的推移合并以提高性能。较少的片段通常意味着查询期间的开销较少,因为索引读取器操作需要考虑所有片段。

Data Grid 在内部使用 Apache Lucene,并在两个层(内存和存储)中索引条目。新条目会首先进入内存索引,然后在出现 flush 时,到配置的索引存储。发生定期提交操作,从之前清空的数据中创建片段,并将所有索引更改永久更改。

注意

index-writer 配置是可选的。默认值应该只适用于大多数情况,自定义配置应该只用于调优性能。

<distributed-cache>
  <indexing storage="filesystem" path="${java.io.tmpdir}/baseDir">
    <index-writer commit-interval="2000"
                  low-level-trace="false"
                  max-buffered-entries="32"
                  queue-count="1"
                  queue-size="10000"
                  ram-buffer-size="400"
                  thread-pool-size="2">
      <index-merge calibrate-by-deletes="true"
                   factor="3"
                   max-entries="2000"
                   min-size="10"
                   max-size="20"/>
    </index-writer>
    <!-- Additional indexing configuration goes here. -->
  </indexing>
</distributed-cache>
表 1.1. 索引写器配置属性
属性描述

commit-interval

以毫秒为单位,索引更改会清除到索引存储,并且执行提交的时间(以毫秒为单位)。因为操作非常昂贵,因此应该避免小的值。默认值为 1000 ms (1 秒)。

max-buffered-entries

在将缓冲的内存中刷新到索引存储前可以缓冲的最大条目数。较大的值会导致索引速度更快,但使用更多内存。当与 ram-buffer-size 属性结合使用时,会首先发生对事件进行刷新。

ram-buffer-size

在清除索引存储前,可用于缓冲添加的条目和删除的最大内存量。较大的值会导致索引速度更快,但使用更多内存。为了加快索引性能,您应该设置此属性而不是 max-buffered-entries。当与 max-buffered-entries 属性结合使用时,会在首先发生事件时清空。

thread-pool-size

由于 Infinispan 15.0,因此会忽略此配置。索引引擎现在使用 Infinispan 线程池。

queue-count

默认 4。用于每个索引类型的内部队列数量。每个队列包含应用于索引和队列的修改批量处理。增加队列数量将导致增加索引吞吐量,但只有在瓶颈是 CPU 时。

queue-size

默认 4000。每个队列可以容纳的最大元素数。增加 queue-size 值会增加索引操作期间使用的内存量。设置太小的值可能会导致 CacheBackpressureFullExceptionRejectedExecutionExceptionOperationSubmitter,因为索引操作请求永远不会被阻断。在这种情况下,要解决这个问题,请增加 queue-size 或将 queue-count 设置为 1。

low-level-trace

为索引操作启用低级追踪信息。启用此属性可显著提高性能。您应该只使用此低级追踪作为故障排除的最后资源。

要配置数据网格如何合并索引片段,您可以使用 index-merge 子元素。

表 1.2. 索引合并配置属性
属性描述

max-entries

索引片段在合并前可以具有的最大条目数。有超过这个条目数量的片段不会被合并。较小的值在频繁更改索引时执行更好,如果索引没有经常更改,较大的值可以提供更好的搜索性能。

factor

一次合并的片段数量。使用较小的值时,合并的频率会更频繁地使用更多资源,但片段总数平均会降低,从而提高了搜索性能。较大的值(超过 10)最适合编写大量情况。

min-size

后台合并的最小目标大小(以 MB 为单位)。小于这个大小的片段会更积极地合并。设置太大的值可能会导致昂贵的合并操作,即使它们比较频繁。

max-size

后台合并的最大片段大小(以 MB 为单位)。大于这个大小的片段永远不会在后台合并。把它设置为较低值有助于降低内存要求,并避免在最佳搜索速度下一些合并操作。当强制合并索引和 max-forced-size 应用时,会忽略此属性。

max-forced-size

强制合并并覆盖 max-size 属性的最大片段大小(以 MB 为单位)。把它设置为与 max-size 或 lower 的值相同。但是,设置值太低的降级搜索性能,因为文档已被删除。

calibrate-by-deletes

在计算网段中的条目时,是否应该考虑索引中已删除条目的数量。设置 false 将导致被 max-entries 导致的更频繁的合并,但会更积极地将片段与删除的文档合并,从而提高查询性能。

索引分片

当您有大量数据时,您可以将 Data Grid 配置为将索引数据分成多个名为分片的索引。在分片中启用数据分布可提高性能。默认情况下禁用分片。

使用 shards 属性配置索引数量。分片数量必须大于 1

<distributed-cache>
  <indexing>
    <index-sharding shards="6" />
  </indexing>
</distributed-cache>

1.2. Data Grid 原生索引注解

当您在缓存中启用索引时,您可以将 Data Grid 配置为创建索引。您还需要为 Data Grid 提供缓存中实体的结构化表示,以便它实际上可以索引它们。

Data Grid 索引注解概述
@Indexed
表示 Data Grid 索引的实体或 Protobuf 消息类型。

指明 Data Grid 索引使用索引注解的字段。您可以采用与嵌入式和远程查询相同的方法使用这些注解。

@Basic
支持任何类型的字段。将 @Basic 注释用于不需要任何转换或处理的数字和简短字符串。
@Decimal
将此注解用于代表十进制值的字段。
@Keyword
将此注解用于字符串和用于完全匹配的字段。关键字字段在索引过程中不会被分析或令牌。
@Text
将此注解用于包含文本数据的字段,并用于全文本搜索功能。您可以使用分析器来处理文本并生成单个令牌。
@Embedded
使用此注解将字段标记为父实体中的嵌入式对象。NESTED 结构保留了原始对象关系结构,而 FLATTENED 结构使 leaf 字段对父实体有多值。@Embedded 使用的默认结构是 NESTED

每个注解都支持一组属性,可用于进一步描述实体的索引方式。

表 1.3. Data Grid 注解和支持的属性
注解支持的属性

@Basic

searchable, sortable, projectable, aggregable, indexNullAs

@Decimal

searchable, sortable, projectable, aggregable, indexNullAs, decimalScale

@Keyword

searchable, sortable, projectable, aggregable, indexNullAs, normalizer, norms

@Text

Searchable, projectable, norms, analyzer, searchAnalyzer

使用 Data Grid 注解

您可以通过两种方式为 Data Grid 提供索引注解:

  • 使用 Data Grid 注解直接标注您的 Java 类或字段。
    然后,您将生成或更新您的 Protobuf 模式 .proto 文件,然后将其上传到 Data Grid Server。
  • 直接使用 @Indexed@Basic@Keyword@Text 直接标注 Protobuf 模式。
    然后,您将 Protobuf 模式上传到 Data Grid Server。

    例如,以下架构使用 @Text 注释:

    /**
      * @Text(projectable = true)
      */
    required string street = 1;

1.3. 重建索引

从缓存中存储的数据重建索引。在更改索引类型或分析器定义等内容时,您应该重建索引。同样,您可以出于任何原因删除索引后重建索引。

重要

重建索引可能需要很长时间才能完成,因为这个过程需要完成网格中的所有数据。在重建操作进行时,查询可能会返回较少的结果。

流程

使用以下方法之一重建索引:

  • 调用 reindexCache () 方法,以编程方式从 Hot Rod Java 客户端重建索引:

    remoteCacheManager.administration().reindexCache("MyCache");
    提示

    对于远程缓存,您还可以从 Data Grid 控制台重建索引。

  • 调用 index.run () 方法为嵌入式缓存重建索引,如下所示:

    Indexer indexer = Search.getIndexer(cache);
    CompletionStage<Void> future = index.run();
    • 使用索引统计的 reindexing 属性检查 reindexing 操作的状态。

1.4. 更新索引模式

更新索引模式操作可让您以最少的停机时间添加模式更改。Data Grid 不移除之前索引的数据并重新创建索引模式,而是为现有的架构添加新字段。更新索引模式比重建索引要快,但只有在您的更改不会影响已索引的字段时,才能更新模式。

重要

只有在更改不会影响之前索引的字段时,才能更新索引模式。当您更改 index 字段定义或删除字段时,您必须重建索引。

流程

  • 为给定缓存更新索引模式:

    • 调用 updateIndexSchema () 方法,以编程方式从 Hot Rod Java 客户端更新索引模式:

      remoteCacheManager.administration().updateIndexSchema("MyCache");
      提示

      对于远程缓存,您可以从 Data Grid 控制台或使用 REST API 更新索引模式。

其他资源

1.5. 非索引查询

Data Grid 建议索引缓存以获得最佳性能。但是,您可以查询非索引的缓存。

  • 对于嵌入式缓存,您可以在 Plain Old Java 对象(POJO)上执行非索引查询。
  • 对于远程缓存,您必须使用带有 application/x-protostream 介质类型的 ProtoStream 编码来执行非索引的查询。

第 2 章 创建 Ickle 查询

Data Grid 提供了一个 Ickle 查询语言,可让您创建关系和全文本查询。

2.1. Ickle 查询

要使用 API,请调用 cache .query () 方法并提供查询字符串。

例如:

// Remote Query using protobuf
Query<Transaction> q = remoteCache.query("from sample_bank_account.Transaction where amount > 20");

// Embedded Query using Java Objects
Query<Transaction> q = cache.query("from org.infinispan.sample.Book where price > 20");

// Execute the query
QueryResult<Book> queryResult = q.execute();
注意

查询将始终以单个实体类型为目标,并在单个缓存的内容上评估。不支持对多个缓存运行查询,或创建以多个实体类型(连接)为目标的查询。

执行查询并获取结果非常简单,就像调用 Query 对象的 execute () 方法一样简单。执行之后,对同一实例调用 execute () 将重新执行查询。

2.1.1. 分页

您可以使用 Query.maxResults (int maxResults) 限制返回的结果数量。这可与 Query.startOffset (long startOffset) 一起使用,来实现结果集的分页。

// sorted by year and match all books that have "clustering" in their title
// and return the third page of 10 results
Query<Book> query = cache.query("FROM org.infinispan.sample.Book WHERE title like '%clustering%' ORDER BY year").startOffset(20).maxResults(10)
注意

如果您没有为查询实例明确设置 maxResults,Data Grid 会将查询返回的结果数量限制为 100。您可以通过设置 query.default-max-results 缓存属性来更改默认限制。

2.1.2. 命中数

QueryResult 对象包含 .hitCount () 方法,它返回一个 hit count 值,代表查询的结果总数,而不考虑任何分页参数。

另外,QueryResult 对象包含 .isExact () 方法返回的布尔值,它指示 hit count 号是否准确还是低绑定。因为性能的原因,点击数仅适用于索引的查询。

2.1.2.1. 达到次数准确性

您可以通过设置 hit-count-accuracy 属性来限制所需的点击数的准确性。处理大型数据集时,精确点击数可能会影响性能。将限制设置为达到达到的准确性,可让您获得更快的查询响应,同时确保提供的点击数对应用程序的需求保持足够准确。

hit-count-accuracy 属性的默认准确性限制为 10000。这意味着,对于任何查询,Data Grid 提供最多 10,000 的点击数。如果有效点击数大于 10,000,则数据网格返回低限估算。您可以通过设置 query.hit-count-accuracy 缓存属性来更改默认限制。或者,也可以在每个查询实例上设置它。

当实际点击数超过 hit-count-accuracy 设置的限制时,.isExact () 方法或 hit_count_exact JSON 字段将为 false,表示返回的点击数是估算。将此值设置为 Integer。MAX 会为任何查询返回准确的结果,但这可能会严重影响查询性能。

为了获得最佳性能,则属性值比预期的点击数稍高。如果您不要求准确点击数,请将其设为低值。

2.1.3. 迭代

Query 对象具有 .iterator () 方法,可以完全获得结果。它返回一个在使用后必须关闭的 CloseableIterator 实例。

注意

远程查询的迭代支持当前有限,因为它在迭代前首先获取客户端的所有条目。

2.1.4. 命名查询参数

可以不必为每个执行构建一个新的 Query 对象,而是在查询中包含命名参数,这些参数可以在执行前使用实际值替换。这允许定义一次查询,并可以有效地执行多次。参数只能在操作器的右侧使用,并通过提供由 org.infinispan.query.dsl.Expression.param (String paramName) 方法生成的对象来创建查询时定义。定义参数后,可以通过调用 Query.setParameter (parameterName, value)Query.setParameters (parameterMap) 来设置参数,如以下示例所示。

// Defining a query to search for various authors and publication years
Query<Book> query = cache.query("SELECT title FROM org.infinispan.sample.Book WHERE author = :authorName AND publicationYear = :publicationYear").build();

// Set actual parameter values
query.setParameter("authorName", "Doe");
query.setParameter("publicationYear", 2010);

// Execute the query
List<Book> found = query.execute().list();

或者,您可以提供一个实际参数值映射以一次性设置多个参数: ⁠

一次设置多个命名参数

Map<String, Object> parameterMap = new HashMap<>();
parameterMap.put("authorName", "Doe");
parameterMap.put("publicationYear", 2010);

query.setParameters(parameterMap);

注意

在首次使用参数执行查询时,执行查询解析、验证和执行规划工作的主要部分。与使用恒定值而不是查询参数类似的查询相比,后续执行期间不会重复这一工作,从而提高性能。

2.1.5. 查询执行

Query API 提供了两种方法在缓存中执行 Ickle 查询:

  • query.execute () 运行 SELECT 语句并返回结果。
  • query.executeStatement () 运行 DELETE 语句并修改数据。
注意

您应该始终调用 executeStatement () 来修改数据并调用 execute () 来获取查询的结果。

2.2. Ickle 查询语言语法

Ickle 查询语言是 JPQL 查询语言的子集,具有一些全文本扩展。

解析器语法有一些值得注意的规则:

  • 空格并不重要。
  • 字段名称不支持通配符。
  • 必须始终指定字段名称或路径,因为没有默认字段。
  • &&|| 在全文本和 JPA predicates 中都接受 ANDOR
  • ! 可以被使用,而不是
  • 缺少布尔值运算符解释为 OR
  • 字符串术语必须用单引号或双引号括起。
  • Fuzziness 和 boosting 没有被任意顺序接受;fuzziness 始终是首先接受的。
  • != 被接受,而不是 <& gt;
  • boosting 无法应用到 >, &gt;=, &lt;,&lt;= operators。范围可用于实现相同的结果。

2.2.1. 过滤 Operator

Ickle 支持许多可用于索引和非索引字段的过滤运算符。

Operator描述Example

in

检查左侧运算对象是否等于所给值集合中的一个元素。

FROM Book WHERE isbn IN ('ZZ', 'X1234')

like

检查左侧参数(预期为 String)是否匹配 JPA 规则之后的通配符模式。

FROM Book WHERE 标题 LIKE '%Java%'

=

检查 left 参数是否与给定值完全匹配。

FROM Book WHERE name = 'Programming Java'

!=

检查 left 参数是否与给定值不同。

FROM Book WHERE 语言 != ' English'

>

检查 left 参数是否大于给定值。

FROM Book WHERE price > 20

>=

检查 left 参数是否大于或等于给定值。

FROM Book WHERE price >= 20

<

检查 left 参数是否小于给定值。

FROM Book WHERE year < 2020

<=

检查 left 参数是否小于或等于给定值。

FROM Book WHERE price 了 50

between

检查 left 参数是否在给定的范围限值之间。

FROM Book WHERE 价格 BETWEEN 50 AND 100

2.2.2. 布尔值条件

以下示例中演示了多个属性条件和逻辑组合()和 disjunction ()运算符,以创建更复杂的条件。布尔值运算符的已知运算符优先级规则适用于此处,因此操作器的顺序无关。这里 运算符的优先级比 高,即使先调用

# match all books that have "Data Grid" in their title
# or have an author named "Manik" and their description contains "clustering"

FROM org.infinispan.sample.Book WHERE title LIKE '%Data Grid%' OR author.name = 'Manik' AND description like '%clustering%'

布尔值负值在逻辑运算符之间具有最高优先级,并且只适用于下一个简单的属性条件。

# match all books that do not have "Data Grid" in their title and are authored by "Manik"
FROM org.infinispan.sample.Book WHERE title != 'Data Grid' AND author.name = 'Manik'

2.2.3. 嵌套条件

通过括号更改逻辑运算符的优先级:

# match all books that have an author named "Manik" and their title contains
# "Data Grid" or their description contains "clustering"
FROM org.infinispan.sample.Book WHERE author.name = 'Manik' AND ( title like '%Data Grid%' OR description like '% clustering%')

2.2.4. SELECT 语句的预测

在某些用例中,如果应用实际使用了一小部分属性,则返回整个域对象是过量的,特别是在域实体有嵌入式实体时。查询语言允许您指定属性(或属性路径)的子集来返回 - 投射。如果使用投射,则 QueryResult.list () 不会返回整个域实体,而是返回 Object[] 列表,则数组中的每个插槽都与投射属性对应。

# match all books that have "Data Grid" in their title or description
# and return only their title and publication year
SELECT title, publicationYear FROM org.infinispan.sample.Book WHERE title like '%Data Grid%' OR description like '%Data Grid%'
2.2.4.1. 项目缓存条目版本

可以使用版本投射功能来项目缓存条目 版本

# return the title, publication year and the cache entry version
SELECT b.title, b.publicationYear, version(b) FROM org.infinispan.sample.Book b WHERE b.title like '%Data Grid%'
2.2.4.2. 项目缓存条目值

可以将 cache 条目值与其他项目一起项目。它可用于将缓存条目值与同一 Object[] 返回命中中的缓存条目版本一起项目。

# return the cache entry value and the cache entry version
SELECT b, version(b) FROM org.infinispan.sample.Book b WHERE b.title like '%Data Grid%'
2.2.4.3. 分数项目

如果查询被索引,则可以将每个匹配的分数与其他投射。它可用于将缓存条目值与同一 Object[] 返回点击中的分数一起项目。

# return the cache entry value and the the score of the matching
SELECT b, score(b) FROM org.infinispan.sample.Book b WHERE b.title like '%Data Grid%'
排序

使用 ORDER BY 子句,根据一个或多个属性或属性路径对结果进行排序。如果指定了多个排序条件,则顺序将指定其优先级。

# match all books that have "Data Grid" in their title or description
# and return them sorted by the publication year and title
FROM org.infinispan.sample.Book WHERE title like '%Data Grid%' ORDER BY publicationYear DESC, title ASC

2.2.5. 分组和聚合

Data Grid 能够根据一组分组字段并构造来自每个组的结果聚合来对查询结果进行分组,方法是将聚合应用到每个组中的值集合。分组和聚合只能应用到投射查询(在 SELECT 子句中带有一个或多个字段)。

支持的聚合有: avgsumcountmaxmin

组分组字段通过 GROUP BY 子句指定,并且用于定义分组字段的顺序无关。投射中选择的所有字段都必须分组字段,否则必须使用下面描述的分组功能之一来聚合它们。项目字段可以聚合,并同时用于分组。选择仅分组字段但没有聚合字段的查询是法律的。附录示例:作者对手册进行分组,并计算它们。

SELECT author, COUNT(title) FROM org.infinispan.sample.Book WHERE title LIKE '%engine%' GROUP BY author
注意

一个投射查询,所有选择的字段都应用了聚合功能,且无法用于分组的字段。在这种情况下,聚合将全局计算,就像有一个全局组一样。

聚合

您可以将以下聚合功能应用到一个字段:

表 2.1. 索引合并属性
聚合功能描述

avg()

计算一组数字的平均数量。接受的值是原始数字和 java.lang.Number 的实例。结果以 java.lang.Double 表示。如果没有非 null 值,则结果为 null

count()

计算非null 行的数量并返回 java.lang.Long。如果没有非 null 值,则 结果为 0。

max()

返回找到的最大价值。接受的值必须是 java.lang.Comparable 的实例。如果没有非 null 值,则结果为 null

min()

返回找到的最小值。接受的值必须是 java.lang.Comparable 的实例。如果没有非 null 值,则结果为 null

sum()

计算一组数字的总和。如果没有非 null 值,则结果为 null。下表根据指定字段显示返回类型。

表 2.2. 表和返回类型
字段类型返回类型

不可或缺(除 BigInteger)

Long

float 或 Double

�

BigInteger

BigInteger

BigDecimal

BigDecimal

使用分组和聚合评估查询

聚合查询可以包含过滤条件,如常规查询。可以在两个阶段执行过滤:在分组操作之前和之后执行。在执行分组操作之前,定义的所有过滤器条件都将应用到缓存条目(而不是最终投射)。这些过滤器条件可以引用查询的实体类型的任何字段,旨在限制要作为分组阶段输入的数据集。调用 groupBy () 方法后定义的所有过滤器条件将应用到投射结果和分组操作。这些过滤器条件可以引用任何 groupBy () 字段或聚合的字段。允许引用在 select 子句中指定的聚合字段,但禁止引用非aggregated 和 non-grouping 字段。在此阶段过滤将根据其属性减少组量。排序也可以指定,类似于常见的查询。排序操作在分组操作后执行,并可引用任何 groupBy () 字段或聚合字段。

2.2.6. DELETE 语句

您可以使用以下语法从 Data Grid 缓存中删除实体:

DELETE FROM <entityName> [WHERE condition]
  • 使用 < entityName&gt; 引用单个实体。DELETE 查询无法使用 join。
  • WHERE 条件是可选的。

DELETE 查询无法使用以下任一方法:

  • SELECT 语句的预测
  • 分组和聚合
  • ORDER BY 子句
提示

调用 Query.executeStatement () 方法来执行 DELETE 语句。

2.3. 全文本查询

您可以使用 Ickle 查询语言执行全文本搜索。

2.3.1. fuzzy 查询

要执行模糊查询,请添加 ~ 和整数,代表术语后面的术语的距离。例如

FROM sample_bank_account.Transaction WHERE description : 'cofee'~2

2.3.2. 范围查询

要执行范围查询,请在大括号内定义给定边界,如下例所示:

FROM sample_bank_account.Transaction WHERE amount : [20 to 50]

2.3.3. 短语查询

可以通过用引号括起来来搜索一组词语,如下例所示:

FROM sample_bank_account.Transaction WHERE description : 'bus fare'

2.3.4. 代理查询

要执行相似查询,请在特定距离中查找两个术语,请在短语后添加一个 ~ 和距离。例如,以下示例将查找取消和费用的词语,只要它们不多于 3 个词语:

FROM sample_bank_account.Transaction WHERE description : 'canceling fee'~3

2.3.5. 通配符查询

要搜索"文本"或"test",请使用 ? 单字符通配符搜索:

FROM sample_bank_account.Transaction where description : 'te?t'

要搜索 "test", "tests" 或 "tester",请使用 * 多字符通配符搜索:

FROM sample_bank_account.Transaction where description : 'test*'

2.3.6. 正则表达式查询

通过在 / 之间指定模式,可以执行正则表达式查询。Ickle 使用 Lucene 的正则表达式语法,因此可以搜索单词 moatboat

FROM sample_library.Book  where title : /[mb]oat/

2.3.7. 改进查询

通过在术语后面添加 ^ 来提高给定查询的相关性,可以提高术语的提升因素越高。例如,要搜索包含 beer 和 wine 较高的相关标题(3 倍),可以使用以下内容:

FROM sample_library.Book WHERE title : beer^3 OR wine

第 3 章 查询远程缓存

您可以在 Data Grid 服务器上索引和查询远程缓存。

3.1. 从 Hot Rod Java 客户端查询缓存

通过数据网格,您可以以编程方式通过 Hot Rod 端点从 Java 客户端查询远程缓存。此流程解释了如何索引查询存储 Book 实例的远程缓存。

先决条件

  • 将 ProtoStream 处理器添加到 pom.xml 中。

Data Grid 为 @ProtoField 注释提供此处理器,以便您可以生成 Protobuf 模式并执行查询。

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>...</version>
      <configuration>
        <annotationProcessorPaths>
          <annotationProcessorPath>
            <groupId>org.infinispan.protostream</groupId>
            <artifactId>protostream-processor</artifactId>
            <version>...</version>
          </annotationProcessorPath>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
  </plugins>
</build>

流程

  1. 在类中添加索引注解,如下例所示:

    Book.java

    import org.infinispan.api.annotations.indexing.Basic;
    import org.infinispan.api.annotations.indexing.Indexed;
    import org.infinispan.api.annotations.indexing.Text;
    import org.infinispan.protostream.annotations.ProtoFactory;
    import org.infinispan.protostream.annotations.ProtoField;
    
    @Indexed
    public class Book {
    
       @Text
       @ProtoField(number = 1)
       final String title;
    
       @Text
       @ProtoField(number = 2)
       final String description;
    
       @Basic
       @ProtoField(number = 3, defaultValue = "0")
       final int publicationYear;
    
       @ProtoFactory
       Book(String title, String description, int publicationYear) {
          this.title = title;
          this.description = description;
          this.publicationYear = publicationYear;
       }
       // public Getter methods omitted for brevity
    }

  2. 在新类中实施 SerializationContextInitializer 接口,然后添加 @ProtoSchema 注释。

    1. 使用 includeClasses 参数引用包含 @ProtoField 注解的类。
    2. 定义 Protobuf 模式的名称,该模式使用 schemaFileNameschemaFilePath 参数生成文件系统路径。
    3. 使用 schemaPackageName 参数指定 Protobuf 模式的软件包名称。

      RemoteQueryInitializer.java

      import org.infinispan.protostream.SerializationContextInitializer;
      import org.infinispan.protostream.annotations.ProtoSchema;
      
      @ProtoSchema(
            includeClasses = {
                  Book.class
            },
            schemaFileName = "book.proto",
            schemaFilePath = "proto/",
            schemaPackageName = "book_sample")
      public interface RemoteQueryInitializer extends SerializationContextInitializer {
      }

  3. 编译您的项目。

    此流程中的代码示例生成 proto/book.proto 模式,以及 annotated Book 类的 RemoteQueryInitializerImpl.java 实现。

后续步骤

创建一个远程缓存,将 Data Grid 配置为索引您的实体。例如,以下远程缓存会在您在上一步中生成的 书.proto 模式中索引了 Book 实体:

<replicated-cache name="books">
  <indexing>
    <indexed-entities>
      <indexed-entity>book_sample.Book</indexed-entity>
    </indexed-entities>
  </indexing>
</replicated-cache>

以下 RemoteQuery 类执行以下操作:

  • 使用 Hot Rod Java 客户端注册 RemoteQueryInitializerImpl 序列化上下文。
  • 将 Protobuf 模式( 书.proto )注册到 Data Grid Server。
  • 将两个 Book 实例添加到远程缓存中。
  • 根据标题中的关键字,执行与书书匹配的全文本查询。

RemoteQuery.java

package org.infinispan;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.Search;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.query.dsl.Query;
import org.infinispan.query.dsl.QueryFactory;
import org.infinispan.query.remote.client.ProtobufMetadataManagerConstants;

public class RemoteQuery {

   public static void main(String[] args) throws Exception {
      ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
      // RemoteQueryInitializerImpl is generated
      clientBuilder.addServer().host("127.0.0.1").port(11222)
            .security().authentication().username("user").password("user")
            .addContextInitializers(new RemoteQueryInitializerImpl());

      RemoteCacheManager remoteCacheManager = new RemoteCacheManager(clientBuilder.build());

      // Grab the generated protobuf schema and registers in the server.
      Path proto = Paths.get(RemoteQuery.class.getClassLoader()
            .getResource("proto/book.proto").toURI());
      String protoBufCacheName = ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME;
      remoteCacheManager.getCache(protoBufCacheName).put("book.proto", Files.readString(proto));

      // Obtain the 'books' remote cache
      RemoteCache<Object, Object> remoteCache = remoteCacheManager.getCache("books");

      // Add some Books
      Book book1 = new Book("Infinispan in Action", "Learn Infinispan with using it", 2015);
      Book book2 = new Book("Cloud-Native Applications with Java and Quarkus", "Build robust and reliable cloud applications", 2019);

      remoteCache.put(1, book1);
      remoteCache.put(2, book2);

      // Execute a full-text query
      Query<Book> query = remoteCache.query("FROM book_sample.Book WHERE title:'java'");

      List<Book> list = query.execute().list(); // Voila! We have our book back from the cache!
   }
}

3.2. 查询 ProtoStream 通用类型

对存储数据为 ProtoStream 通用类型的缓存执行 Ickle 查询,如 BigIntegerBigDecimal

流程

  1. 在类中添加索引注解,如下例所示:

    @Indexed
    public class CalculusIndexed {
        @Basic
        @ProtoField(value = 1)
        public BigInteger getPurchases() {
          return purchases;
        }
    
        @Decimal // the scale is 2 by default
        @ProtoField(value = 2)
        public BigDecimal getProspect() {
          return prospect;
        }
    }
  2. dependentOn 属性设置为 CommonTypes.class,以指示生成的 Protobuf 模式可以引用和使用 CommonTypes 类型,如 BigIntegerBigDecimal

    @ProtoSchema(includeClasses = CalculusIndexed.class, dependsOn = CommonTypes.class,
         schemaFilePath = "/protostream", schemaFileName = "calculus-indexed.proto",
         schemaPackageName = "lab.indexed")
    public interface CalculusIndexedSchema extends GeneratedSchema {
    }
  3. 执行查询:

    Query<Product> query = cache.query("from lab.indexed.CalculusIndexed c where c.purchases > 9");
    QueryResult<Product> result = query.execute();
    // play with the result
    
    query = cache.query("from lab.indexed.CalculusIndexed c where c.prospect = 2.2");
    result = query.execute();
    // play with the result

3.3. 从 Data Grid 控制台和 CLI 查询缓存

Data Grid Console 和 Data Grid Command Line Interface (CLI)可让您查询索引和非索引的远程缓存。您还可以使用任何 HTTP 客户端通过 REST API 索引和查询缓存。

此流程解释了如何索引和查询存储 Person 实例的远程缓存。

先决条件

  • 至少有一个正在运行的 Data Grid 服务器实例。
  • 具有具有创建权限的 Data Grid 凭据。

流程

  1. 在 Protobuf 模式中添加索引注解,如下例所示:

    package org.infinispan.example;
    
    /* @Indexed */
    message Person {
    
        /* @Basic */
        optional int32 id = 1;
    
        /* @Keyword(projectable = true) */
        required string name = 2;
    
        /* @Keyword(projectable = true) */
        required string surname = 3;
    
        /* @Basic(projectable = true, sortable = true) */
        optional int32 age = 6;
    
    }

    在 Data Grid CLI 中,使用带有 --upload= 参数的 schema 命令,如下所示:

    schema --upload=person.proto person.proto
  2. 创建名为 people 的缓存,它使用 ProtoStream 编码并将 Data Grid 配置为索引您的 Protobuf 模式中声明的实体。

    以下缓存索引上一步中的 Person 实体:

    <distributed-cache name="people">
      <encoding media-type="application/x-protostream"/>
      <indexing>
        <indexed-entities>
          <indexed-entity>org.infinispan.example.Person</indexed-entity>
        </indexed-entities>
      </indexing>
    </distributed-cache>

    在 CLI 中,使用带有 --file= 参数的 create cache 命令,如下所示:

    create cache --file=people.xml people
  3. 向缓存中添加条目。

    要查询远程缓存,需要包含一些数据。在本例中,创建使用以下 JSON 值的条目:

    PersonOne

    {
      "_type":"org.infinispan.example.Person",
      "id":1,
      "name":"Person",
      "surname":"One",
      "age":44
    }

    PersonTwo

    {
      "_type":"org.infinispan.example.Person",
      "id":2,
      "name":"Person",
      "surname":"Two",
      "age":27
    }

    PersonThree

    {
      "_type":"org.infinispan.example.Person",
      "id":3,
      "name":"Person",
      "surname":"Three",
      "age":35
    }

    在 CLI 中,使用带有 --file= 参数的 put 命令来添加每个条目,如下所示:

    put --encoding=application/json --file=personone.json personone
    提示

    在 Data Grid Console 中,当使用自定义类型添加 JSON 格式的值时,您必须为 Value 内容类型 字段选择 Custom Type

  4. 查询您的远程缓存。

    在 CLI 中,使用远程缓存上下文中的 query 命令。

    query "from org.infinispan.example.Person p WHERE p.name='Person' ORDER BY p.age ASC"

    查询返回所有名称与 Person by age 匹配的条目(以升序表示)。

其他资源

3.4. 使用带有远程缓存的 analyzers

分析器将输入数据转换为您可以索引和查询的术语。您可以在 Java 类中使用 @Text 注释指定分析器定义,或者在 Protobuf schema 中直接指定分析器定义。

流程

  1. 使用 @Text 注释给属性添加注释,以指示其值已被分析。
  2. 使用 分析器 属性指定您要用于索引和搜索所需的分析器。

protobuf 模式

/* @Indexed */
message TestEntity {

    /* @Keyword(projectable = true) */
    optional string id = 1;

    /* @Text(projectable = true, analyzer = "simple") */
    optional string name = 2;
}

Java 类

@Text(projectable = true, analyzer = "whitespace")
@ProtoField(value = 1)
private String id;

@Text(projectable = true, analyzer = "simple")
@ProtoField(value = 2)
private String description;

3.4.1. 默认分析器定义

Data Grid 提供了一组默认的分析器定义。

定义描述

standard

将文本字段拆分为令牌,将空格和标点分隔为分隔符。

simple

通过取消限制非字母的令牌化输入流,然后将所有字母转换为小写字符。空格和非字母将被丢弃。

空格

分割空格上的文本流,并将非空格字符序列返回为令牌。

关键字

将整个文本字段视为单一令牌。

stemmer

使用 Snowball Porter 过滤器窃取英语词.

ngram

默认情况下,生成大小为 3 分的 ngram 令牌。

filename

将文本字段分成比 标准 分析器更大的令牌,将空格视为分隔符,并将所有字母转换为小写字符。

小写

将文本的所有字母转换为小写字符,文本不会被令牌化(规范化程序)。

这些分析器定义基于 Apache Lucene。有关令牌工具、过滤器和 CharFilters 的更多信息,请参阅 Apache Lucene 文档。

其他资源

3.4.2. 创建自定义分析器定义

创建自定义分析器定义,并将它们添加到您的 Data Grid Server 安装中。

先决条件

  • 如果 Data Grid Server 正在运行,则停止它。

    Data Grid Server 仅在启动时加载类。

流程

  1. 实施 ProgrammaticSearchMappingProvider API。
  2. 在 JAR 中打包您的实现,在以下文件中带有完全限定类(FQN):

    META-INF/services/org.infinispan.query.spi.ProgrammaticSearchMappingProvider
  3. 将 JAR 文件复制到 Data Grid Server 安装的 server/lib 目录中。
  4. 启动 Data Grid Server。

ProgrammaticSearchMappingProvider 示例

import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
import org.apache.lucene.analysis.core.StopFilterFactory;
import org.apache.lucene.analysis.standard.StandardFilterFactory;
import org.apache.lucene.analysis.standard.StandardTokenizerFactory;
import org.hibernate.search.cfg.SearchMapping;
import org.infinispan.Cache;
import org.infinispan.query.spi.ProgrammaticSearchMappingProvider;

public final class MyAnalyzerProvider implements ProgrammaticSearchMappingProvider {

   @Override
   public void defineMappings(Cache cache, SearchMapping searchMapping) {
      searchMapping
            .analyzerDef("standard-with-stop", StandardTokenizerFactory.class)
               .filter(StandardFilterFactory.class)
               .filter(LowerCaseFilterFactory.class)
               .filter(StopFilterFactory.class);
   }
}

3.5. 按键查询

您可以将缓存条目的键定义为 索引 类型,以索引键字段以及允许 Ickle 查询中使用的键的值字段。

要定义 Indexed 键,请指定 ProtocolBuffer 消息类型的完全限定名称,以用作 @Indexed 注释的 keyEntity 属性中的 key 类型。

注意

此功能仅适用于索引的远程查询。

指定索引实体的 keyEntity

import org.infinispan.api.annotations.indexing.Basic;
import org.infinispan.api.annotations.indexing.Indexed;
import org.infinispan.api.annotations.indexing.Text;
import org.infinispan.protostream.GeneratedSchema;
import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;
import org.infinispan.protostream.annotations.ProtoSchema;

@Indexed(keyEntity = "model.StructureKey")
public class Structure {

   private final String code;
   private final String description;
   private final Integer value;

   @ProtoFactory
   public Structure(String code, String description, Integer value) {
      this.code = code;
      this.description = description;
      this.value = value;
   }

   @ProtoField(1)
   @Basic
   public String getCode() {
      return code;
   }

   @ProtoField(2)
   @Text
   public String getDescription() {
      return description;
   }

   @ProtoField(3)
   @Basic
   public Integer getValue() {
      return value;
   }

   @ProtoSchema(includeClasses = { Structure.class, StructureKey.class }, schemaPackageName = "model")
   public interface StructureSchema extends GeneratedSchema {
      StructureSchema INSTANCE = new StructureSchemaImpl();
   }
}

定义密钥实体及其索引字段

import org.infinispan.api.annotations.indexing.Basic;
import org.infinispan.api.annotations.indexing.Indexed;
import org.infinispan.api.annotations.indexing.Keyword;
import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;

@Indexed
public class StructureKey {

   private String zone;
   private Integer row;
   private Integer column;

   @ProtoFactory
   public StructureKey(String zone, Integer row, Integer column) {
      this.zone = zone;
      this.row = row;
      this.column = column;
   }

   @Keyword(projectable = true, sortable = true)
   @ProtoField(1)
   public String getZone() {
      return zone;
   }

   @Basic(projectable = true, sortable = true)
   @ProtoField(2)
   public Integer getRow() {
      return row;
   }

   @Basic(projectable = true, sortable = true)
   @ProtoField(3)
   public Integer getColumn() {
      return column;
   }
}

3.5.1. 键属性名称

默认情况下,使用名为 key 的属性将作为目标。

在 Ickle 查询中使用关键属性

select s.key.column from model.Structure s where s.key.zone = 'z7'

如果值已经具有名为 key 的属性,则密钥实体的定义可能会创建与属性的命名冲突。因此,通常也可以更改名称,将其分配为属性键的前缀,更改 @Indexed 注释的属性 keyPropertyName

3.5.2. 键包括深度

entity 键可以带有嵌入式实体。您可以通过更改属性 keyIncludeDepth (默认为 3 )来限制被索引的嵌入式实体字段的深度。

第 4 章 查询嵌入式缓存

当您将 Data Grid 作为自定义应用程序的库添加时,使用嵌入式查询。

使用嵌入式查询不需要 protobuf 映射。索引和查询都在 Java 对象之上完成。

4.1. 查询嵌入式缓存

本节介绍如何使用名为"books"的示例缓存来查询嵌入式缓存,该缓存存储索引的 Book 实例。

在本例中,每个 Book 实例定义了索引哪些属性,并使用 Hibernate Search 注解指定一些高级索引选项,如下所示:

Book.java

package org.infinispan.sample;

import java.time.LocalDate;
import java.util.HashSet;
import java.util.Set;

import org.infinispan.api.annotations.indexing.*;

// Annotate values with @Indexed to add them to indexes
// Annotate each field according to how you want to index it
@Indexed
public class Book {
   @Keyword
   String title;

   @Text
   String description;

   @Keyword
   String isbn;

   @Basic
   LocalDate publicationDate;

   @Embedded
   Set<Author> authors = new HashSet<Author>();
}

Author.java

package org.infinispan.sample;

import org.infinispan.api.annotations.indexing.Text;

public class Author {
   @Text
   String name;

   @Text
   String surname;
}

流程

  1. 配置 Data Grid 以索引"books"缓存,并将 org.infinispan.sample.Book 指定为要索引的实体。

    <distributed-cache name="books">
      <indexing path="${user.home}/index">
        <indexed-entities>
          <indexed-entity>org.infinispan.sample.Book</indexed-entity>
        </indexed-entities>
      </indexing>
    </distributed-cache>
  2. 获取缓存。

    import org.infinispan.Cache;
    import org.infinispan.manager.DefaultCacheManager;
    import org.infinispan.manager.EmbeddedCacheManager;
    
    EmbeddedCacheManager manager = new DefaultCacheManager("infinispan.xml");
    Cache<String, Book> cache = manager.getCache("books");
  3. 对存储在 Data Grid 缓存中的 Book 实例中的字段执行查询,如下例所示:

    // Create an Ickle query that performs a full-text search using the ':' operator on the 'title' and 'authors.name' fields
    // You can perform full-text search only on indexed caches
    Query<Book> fullTextQuery = cache.query("FROM org.infinispan.sample.Book b WHERE b.title:'infinispan' AND b.authors.name:'sanne'");
    
    // Use the '=' operator to query fields in caches that are indexed or not
    // Non full-text operators apply only to fields that are not analyzed
    Query<Book> exactMatchQuery= cache.query("FROM org.infinispan.sample.Book b WHERE b.isbn = '12345678' AND b.authors.name : 'sanne'");
    
    // You can use full-text and non-full text operators in the same query
    Query<Book> query= cache.query("FROM org.infinispan.sample.Book b where b.authors.name : 'Stephen' and b.description : (+'dark' -'tower')");
    
    // Get the results
    List<Book> found=query.execute().list();

4.2. 实体映射注解

在您的 Java 类中添加注释,将实体映射到索引。

Hibernate Search API

Data Grid 使用 Hibernate Search API 为实体级别索引定义精细的配置。此配置包括注释哪些字段,它们应使用分析器、如何映射嵌套对象等。

以下小节提供了适用于 Data Grid 的实体映射注解的信息。

有关这些注解的完整详情,您应该参考 Hibernate Search manual

@DocumentId

与 Hibernate Search 不同,使用 @DocumentId 将字段标记为标识符不适用于 Data Grid 值;在 Data Grid 中,所有 @Indexed 对象的标识符是用于存储值的密钥。您仍然可以使用 @Transformable、自定义类型和自定义 FieldBridge 实施的组合来自定义键的索引方式。

@Transformable 键

每个值的密钥也需要索引,必须在 String 中转换密钥实例。Data Grid 包括一些默认的转换例程来编码常见的原语,但要使用一个自定义密钥,您必须提供一个 org.infinispan.query.Transformer 的实现。

通过注解注册密钥转换程序

您可以使用 org.infinispan.query.Transformable 标注密钥类,您的自定义转换器实施将自动获取:

@Transformable(transformer = CustomTransformer.class)
public class CustomKey {
   ...
}

public class CustomTransformer implements Transformer {
   @Override
   public Object fromString(String s) {
      ...
      return new CustomKey(...);
   }

   @Override
   public String toString(Object customType) {
      CustomKey ck = (CustomKey) customType;
      return ...
   }
}

通过缓存索引配置注册密钥转换程序

在嵌入式和服务器配置中使用 key-transformers xml 元素:

<replicated-cache name="test">
  <indexing>
    <key-transformers>
      <key-transformer key="com.mycompany.CustomKey"
                       transformer="com.mycompany.CustomTransformer"/>
    </key-transformers>
  </indexing>
</replicated-cache>

或者,使用 Java 配置 API (embedded 模式):

   ConfigurationBuilder builder = ...
   builder.indexing().enable()
         .addKeyTransformer(CustomKey.class, CustomTransformer.class);

第 5 章 创建持续查询

应用程序可以注册监听程序来接收有关与查询过滤器匹配的缓存条目的持续更新。

5.1. 持续查询

持续查询为应用程序提供关于通过查询过滤的 Data Grid 缓存中数据的实时通知。当条目与查询 Data Grid 匹配时,会将更新的数据发送到任何监听程序,它提供事件流,而不是必须执行查询的应用程序。

持续查询可以通知应用程序有关传入匹配项、加入集合的值;更新匹配,以获得已修改并继续匹配项的匹配值;以及传出的匹配,对于离开该集合的值。

例如,持续查询可以通知应用程序:

  • 在 18 到 25 之间有年龄的个人,假设 Person 实体具有 年龄 属性,并由用户应用程序更新。
  • 交易量超过 2000美元.
  • F1 竞争者的 lap 速度小于 1:45.00 秒时,假设缓存包含 Lap 条目,并在竞争过程中输入 laps。
注意

持续查询可以使用所有查询功能,但分组、聚合和排序操作除外。

持续查询的工作方式

持续查询使用以下事件通知客户端监听程序:

join
缓存条目与查询匹配。
Update(更新)
与查询匹配的缓存条目已更新,仍然与查询匹配。
leave
缓存条目不再与查询匹配。

当客户端注册一个连续查询监听程序时,它会立即接收与查询匹配的任何条目的 Join 事件。客户端监听程序在每次缓存操作修改与查询匹配的条目时接收后续事件。

Data Grid 决定将 JoinUpdate 或 Leave 事件发送到 客户端监听程序的时间,如下所示:

  • 如果旧值和新值的查询不匹配,则 Data Grid 不会发送事件。
  • 如果旧值的查询不匹配,但新值不匹配,则 Data Grid 会发送 Join 事件。
  • 如果旧值和新值上的查询都匹配,Data Grid 会发送 Update 事件。
  • 如果对旧值的查询匹配但新值不匹配,则 Data Grid 会发送 Leave 事件。
  • 如果对旧值的查询匹配并且条目被删除或过期,则 Data Grid 会发送 Leave 事件。

5.1.1. 持续查询和数据网格性能

持续查询为应用程序提供持续的更新流,从而产生大量事件。Data Grid 会临时为它生成的每个事件分配内存,这可能会导致内存压力,并可能导致 OutOfMemoryError 异常,特别是对于远程缓存。因此,您应该仔细设计您的连续查询以避免出现性能影响。

Data Grid 强烈建议您将持续查询的范围限制为您需要的最小信息。要达到此目的,您可以使用 projections 和 predicates。例如,以下语句只提供与条件匹配的字段子集而不是整个条目的结果:

SELECT field1, field2 FROM Entity WHERE x AND y

还需要确保创建的每个 continuous QueryListener 可以快速处理所有接收的事件,而无需阻塞线程。要达到此目的,您应该避免任何不必要地生成事件的缓存操作。

5.2. 创建持续查询

您可以为远程和嵌入式缓存创建连续查询。

流程

  1. 创建 Query 对象。
  2. 通过调用适当的方法来获取缓存的 continuous Query 对象:

    • 远程缓存: org.infinispan.client.hotrod.Search.getContinuousQuery (RemoteCache<K, V> cache)
    • 嵌入式缓存: org.infinispan.query.Search.getContinuousQuery (Cache<K, V> cache)
  3. 注册查询和 ContinuousQueryListener 对象,如下所示:

    continuousQuery.addContinuousQueryListener(query, listener);
  4. 当您不再需要持续查询时,删除监听程序,如下所示:

    continuousQuery.removeContinuousQueryListener(listener);
持续查询示例

以下代码示例演示了带有嵌入式缓存的简单连续查询。

在本例中,当 21 年龄下的任何 Person 实例添加到缓存中时,侦听器接收通知。这些 Person 实例也添加到"匹配"映射中。当从缓存中删除条目或其年龄大于或等于 21 时,它们会从"matches"映射中删除。⁠

注册连续查询

import org.infinispan.query.api.continuous.ContinuousQuery;
import org.infinispan.query.api.continuous.ContinuousQueryListener;
import org.infinispan.query.Search;
import org.infinispan.query.dsl.QueryFactory;
import org.infinispan.query.dsl.Query;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

[...]

// We have a cache of Person objects.
Cache<Integer, Person> cache = ...

// Create a ContinuousQuery instance on the cache.
ContinuousQuery<Integer, Person> continuousQuery = Search.getContinuousQuery(cache);

// Define a query.
// In this example, we search for Person instances under 21 years of age.
Query query = cache.query("FROM Person p WHERE p.age < 21");

final Map<Integer, Person> matches = new ConcurrentHashMap<Integer, Person>();

// Define the ContinuousQueryListener.
ContinuousQueryListener<Integer, Person> listener = new ContinuousQueryListener<Integer, Person>() {
    @Override
    public void resultJoining(Integer key, Person value) {
        matches.put(key, value);
    }

    @Override
    public void resultUpdated(Integer key, Person value) {
        // We do not process this event.
    }

    @Override
    public void resultLeaving(Integer key) {
        matches.remove(key);
    }
};

// Add the listener and the query.
continuousQuery.addContinuousQueryListener(query, listener);

[...]

// Remove the listener to stop receiving notifications.
continuousQuery.removeContinuousQueryListener(listener);

第 6 章 监控和调优数据网格查询

Data Grid 会公开查询的统计信息,并提供您可以调整的属性以提高查询性能。

6.1. 获取查询统计信息

收集统计信息以收集有关索引和查询的性能信息,包括索引类型、查询平均完成以及索引操作中可能失败的次数。

流程

执行以下操作之一:

  • 为嵌入式缓存调用 getSearchStatistics ()getClusteredSearchStatistics () 方法。
  • 使用 GET 请求从 REST API 获取远程缓存的统计信息。

嵌入式缓存

// Statistics for the local cluster member
SearchStatistics statistics = Search.getSearchStatistics(cache);

// Consolidated statistics for the whole cluster
CompletionStage<SearchStatisticsSnapshot> statistics = Search.getClusteredSearchStatistics(cache)

远程缓存

GET /rest/v2/caches/{cacheName}/search/stats

6.2. 调优查询性能

使用以下准则,以帮助您提高索引操作和查询的性能。

检查索引用量统计

对部分索引缓存的查询会返回较慢的缓存结果。例如,如果没有注解模式中的一些字段,则生成的索引不包括这些字段。

通过检查每种查询运行所需的时间,开始调整查询性能。如果您的查询似乎很慢,您应该确保查询对缓存使用索引,并且所有实体和字段映射都被索引。

为索引调整提交间隔

索引可以降级 Data Grid 集群的写入吞吐量。commit-interval 属性定义间隔,以毫秒为单位定义内存中缓冲的索引更改被刷新到索引存储,并执行提交。

此操作的成本比较昂贵,您应该避免配置太小的间隔。默认值为 1000 ms (1 秒)。

为查询调整刷新间隔

refresh-interval 属性定义刷新索引读取器之间的间隔(以毫秒为单位)。

默认值为 0, 它会在将查询写入缓存后立即返回查询的数据。

值大于 0 会导致一些过时的查询结果,但显著提高吞吐量,特别是在写密集型场景中。如果您不需要在查询被写入后马上返回数据,您应该调整刷新间隔以提高查询性能。

法律通告

Copyright © 2025 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.
Red Hat logoGithubRedditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

© 2024 Red Hat, Inc.