索引性能增强

https://blueprints.launchpad.net/searchlight/+spec/index-performance-enhancement.

此特性将改进 Searchlight 中资源类型索引的性能。

问题描述

如果上面的链接太麻烦,请允许我们抄袭蓝图中的内容。

在索引(首次或重新索引)时,我们会按顺序索引所有资源组类型。我们循环遍历所有插件,依次索引每个插件。结果是重新索引所需的时间等于所有插件所需时间的总和。这可能比应该花费的时间更长。在某些情况下,甚至长得多。

完成整个索引所需的时间是

    n
O( ∑ T(p) )
    p=0

当 n 是插件的数量,T(p) 是插件 p 索引所需的时间时。

我们应该更改算法以并行索引,而不是串行索引。当我们循环遍历每个插件以重新索引时,我们应该将每个索引任务放入它自己的线程中。这样,索引所需的时间就是最长的插件重新索引所需的时间。

通过此增强,完成索引所需的时间是

         n
O( MAX( T(p) ) )
        p=0

为了提供设计的上下文,我们将回顾当前重新索引的设计。重新索引在管理员运行命令时开始

searchlight-manage index sync

在底层,searchlight-manage 正在执行以下操作

  • 确定需要重新索引哪些资源组。

  • 确定每个资源组中需要重新索引哪些资源类型。

  • 对于确实需要重新索引的每个资源类型,searchlight-manage 将调用与该资源类型关联的插件。该插件将向该服务发出 API 调用并重新索引信息。

  • 对于不需要重新索引的每个资源类型,searchlight-manage 将直接调用 ElasticSearch 并从旧索引重新索引到新索引。

  • 一旦所有重新索引完成,ES 别名将被调整,并且 searchlight-manage 返回给用户。

这意味着以下几点
  • 管理员必须等待所有重新索引完成,然后 searchlight-manage 才能完成。

  • searchlight-manage 完成时,管理员知道重新索引的确切状态。无论它是否成功完成或发生错误。

提议的变更

如蓝图中所述,我们希望减少完成重新索引的时间。根据与蓝图和此规范的讨论,我们将仅实现蓝图中的第一个增强。我们将使用 python 线程来完成此任务。我们需要了解与实现多线程方法相关的设计问题。

  1. 索引插件是否线程安全?

如果插件之间存在大量的内部依赖关系,那么尝试将插件多线程化可能不会带来好处。通过审查插件的代码和功能,它们似乎足够独立,可以移动到它们自己的线程中。插件彼此隔离,并且不依赖于任何内部结构来处理实际的索引。

设计建议: 个人插件可以成功地线程化。

  1. 我们应该在哪个级别创建索引线程?

显而易见的选择是资源类型(例如 OS::Nova::Server)或资源类型组(例如索引“searchlight”)。我们考虑此增强的主要原因是特定资源类型花费了大量时间,但特定资源类型组却没有。

searchlight-manage 内部,这种区别很快就变得模糊了。我们使用资源类型组仅确定需要重新索引哪些资源类型。我们还在 searchlight-manage 中有一个现有的增强功能,我们仅通过插件 API 重新索引用户明确要求的资源类型。所有其他资源类型都直接在 ElasticSearch 中重新索引。我们需要保留此增强功能。

保持当前设计不变意味着我们希望在细粒度的资源类型级别进行线程化,而不是在粗粒度的资源类型组级别进行线程化。基于某些资源类型之间存在的父/子关系,这是我们将考虑的“细”级别。

由于我们已经为 Elasticsearch 重新索引使用了批量命令,因此我们将所有 Elasticsearch 重新索引放入单个线程中。考虑到这将受到 Elasticsearch 侧 I/O 的限制,似乎没有理由为每个资源类型单独创建一个 Elasticsearch 重新索引线程。

设计建议: 每当索引代码当前调用插件 API 时,它将在线程池中创建一个 worker。

设计建议: 所有调用 ElasticSearch 以重新索引现有索引的调用都将放置在线程池中的单个 worker 中。

  1. 插件到线程的映射

Searchlight 中使用的插件数量可能很多。如果每个插件都有自己的线程,我们可能会使用很多线程。与其让单个线程映射到单个插件,我们将使用线程池。这将使线程数量保持在可管理的水平,同时仍然允许适当程度的异步重新索引。线程池的大小可以通过配置选项进行更改。

设计建议: 使用线程池。

  1. 我们何时知道切换 ElasticSearch 别名?

在串行重新索引模型中,知道何时切换 ElasticSearch 别名以使用新索引是微不足道的。就是在最后一个索引完成后!切换到并行运行的异步线程模型可能会使别名更新复杂化。

索引代码将等待所有线程完成。当所有线程都完成后,索引代码可以继续更新别名。

设计建议: 别名切换代码将在所有线程完成后运行。

  1. 如何清理失败的线程?

索引代码需要让线程在发生灾难性故障时进行通信。在所有 worker 放入线程池后,主程序将等待所有线程完成。如果任何线程失败,它将引发异常。该异常将被捕获并开始正常的清理调用。所有仍在等待运行的线程将被取消。

设计建议: 捕获失败线程抛出的异常。

对于那些关注代码的人(searchlight/cmd/manage.py::sync),这里是对更改的粗略指南。我们将参考大型注释块中提到的部分

  • 第一遍:没有更改。

  • 第二遍:没有更改。

  • 步骤 #1:没有更改。

  • 步骤 #2:没有更改。

  • 步骤 #3:没有更改。

  • 步骤 #4:使用线程。跟踪线程使用情况。

  • 步骤 #5:没有更改。

  • 步骤 #6:没有更改。

备选方案

我们可以选择不执行任何增强。或者我们可以回到此规范的第一个草案。

参考资料