4

我正在体验使用 .NET NEST 客户端和 ElasticSearch 的批量索引性能随着时间的推移而降低,索引数量和文档数量都是恒定的。

我们ElasticSearch Version: 0.19.11, JVM: 23.5-b02在一个 m1.large Amazon 实例上运行,它带有 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");
}

我在这里做错了什么吗?

4

2 回答 2

6

抱歉 - 刚刚开始写另一条很长的评论,并认为我会把它全部放在答案中,以防它对其他人有益......

ES_HEAP_SIZE

我在这里注意到的第一件事是您说您将弹性搜索的最大和最小堆值设置为不同的值。这些应该是一样的。在配置/init.d 脚本中应该有一个可以设置的 EX_HEAP_SIZE。请务必仅设置此值(而不是最小值和最大值),因为它将最小值和最大值设置为您想要的相同值。如果你不这样做,当你开始需要更多内存时,JVM 会阻塞 Java 进程 -请参阅最近在 github 上关于中断的这篇精彩文章(这里有一个引用):

设置 ES_HEAP_SIZE 环境变量,以便 JVM 对最小和最大内存使用相同的值。将 JVM 配置为具有不同的最小值和最大值意味着每次 JVM 需要额外的内存(达到最大值)时,它都会阻塞 Java 进程来分配它。结合旧的 Java 版本,这解释了我们的节点在向公共搜索开放时引入更高负载和连续内存分配时表现出的暂停。elasticsearch 团队建议设置为系统 RAM 的 50%。

还可以查看这篇很棒的帖子,了解更多来自战壕的 elasticsearch 配置。

锁定内存以停止交换

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

升级

Elasticsearch 还是很新的。计划相当频繁地升级,因为在您使用的版本 (0.19.11) 和当前版本 (0.20.4) 之间引入的错误修复非常重要。有关详细信息,请参阅ES 站点。您使用的是 Java 7,这绝对是正确的方法,我从 Java 6 开始并很快意识到它还不够好,尤其是对于批量插入。

插件

最后,对于遇到类似问题的其他人,请安装一个不错的插件,以了解您的节点和 JVM。我推荐bigdesk - 运行 bigdesk,然后使用一些批量插入来点击 elasticsearch,并注意奇怪的堆内存模式、大量线程等,一切都在那里!

希望有人觉得这很有用!

干杯,詹姆斯

于 2013-02-05T11:28:56.790 回答
2

只是冒险猜测:

随着索引性能下降,您是否注意到索引占用了更多磁盘空间?

可能是这样的,而不是在重新索引时替换旧索引或旧文档,而是添加一堆新文档,有效地使文档数量增加一倍,并且可能使用大量重复的数据。可能值得抓住一个陈旧、缓慢的索引并将其加载到一些查看器中进行调试(例如Luke)。如果您看到的文档比您预期的要多得多,那么您可能会考虑让您的重建创建一个新索引来替换旧索引。

由于重新启动守护程序并不能解决问题,我想可以排除打开文件句柄、正在运行的进程、连接等,但我想检查这些统计信息,并确定我是否在服务器。

此外,关于优化,您可能会看到一些性能增强,当然,但这是一项非常昂贵的操作。我建议仅在完全重建完成后运行优化,而不是在每次增量批量索引操作后运行。

于 2012-12-07T10:37:58.637 回答