.net - 随着索引和文档数量不变,elasticsearch 批量索引会随着时间变慢

标签 .net lucene amazon-ec2 elasticsearch nest

我发现使用 .NET NEST 客户端和 ElasticSearch 的批量索引性能随着时间的推移而降低,索引数量和文档数量保持不变。

我们正在 m1.large Amazon 实例上运行 ElasticSearch 版本:0.19.11,JVM:23.5-b02,Ubuntu Server 12.04.1 LTS 64 位和 Sun Java 7。没有别的在此实例上运行,但 Ubuntu 安装附带的除外。

Amazon M1 大型实例:来自 http://aws.amazon.com/ec2/instance-types/

7.5 GiB memory
4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute Units each)
850 GB instance storage
64-bit platform
I/O Performance: High
EBS-Optimized Available: 500 Mbps
API name: m1.large

ES_MAX_MEM 设置为 4g,ES_MIN_MEM 设置为 2g

每天晚上,我们在 .NET 应用程序中使用 NEST 索引/重新索引约 15000 个文档。在任何给定时间,只有一个索引包含 <= 15000 个文档。

首次安装服务器时,前几天索引和搜索速度很快,然后索引开始变得越来越慢。批量索引一次索引 100 个文档,一段时间后,批量操作最多需要 15 秒才能完成。在那之后,我们开始看到很多以下异常,索引逐渐停止。

System.Net.WebException: The request was aborted: The request was canceled.
   at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization) : 

构建索引的实现看起来像这样

private ElasticClient GetElasticClient()
{
    var setting = new ConnectionSettings(ConfigurationManager.AppSettings["elasticSearchHost"], 9200);
    setting.SetDefaultIndex("products");
    var elastic = new ElasticClient(setting);
    return elastic;
}

private void DisableRefreshInterval()
{
    var elasticClient = GetElasticClient();
    var s = elasticClient.GetIndexSettings("products");
    var settings = s != null && s.Settings != null ? s.Settings : new IndexSettings();
    settings["refresh_interval"] = "-1";
    var result = elasticClient.UpdateSettings(settings);
    if (!result.OK)
        _logger.Warn("unable to set refresh_interval to -1, {0}", result.ConnectionStatus == null || result.ConnectionStatus.Error == null ? "" : result.ConnectionStatus.Error.ExceptionMessage);
}

private void EnableRefreshInterval()
{
    var elasticClient = GetElasticClient();
    var s = elasticClient.GetIndexSettings("products");
    var settings = s != null && s.Settings != null ? s.Settings : new IndexSettings();
    settings["refresh_interval"] = "1s";
    var result = elasticClient.UpdateSettings(settings);
    if (!result.OK)
        _logger.Warn("unable to set refresh_interval to 1s, {0}", result.ConnectionStatus == null || result.ConnectionStatus.Error == null ? "" : result.ConnectionStatus.Error.ExceptionMessage);
}

public void Index(IEnumerable<Product> products)
{
    var enumerable = products as Product[] ?? products.ToArray();
    var elasticClient = GetElasticClient();
    try
    {
        DisableRefreshInterval();

        _logger.Info("Indexing {0} products", enumerable.Count());
        var status = elasticClient.IndexMany(enumerable as IEnumerable<Product>, "products");

        if (status.Items != null)
            _logger.Info("Done, Indexing {0} products, duration: {1}", status.Items.Count(), status.Took);

        if (status.ConnectionStatus.Error != null)
        {
            _logger.Error(status.ConnectionStatus.Error.OriginalException);
        }
    }
    catch(Exception ex)
    {
        _logger.Error(ex);
    }
    finally
    {
        EnableRefreshInterval();
    }
}

重新启动 elasticsearch 守护进程似乎没有任何区别,但删除索引并重新索引所有内容。但几天后,我们会遇到同样的索引速度慢的问题。

我刚刚删除了索引并在每次批量索引操作后重新启用刷新间隔后添加了一个优化,希望这可以防止索引降级。

...
...
finally
{
    EnableRefreshInterval();
    elasticClient.Optimize("products");
}

我在这里做错了什么吗?

最佳答案

抱歉 - 刚开始写另一个很长的评论,我想我会把它全部放在一个答案中,以防它对其他人有好处......

ES_HEAP_SIZE

我在这里注意到的第一件事是您说您将 elasticsearch 的最大和最小堆值设置为不同的值。这些应该是一样的。在 configuration/init.d 脚本中应该有一个 EX_HEAP_SIZE 可以设置。请务必仅设置此项(而不是最小值和最大值),因为它会将最小值和最大值设置为您想要的相同值。如果您不这样做,JVM 将在您开始需要更多内存时阻止 Java 进程 - see this great article最近 github 的一次中断(这里引用一段话):

Set the ES_HEAP_SIZE environment variable so that the JVM uses the same value for minimum and maximum memory. Configuring the JVM to have different minimum and maximum values means that each time the JVM needs additional memory (up to the maximum), it will block the Java process to allocate it. Combined with the old Java version, this explains the pauses that our nodes exhibited when introduced to higher load and continuous memory allocation when they were opened up to public searches. The elasticsearch team recommends a setting of 50% of system RAM.

另请查看 this great post从战壕中获取更多 Elasticsearch 配置。

锁定内存以停止交换

根据我的研究,我发现您还应该锁定 Java 进程可用的内存量以避免内存交换。我不是该领域的专家,但有人告诉我这也会降低性能。您可以在 elasticsearch.yml 配置文件中找到 bootstrap.mlockall。

升级

Elasticsearch 仍然很新。计划相当频繁地升级,因为在您使用的版本 (0.19.11) 和当前版本 (0.20.4) 之间引入的错误修复非常重要。查看ES site了解详情。您使用的是 Java 7,这绝对是正确的选择,我开始使用 Java 6 并很快意识到它不够好,尤其是对于批量插入而言。

插件

最后,对于遇到类似问题的任何其他人,请安装一个像样的插件来概览您的节点和 JVM。我推荐bigdesk - 运行 bigdesk,然后用一些批量插入点击 elasticsearch,注意奇怪的堆内存模式、大量线程等,它们都在那里!

希望有人觉得这有用!

干杯, 詹姆斯

关于.net - 随着索引和文档数量不变,elasticsearch 批量索引会随着时间变慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13757398/

相关文章:

c# - 新的或修改的日历事件的 EWS PullSubscription

c# - 如何更改 C# 错误提示的语言?

java - Lucene 和 Google App Engine

c# - 在 Linq To Sql 中将字段标记为脏

.net - 混合模式 C++/CLR 应用程序中的内存泄漏

amazon-s3 - 在 Amazon EC2、S3 和/或 EBS 上托管和访问 HDF5 文件

tomcat - 托管在 EC2 上的网站问题

amazon-ec2 - PhantomJS CPU(核心)是否受限?

apache - Apache Nutch爬虫如何排除静态文件夹之类; cgi-bin,图片,css是否从NutCrawer中排除?

java - solr 与 mysql 驱动程序的索引性能