avatar

十六小站

欢迎来到我的个人主页! 期待与您分享我的经验与故事,一起探索技术的无穷可能!

  • 首页
  • NAS专题
  • 关于
Home OpenSearch踩坑记录
文章

OpenSearch踩坑记录

Posted 2024-11-14 Updated 2024-11- 14
By 十六
14~18 min read

Opensearch继承这里不做赘述,文中代码皆为Demo,不可直接用于生产需要包装。

分页查询相关

  1. Opensearch是支持分页查询功能的其代码如下:

import org.opensearch.client.RequestOptions;
import org.opensearch.client.RestHighLevelClient;
import org.opensearch.client.indices.GetIndexRequest;
import org.opensearch.client.indices.GetIndexResponse;
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.search.builder.SearchSourceBuilder;
import org.opensearch.client.RequestOptions;
import org.opensearch.search.SearchRequest;
import org.opensearch.search.SearchResponse;
import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.opensearch.search.aggregations.AggregationBuilders;

import java.io.IOException;

public class OpenSearchPaginationClient extends RestHighLevelClient {

    public OpenSearchPaginationClient(RestClientBuilder builder) {
        super(builder);
    }

    public SearchResponse searchWithPagination(String index, int page, int size) throws IOException {
        // 创建搜索请求
        SearchRequest searchRequest = new SearchRequest(index);
        
        // 构建搜索源
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        
        // 设置查询条件(示例为匹配所有)
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        
        // 设置分页参数
        searchSourceBuilder.from(page * size); // 从第几条记录开始
        searchSourceBuilder.size(size); // 每页的条目数
        
        // 将源构建器应用到搜索请求
        searchRequest.source(searchSourceBuilder);
        
        // 执行搜索并返回响应
        return this.search(searchRequest, RequestOptions.DEFAULT);
    }

