查询数据网格缓存
在 Data Grid cache 中查询您的数据
摘要
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 配置为索引缓存。索引对您的查询提供了显著的性能,可让您更快地了解您的数据。
流程
在缓存配置中启用索引。
<distributed-cache> <indexing> <!-- Indexing configuration goes here. --> </indexing> </distributed-cache>
提示在
配置中添加
indexing 元素可启用索引,而无需包含enabled=true
属性。对于添加此元素的远程缓存,也会隐式将编码配置为 ProtoStream。
使用
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>
属性 | 描述 |
---|---|
| 以毫秒为单位,索引更改会清除到索引存储,并且执行提交的时间(以毫秒为单位)。因为操作非常昂贵,因此应该避免小的值。默认值为 1000 ms (1 秒)。 |
|
在将缓冲的内存中刷新到索引存储前可以缓冲的最大条目数。较大的值会导致索引速度更快,但使用更多内存。当与 |
|
在清除索引存储前,可用于缓冲添加的条目和删除的最大内存量。较大的值会导致索引速度更快,但使用更多内存。为了加快索引性能,您应该设置此属性而不是 |
| 由于 Infinispan 15.0,因此会忽略此配置。索引引擎现在使用 Infinispan 线程池。 |
| 默认 4。用于每个索引类型的内部队列数量。每个队列包含应用于索引和队列的修改批量处理。增加队列数量将导致增加索引吞吐量,但只有在瓶颈是 CPU 时。 |
|
默认 4000。每个队列可以容纳的最大元素数。增加 |
| 为索引操作启用低级追踪信息。启用此属性可显著提高性能。您应该只使用此低级追踪作为故障排除的最后资源。 |
要配置数据网格如何合并索引片段,您可以使用 index-merge
子元素。
属性 | 描述 |
---|---|
| 索引片段在合并前可以具有的最大条目数。有超过这个条目数量的片段不会被合并。较小的值在频繁更改索引时执行更好,如果索引没有经常更改,较大的值可以提供更好的搜索性能。 |
| 一次合并的片段数量。使用较小的值时,合并的频率会更频繁地使用更多资源,但片段总数平均会降低,从而提高了搜索性能。较大的值(超过 10)最适合编写大量情况。 |
| 后台合并的最小目标大小(以 MB 为单位)。小于这个大小的片段会更积极地合并。设置太大的值可能会导致昂贵的合并操作,即使它们比较频繁。 |
|
后台合并的最大片段大小(以 MB 为单位)。大于这个大小的片段永远不会在后台合并。把它设置为较低值有助于降低内存要求,并避免在最佳搜索速度下一些合并操作。当强制合并索引和 |
|
强制合并并覆盖 |
|
在计算网段中的条目时,是否应该考虑索引中已删除条目的数量。设置 |
其他资源
索引分片
当您有大量数据时,您可以将 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
。
每个注解都支持一组属性,可用于进一步描述实体的索引方式。
注解 | 支持的属性 |
---|---|
@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
操作的状态。
-
使用索引统计的 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 中都接受AND
或OR
。 -
!
可以被使用,而不是。
-
缺少布尔值运算符解释为
OR
。 - 字符串术语必须用单引号或双引号括起。
- Fuzziness 和 boosting 没有被任意顺序接受;fuzziness 始终是首先接受的。
-
!=
被接受,而不是 <>
。 -
boosting 无法应用到 >,
>
;=,
<
,<
;= operators。范围可用于实现相同的结果。
2.2.1. 过滤 Operator
Ickle 支持许多可用于索引和非索引字段的过滤运算符。
Operator | 描述 | Example |
---|---|---|
| 检查左侧运算对象是否等于所给值集合中的一个元素。 |
|
| 检查左侧参数(预期为 String)是否匹配 JPA 规则之后的通配符模式。 |
|
| 检查 left 参数是否与给定值完全匹配。 |
|
| 检查 left 参数是否与给定值不同。 |
|
| 检查 left 参数是否大于给定值。 |
|
| 检查 left 参数是否大于或等于给定值。 |
|
| 检查 left 参数是否小于给定值。 |
|
| 检查 left 参数是否小于或等于给定值。 |
|
| 检查 left 参数是否在给定的范围限值之间。 |
|
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 子句中带有一个或多个字段)。
支持的聚合有: avg
、sum
、count
、max
和 min
。
组分组字段通过 GROUP BY
子句指定,并且用于定义分组字段的顺序无关。投射中选择的所有字段都必须分组字段,否则必须使用下面描述的分组功能之一来聚合它们。项目字段可以聚合,并同时用于分组。选择仅分组字段但没有聚合字段的查询是法律的。附录示例:作者对手册进行分组,并计算它们。
SELECT author, COUNT(title) FROM org.infinispan.sample.Book WHERE title LIKE '%engine%' GROUP BY author
一个投射查询,所有选择的字段都应用了聚合功能,且无法用于分组的字段。在这种情况下,聚合将全局计算,就像有一个全局组一样。
聚合
您可以将以下聚合功能应用到一个字段:
聚合功能 | 描述 |
---|---|
|
计算一组数字的平均数量。接受的值是原始数字和 |
|
计算非null 行的数量并返回 |
|
返回找到的最大价值。接受的值必须是 |
|
返回找到的最小值。接受的值必须是 |
|
计算一组数字的总和。如果没有非 null 值,则结果为 |
字段类型 | 返回类型 |
---|---|
不可或缺(除 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>
; 引用单个实体。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 的正则表达式语法,因此可以搜索单词 moat
或 boat
:
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>
流程
在类中添加索引注解,如下例所示:
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 }
在新类中实施
SerializationContextInitializer
接口,然后添加@ProtoSchema
注释。-
使用
includeClasses
参数引用包含@ProtoField
注解的类。 -
定义 Protobuf 模式的名称,该模式使用
schemaFileName
和schemaFilePath
参数生成文件系统路径。 使用
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 { }
-
使用
编译您的项目。
此流程中的代码示例生成
proto/book.proto
模式,以及 annotatedBook
类的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 查询,如 BigInteger
和 BigDecimal
。
流程
在类中添加索引注解,如下例所示:
@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; } }
将
dependentOn
属性设置为CommonTypes.class
,以指示生成的 Protobuf 模式可以引用和使用CommonTypes
类型,如BigInteger
和BigDecimal
:@ProtoSchema(includeClasses = CalculusIndexed.class, dependsOn = CommonTypes.class, schemaFilePath = "/protostream", schemaFileName = "calculus-indexed.proto", schemaPackageName = "lab.indexed") public interface CalculusIndexedSchema extends GeneratedSchema { }
执行查询:
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 凭据。
流程
在 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
创建名为 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
向缓存中添加条目。
要查询远程缓存,需要包含一些数据。在本例中,创建使用以下 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。
查询您的远程缓存。
在 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 中直接指定分析器定义。
流程
-
使用
@Text
注释给属性添加注释,以指示其值已被分析。 -
使用
分析器
属性指定您要用于索引和搜索所需的分析器。
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 提供了一组默认的分析器定义。
定义 | 描述 |
---|---|
| 将文本字段拆分为令牌,将空格和标点分隔为分隔符。 |
| 通过取消限制非字母的令牌化输入流,然后将所有字母转换为小写字符。空格和非字母将被丢弃。 |
| 分割空格上的文本流,并将非空格字符序列返回为令牌。 |
| 将整个文本字段视为单一令牌。 |
| 使用 Snowball Porter 过滤器窃取英语词. |
| 默认情况下,生成大小为 3 分的 ngram 令牌。 |
|
将文本字段分成比 |
| 将文本的所有字母转换为小写字符,文本不会被令牌化(规范化程序)。 |
这些分析器定义基于 Apache Lucene。有关令牌工具、过滤器和 CharFilters 的更多信息,请参阅 Apache Lucene 文档。
其他资源
3.4.2. 创建自定义分析器定义
创建自定义分析器定义,并将它们添加到您的 Data Grid Server 安装中。
先决条件
如果 Data Grid Server 正在运行,则停止它。
Data Grid Server 仅在启动时加载类。
流程
-
实施
ProgrammaticSearchMappingProvider
API。 在 JAR 中打包您的实现,在以下文件中带有完全限定类(FQN):
META-INF/services/org.infinispan.query.spi.ProgrammaticSearchMappingProvider
-
将 JAR 文件复制到 Data Grid Server 安装的
server/lib
目录中。 - 启动 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; }
流程
配置 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>
获取缓存。
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");
对存储在 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 决定将 Join
、Update
或 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. 创建持续查询
您可以为远程和嵌入式缓存创建连续查询。
流程
-
创建
Query
对象。 通过调用适当的方法来获取缓存的 continuous
Query
对象:-
远程缓存:
org.infinispan.client.hotrod.Search.getContinuousQuery (RemoteCache<K, V> cache)
-
嵌入式缓存:
org.infinispan.query.Search.getContinuousQuery (Cache<K, V> cache)
-
远程缓存:
注册查询和
ContinuousQueryListener
对象,如下所示:continuousQuery.addContinuousQueryListener(query, listener);
当您不再需要持续查询时,删除监听程序,如下所示:
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
会导致一些过时的查询结果,但显著提高吞吐量,特别是在写密集型场景中。如果您不需要在查询被写入后马上返回数据,您应该调整刷新间隔以提高查询性能。