    public static void main(String[] args) {
        // 需要替换为您的 OpenSearch 客户端构建逻辑
        RestHighLevelClient client = new OpenSearchPaginationClient(/* Your RestClientBuilder */);
        
        int page = 0; // 目标页码
        int size = 10; // 每页文档数量
        
        try {
            SearchResponse response = client.searchWithPagination("your_index_name", page, size);
            // 处理搜索响应,如打印搜索结果
            Arrays.stream(response.getHits().getHits()).forEach(hit -> {
                System.out.println("Document ID: " + hit.getId());
                System.out.println("Source: " + hit.getSourceAsString());
            });
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在使用传统的基于 from 和 size 的分页方法时,尤其是在数据量较大的情况下,可能会遇到以下几个问题:

  1. 性能问题 : 随着页码的增加,from 参数的值也在增加,这会导致 OpenSearch 在执行查询时,跳过越来越多的文档,从而导致性能下降。

  2. 重复数据 : 如果数据在分页之间发生了变化(例如插入、更新或删除),可能会导致某些文档在不同的页面上重复出现,或者某些文档在某个页面上缺失。

为了避免这些问题,建议使用游标分页 (或者称为深度分页),这种方式通过保持状态来避免使用 from 和 size 方法,以下列举几种常见的游标分页方法:

1. 使用 Search After

search_after 是 OpenSearch 和 Elasticsearch 提供的一个高效的分页方法。它可以根据最后一条文档的信息进行下一页的查询,避免了传统分页方法的性能问题。

public SearchResponse searchWithSearchAfter(String index, int size, Object[] searchAfter) throws IOException {
    SearchRequest searchRequest = new SearchRequest(index);
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

    // 设置查询条件(示例为匹配所有)
    searchSourceBuilder.query(QueryBuilders.matchAllQuery());
    
    // 设置每页的条目数
    searchSourceBuilder.size(size); 

    // 使用 search_after 实现游标分页
    if (searchAfter != null) {
        searchSourceBuilder.searchAfter(searchAfter);
    }

    searchRequest.source(searchSourceBuilder);

    return this.search(searchRequest, RequestOptions.DEFAULT);
}

在该方法中,您可以传入 searchAfter 参数(一个 Object 数组),它表示上一个页面的最后一条文档的排序值。

改善数据的一致性

结合 search_after 使用时,可以确保在数据变更时,有效地减少重复或者遗漏。例如:

  1. 在文档或索引上使用时间戳字段,以确保在分页时可以通过自然排序来检索数据。

  2. 使用唯一标识符组合(如时间戳 + ID)作为 search_after 的参数。

2. 使用 Scroll API

如果需要在大数据集上实施深度分页,Scroll API 也是一个优秀的方案。Scroll API 主要用于查找所有文档,特别是在搜索结果集非常大的情况下可以减少性能开销。

public SearchResponse searchWithScroll(String index, int size) throws IOException {
    // 创建首次搜索请求
    SearchRequest searchRequest = new SearchRequest(index);
    searchRequest.scroll(TimeValue.timeValueMinutes(1)); // 设置 Scroll 的时间
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.query(QueryBuilders.matchAllQuery());
    searchSourceBuilder.size(size);
    searchRequest.source(searchSourceBuilder);

    // 执行首次搜索
    SearchResponse searchResponse = this.search(searchRequest, RequestOptions.DEFAULT);
    
    // 在这里处理首次搜索的结果
    // ...

    String scrollId = searchResponse.getScrollId();
    while (searchResponse.getHits().getHits().length != 0) {
        // 处理数据...
        
        // 创建新的 Scroll 请求
        SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
        scrollRequest.scroll(TimeValue.timeValueMinutes(1));

        // 执行 Scroll 请求
        searchResponse = this.scroll(scrollRequest, RequestOptions.DEFAULT);
        scrollId = searchResponse.getScrollId();
    }

    // 清除 Scroll 上下文
    ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
    clearScrollRequest.addScrollId(scrollId);
    this.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);

    return searchResponse;
}

注意事项:

  • 使用 search_after : 当你的数据支持用来排序的字段时,这是处理深度分页的最佳解决方案。

  • 使用 Scroll API : 如果有复杂的场景需要遍历大量文档,使用 Scroll API 是合适的选择。

  • 对于实时性要求较高的场景,需在实现中充分考虑数据一致性和状态管理。

高手之路
Java
License:  CC BY 4.0
Share

Further Reading

Apr 2, 2025

简单规则引擎

背景 在一些项目中我们需要通过用户自定义配置一些阈值逻辑进行判断是否触发阈值,例如大部分IOT项目中需要设置自动触发条件(例如:每天22:00后 且 窗帘关闭的情况下关灯)。 当然有一些成熟如规则引擎如:Drools和LiteFlow,但是这里写一款简单的java实现方案作为记录。 前端示例 以下示

Feb 24, 2025

Jvm参数

JVM参数的设置对应用程序的性能和稳定性至关重要,尤其是在高并发场景下。合理的JVM参数配置可以优化内存使用、减少GC停顿时间、提升吞吐量。合适的jvm参数能将服务器性能压榨到极致。 堆内存设置 堆内存是JVM中最重要的内存区域,用于存储对象实例。合理设置堆内存大小可以避免频繁GC和内存溢出。 -X

Dec 25, 2024

多线程的那些事

踩坑记录 在线程中打印的日志,没有错误信息和堆栈信息。 代码: ForkJoinTask<?> task = pool.submit(() -> { try { processContent(appendixs);

OLDER

HTML编译成应用程序

NEWER

多线程的那些事

Recently Updated

  • Onlyoffice编译
  • K6+Playwright实现并发测试
  • 简单规则引擎
  • 在WEB中子线程可以访问Request上下文
  • onlyoffice配置

Trending Tags

Java Docker 前端 中间件 数据库 群晖 unraid

Contents

©2025 十六小站. 陕ICP备2023009742号-2