vlambda博客
学习文章列表

读书笔记《elasticsearch-blueprints》处理基于时间的数据

Chapter 8. Handling Time-based Data

在本章中,我们将探讨在 Elasticsearch 中使用常规索引时所面临的困难的解决方案。本章将向您介绍以下主题:

  • 使用基于时间的常规计划外索引数据遇到的问题

  • 在 Elasticsearch 中索引基于时间的数据

  • 覆盖未来指数的默认值

  • 覆盖索引级、类型级和字段级设置

  • 在基于时间的数据中进行高性能搜索

  • 调整最近数据的性能

在前面的章节中,我们看到了如何在 Elasticsearch 中索引数据。现在我们已经完成了一种非常基本的索引形式,让我们来看看普通方法的优缺点。在迄今为止的索引方法中,我们看到当我们创建索引时,我们还定义了与其关联的分片数量。一旦我们创建了一个索引,我们就不能增加或减少它的分片数量。

您可能还记得,碎片是包含我们数据的地方。但是,一旦我们在创建索引时指定了 分片的数量,以后就无法增加或减少该数量。因此,只有当我们事先知道我们创建的索引中将包含多少数据时,这种方法基本上是可以的。尽管如此,这不一定是很多次的情况。当被索引的数据是基于时间的时,这种索引的主要问题就出现了。

让我们在细节中探讨问题,即基于时间的数据何时出现。基于时间的数据是我们随着时间不断添加文档的地方。最初,可能没有大量数据,但随着时间的推移,可能会有大量数据。

Twitter 收听工具可以作为一个完美的例子,说明如何展示由 正常索引方式引起的问题。假设我们计划对来自某个地理位置(例如美国)的所有推文进行索引。如果我们使用单个索引来存储所有这些推文,在某个时间点,系统将会崩溃,因为我们无法扩展超出索引定义的分片数量。

因此,我们必须找到一种方法来解决索引创建中的先前问题。存储数据的最佳方式是在每个时间范围内为时间数据创建一个索引。在这里,根据数据量,我们可以每小时、每天、每周甚至一个月拥有一个索引。这有助于轻松进行数据管理,还可以解决由于 Elasticsearch 中的单一索引可能导致的问题。

下图显示了一种索引创建方法,其中索引是每天创建和命名的。这里,在下面的例子中,命名为YYYY-MM-DD,即年-月-日格式,前缀logstash:

读书笔记《elasticsearch-blueprints》处理基于时间的数据

Overriding default mapping and settings in Elasticsearch


在基于时间的数据中,我们注意到我们在每个时间范围内创建了一个新索引。假设索引每个时间范围需要自定义设置而不是默认设置,手动创建所有未来索引是不可能的。由于 Elasticsearch 在索引时自动创建缺少的索引,使用默认设置和映射,我们需要一些机制来覆盖默认值。

Elasticsearch 允许我们基于 < /a>模式。

Index template creation


模板创建是 Elasticsearch 中最重要的功能之一。它广泛用于与基于时间的数据索引。我们为索引使用模板创建的主要原因是大多数时候,我们会为不同的数据集使用自定义设置和映射。索引模板允许我们通过提供索引名称模式来定义索引的默认设置。所以,假设我们每天有一个索引,格式是 logstash-YYYY-MM-DD。假设我们想要一个只有 2 个分片而不是 5 个分片的索引,这是默认设置。我们可以为 logstash-* 模式分配一个模板。每次插入文档并且缺少文档的索引时,模式都会从模板中获取默认值。让我们看看如何创建模板:

curl -XPUT localhost:9200/_template/logstash -d '{
  "template": "logstash-*",
  "order": 0,
  "settings": {
    "number_of_shards": 2
  },
  "mappings": {
    "logs": {
      "_source": {
        "enabled": false
      }
    }
  }
}' 

在这里,创建了一个名为 logstash 的模板,我们在其中定义了自定义设置和映射。我们定义了一个 template 字段,它定义了它将匹配并应用模板设置的所有索引名称模式。在此示例中,设置和映射将应用于与 logstash-* 模板匹配的任何索引名称。也就是说,如果有名称的索引,logstash-23-06-2015logstash-masterlogstash:server,模板设置和映射将应用于前两个索引,因为它与我们模板中定义的模式匹配。

我们在前面的模板定义代码中有一个 order 字段,它的值是 0。 order 值设置 Elasticsearch 首选创建的模板的优先级。

Deleting a template

模板 在 Elasticsearch 中由它们的名称标识。所以,如果我们想删除名为 logstash 的模板,下面的代码可以做到:

curl -XDELETE localhost:9200/_template/logstash 

The GET template

在这里,标识 也是按名称,我们可以按名称检索模板。在我们的例子中,它将是:

curl -XGET localhost:9200/_template/logstash 

Multiple matching of templates

在 Elasticsearch 中使用多个模板创建索引是一种非常常见的实践。您可能经常会问这样的问题,如果此类模板中存在冲突的设置/映射会发生什么?让我们看看这样一个场景。假设我们有两个定义如下的模板:

  • 模板 1:

    curl -XPUT localhost:9200/_template/logstash1 -d '{
      "template": "logstash-*",
      "order": 0,
      "settings": {
        "number_of_shards": 2
      },
      "mappings": {
        "logs": {
          "_source": {
            "enabled": false
          }
        }
      }
    }'
  • 模板 2:

    curl -XPUT localhost:9200/_template/logstash2 -d ' {
      "template": "logs*",
      "order": 1,
      "settings": {
        "number_of_shards": 3
    },
    "mappings": {
      "logs": {
        "_source": { 
          "enabled": false 
        }
      } 
     }
    }'

在这种情况下,名为 logstash-21-04-2015logstash-server 的索引属于这两个模板。但是,模板 logstash1logstash2 的设置有 number_of_shards 字段中的冲突。因此,弄清楚 Elasticsearch 将哪个模板应用于索引会很有趣。

如果多个模板匹配一个索引名称,所有匹配的模板设置将被合并以获取要创建的索引的设置。如果发生冲突,订单号将用于解决冲突。

如您所见,logstash1 模板已按 0 的顺序定义,而 logstash2 模板已按 1 的顺序定义。 order 字段的值越小,优先级越高。这意味着在发生冲突的情况下,logstash1 模板的优先级高于 logstash2,这意味着冲突的字段值将从具有更高优先级的索引中获取,其余字段将被合并。

Overriding default settings for all indices

现在,有时,要求创建的所有索引都应具有一组特定的映射/设置。

这可以通过创建适用于所有索引的索引模板变得简单。为此,我们可以将 template 字段定义为 *,这将使模板能够将其所有设置/映射应用于索引用任何名字。

此类索引模板的示例如下所示:

curl -XPUT localhost:9200/_template/forall -d '{
  "template": "*",
  "order": 2,
  "settings": {
    "number_of_shards": 3
  },
  "mappings": {
    "logs": {
      "_all": {
        "enabled": false
      }
    }
  }
}'

Overriding mapping of all types under an index

使用索引模板,我们还可以覆盖索引下类型的默认值。默认情况下,对于 所有索引类型,_all 将被启用。在许多情况下,这可能会浪费资源。在模板中,我们可以定义名称已知的类型的映射,以及将来自动创建的任何索引类型的默认值。

一个例子是:

curl -XPUT 'localhost:9200/data-index' -d '{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 0
  },
  "mappings": {
    "_default_": {
      "_all": {
        "enabled": false
      }
    },
    "type1": {
      "_all": {
        "enabled": true
      }
    }
  }
}'

在这里,我们有一个名为 data-index 的索引。假设索引data-index下有三种类型,分别命名为type1type2< /code> 和 type3 以及对于 type1,我们需要启用 _all 字段。在前一种情况下,我们默认禁用 _all 字段。为了启用type1中的_all字段,我们单独提及并指定启用< /code> 为 true

现在使用此设置,除了 type1 之外,_all 将对所有其他类型禁用。

Overriding default field settings

在文档索引中,如果映射中未定义字段,则根据数据“猜测”该字段的类型。这很好用,但在许多情况下,我们可能需要覆盖此行为。最好的例子是自定义格式的时间字段。

当我们需要动态添加新变量时,动态模板用于动态映射。动态模板允许我们定义新生成的字段的映射。可以根据数据类型或字段名称将映射应用于新生成的字段。

这种动态模板的示例代码如下:

curl -XPUT 'localhost:9200/data-index' -d ' {
  "mappings": {
    "my_type": {
      "dynamic_templates": [
        {
          "customdate": {
            "match": "date_*",
            "match_mapping_type": "string",
            "mapping": {
              "type": "date",
              "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"
            }
          }
        },
        {
          "customstring": {
            "match": "*",
            "match_mapping_type": "string",
            "mapping": {
              "type": "string",
              "analyzer": "my-analyzer"
            }
          }
        }
      ]
    }
  }
}'

在前面的示例代码中,我们为字符串字段定义了两个名为 customdatecustomstring 的模板。

customdate 的作用是检查所有以 date_* 开头的字段。然后为匹配的字段提供映射类型 date,该映射类型定义了特定格式。这实际上是将 date_* 字段过滤为日期格式。

同样,在 customstring 模板中,match 是为 * 定义的,即是,所有字段,它会将映射应用于所有字段。映射可以为此目的使用任何分析器,甚至是我们创建的自定义分析器。

模板按出现的顺序进行检查。由于 customdate 被放在首位,带有 date_* 的字段首先被映射,然后,customstring 模板被选中。

Searching for time-based data


查询基于时间的数据的特殊性质在于,它主要是面向时间的。在大多数查询中,都会有一个确定的时间范围,主要指向最近的数据。让我们看看如何在搜索中利用这一点。

在前面的 部分中,我们看到了如何使用模板为基于时间的数据创建自定义索引,以及如何覆盖设置和映射。正如我们在上一节中看到的,最重要的应用是对我们感兴趣的索引进行建模以进行查询。这意味着,我们可以从整个基于时间的索引池中选择特定的索引,并对选定的几个进行操作。

假设我们有许多 logstash 索引以它们创建的那一周命名。所以,在一年内,比如 2014 年,总共有 52 个索引.假设我们还创建了 logstash-YYYY-WW 格式的索引名称,即年-周数字格式。因此,示例索引的名称为 logstash-2014-21,这意味着该索引是为 2014 年第 21 周的数据创建的。

让我们看一个实时场景并了解它是如何工作的。假设每天有一个索引,其中索引名称的格式为 logstash-YYYY-MM-DD。现在,让我们看看我们如何运行一个查询,该查询需要在过去 3 天中所有具有公司字段为 cisco 的文档:

POST /logstash-*/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "created_at": {
              "gt": "now-3d",
              "lt": "now"
            }
          }
        },
        {
          "term": {
            "company": "cisco"
          }
        }
      ]
    }
  }
}

如您所见,我们在整个索引集上运行查询,而不仅仅是在我们需要的索引上运行 。因此,我们在搜索上不必要地浪费了带宽。一个好的解决方法是只选择我们想要搜索的索引。

因此,可以以更有效的方式对前面的查询进行改造,如下所示(假设今天的日期是 2015-03-05):

POST /logstash-2015-03-05,logstash-2015-03-04, logstash-2015-03-03/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "created_at": {
              "gt": "now-3d",
              "lt": "now"
            }
          }
        },
        {
          "term": {
            "company": "cisco"
          }
        }
      ]
    }
  }
}

在这里,通过确保我们只搜索所需的索引,我们获得了更好的性能。

Archiving time-based data


在处理 与基于时间的数据时,通常会注意到最有用的数据是当前数据。这使得旧数据与我们的目的不太相关。因此,随着时间的推移,过去数据的相关性会迅速下降,我们索引的数据在集群中没有被使用就存在。这种情况对资源不是很友好,因为会有很多未使用的数据存储用于无目的或无目的的使用。

我们可以可视化不同级别的归档,如下所示:

  1. 将最热索引保留在具有良好硬件(分片过滤)的机器中。

  2. 在完成写入的索引上运行优化的 API。

  3. 关闭即时搜索不需要的索引。

  4. 拍摄快照并存档旧索引。

  5. 最后,删除不再需要的索引。

Shard filtering

对于基于时间的数据,最近的指数使用更频繁或更相关。换句话说,在给定时间,数据流将使用基于天、周或月的特定索引。该索引将成为所有新文档的目标,并且大多数 查询也将命中相同的索引。因此,为包含我们大量使用的索引的节点分配最佳资源是一个好主意。

这种方法的问题在于,Elasticsearch 很难识别我们为频繁使用的索引分配了哪个节点。为了让 Elasticsearch 了解哪个节点,我们可以在节点启动或启动时为其指定标签。这可以在 Elasticsearch 的配置文件中定义,如下所示:

./bin/elasticsearch –node.memory 16G

这里,memory 是任意键;我们可以给它任何我们想要的名字。这些键值对充当一组节点的标识符。因此,我们可以根据 best_node 键的不同值对不同的节点进行分组。具有相同内存值的所有节点都属于一组。使用这种方法,我们可以根据硬件手动对机器进行分组。

假设我们有名为 logstash-02-07-2015logstash-02-06-2015 的索引。假设第一个索引是我们当前的索引,我们需要分配具有最佳资源索引的节点。在这里,第二个索引 logstash-02-06-2015 是一个旧索引,我们暂时不会大量使用它。

以下是我们如何让 Elasticsearch 理解相同:

PUT / logstash-02-07-2015
{
  "settings": {
    "index.routing.allocation.include.memory ": "16G"
  }
}

在前面的 代码中,对于索引的创建,我们指定数据流到节点 具有 best_node" : 16G" 标签,这是我们最好的资源。

我们看到了如何将数据移动到最佳节点;现在,正如我们之前提到的,旧数据可能不太有用,我们可以看到旧索引如何移动到资源较少或中等资源的节点。

首先,定义节点的标签(这里,资源较少):

./bin/elasticsearch –node.memory 8G

然后,在我们的示例中,旧索引是 logstash-02-06-2015。现在更新该索引的设置,如下所示:

POST / logstash-02-06-2015/_settings
{
     "index.routing.allocation.include.memory" : "8G"
} 

现在,索引 logstash-02-06-2015 被移动到匹配 memory" : "8G" 标签的节点. 这样,当前的索引,即写重读和重读被分配给有的机器。

下图显示了分片分配中发生的情况的图形表示:

读书笔记《elasticsearch-blueprints》处理基于时间的数据

Running the optimized API on indices where writing is done

在时间数据索引中,将完成对先前索引的写入,并且大部分 是只读的。因此,将这些索引的多个段转换为单个段是有意义的。单个段具有资源利用率和性能的优势。优化单个段上的索引是一个好主意,但是,如果它是一个文档仍在流动的索引,那么这是一个坏主意。

现在,我们可以使用以下 HTTP 请求运行强制合并操作:

POST / logstash-02-06-2015/_optimize?max_num_segments=1 

如您所见,我们在前面的代码中使用了优化 API (_optimize)。优化 API 将强制分片将其段与我们分配给 max_num_segments 的数字合并。

在这种情况下,我们将要使用的最大段数定义为 1。一般的做法是将最大段数设置为1,因为这样会使用更少的资源,而且搜索操作也会快很多。

Closing older indices


随着时间的推移,驻留在旧索引中的文档将被利用到 级别,甚至可能无法访问。这些索引将使用我们的 CPU、主内存和其他资源,因为它们驻留在其节点的磁盘中。这种情况并不健康,因为我们只是将资源用于未使用或未访问的数据。 Elasticsearch 通过让我们“关闭”旧索引为我们提供了一种管理这种情况的方法。

关闭索引意味着该索引仍将保留在集群中,除了正在使用的磁盘空间之外没有任何资源。关闭索引的优点是重新打开它比从备份中恢复索引要容易得多。

我们可以使用 close API 关闭该索引:

POST / logstash-02-06-2015/_close

如前所述,指定的索引将存储在磁盘中,但所有的读/写操作都会被阻止。

如果我们想重新打开索引,我们可以使用 open API:

POST / logstash-02-06-2015/_open 

Snapshot creation and restoration of indices

会有非常旧的索引存储基于时间的数据,如果它们被 removed 来自我们的生态系统,以便我们可以利用它们占用的磁盘空间。这些索引可以单独保存在共享磁盘或其他一些第三方长期存储服务中,以备将来访问它们时使用。拥有此类备份的优点是数据永远不会真正丢失,因为我们可以从我们拥有的备份中恢复这些索引。

Elasticsearch 为前面提到的目的提供了快照恢​​复 API。让我们看看如何创建快照。

Repository creation

在创建快照之前,我们需要在 Elasticsearch 中注册一个存储库。这个可以如下做:

PUT/_snapshot/index_backup
{
  "type": "fs",
  "settings": {
    "compress": "true",
    "location": "/mount/backups"
  }
}

在这里,我们可以看到使用值 fs 定义的 type 字段。这意味着我们正在文件系统中创建存储库。如果我们使用外部存储插件,例如 AWS,类型值应该作为 s3 提供,并且设置也会相应地改变。 AWS 快照存储库的示例是:

PUT /_snapshot/backup_s3
{
  "type": "s3",
  "settings": {
    "bucket": "s3_backup",
    "region": "eu-west-1"
  }
}

Snapshot creation


我们在上一节看到了一个仓库的创建和注册;现在,我们可以继续创建快照了。

index_backup 存储库中创建一个名为 snap01 的快照可以通过以下命令完成:

PUT /_snapshot/index_backup/snap01

我们还可以将 wait_for_completion 参数与前面的命令一起使用,如下所示:

PUT /_snapshot/index_backup/snap01?wait_for_completion=true

wait_for_completion 参数如果设置为 true,将等到创建快照,然后 只有请求会返回。而 false 值将立即返回请求,同时允许在后台完成快照创建。 wait_for_completion 的默认值被视为 false,即使在创建快照时未提及该参数。

Snapshot creation on specific indices

当我们创建 快照时,默认情况下,它会备份集群中所有启动和运行的索引。在某些情况下,这种行为可能不太好,因为我们需要备份特定索引。这可以通过以下命令完成:

PUT /_snapshot/index_backup/snap01
{
  "indices": "logstash-20-06-2015,logstash-20-05-2015",
  "ignore_unavailable": "true"
}

在这里,快照 snap01 将包含索引 logstash-20-06-2015logstash-20-05-2015

ignore_unavailable 参数设置为 true 时,将在创建快照时忽略索引/索引的不可用性。

Restoring a snapshot


我们 创建的名为snap01 的快照可以通过以下命令恢复:

POST /_snapshot/index_backup/snap01/_restore 

默认情况下,快照中的所有索引都是通过该方法恢复的。

Restoring multiple indices


还支持选择性索引的替换,如下所示:

POST /_snapshot/index_backup/snap01/_restore
{
  "indices": " logstash-20-06-2015,logstash-20-05-2015",
  "ignore_unavailable": "true"
}

现有索引只有在关闭并且与快照中的索引具有相同数量的分片时才能恢复。

The curator


Curator 是由 Elasticsearch 社区用 Python 开发的工具,用于在 Elasticsearch 中管理或管理 基于时间的索引。 curator 的主要目的是对基于时间的建模索引进行操作。比如说,我们想要优化所有比今天更早的索引。我们将需要查询并了解哪些所有索引都处于这种情况下,然后,我们需要为每个索引运行命令。这可能是一个耗时的过程。为此,我们可以使用 curator 并要求它为我们执行基于时间的索引的任务。例如,我们可以告诉 curator 删除/优化超过一天的索引。

Curator 了解各种基于时间的索引名称格式,一旦它们作为命令行参数给出。使用相同的,您可以发出各种命令。

通过使用curator,我们可以对索引进行前面提到的操作,比如:

  • 分片分配

  • 指数的开市和收市

  • 优化

  • 快照创建

Shard allocation using curator


如前所述,将好机器分配到所需索引的任务可以使用 curator 工具,如下图:

curator  --host <IP> allocation --rule memory=16G indices  --timestring '%Y-%M-%D' --prefix "logstash-" --newer-than 2 --time-unit days

在这里,我们告诉 curator 将所有比过去 2 天更新的索引移动到内存属性为 16G 值的节点。通过这个过程,最新的索引将被移动到更好的硬件上,如下所示:

curator  --host <IP> allocation --rule ram=8G indices   --timestring '%Y-%M-%D' --prefix "logstash-"  --older-than  2 --time-unit days

我们还需要运行前面的命令以确保将其余索引移动到只有 8GB 主内存的普通节点。

Opening and closing of indices

索引的打开或关闭也可以使用curator来完成。这方面的一个例子是:

curator  --host <IP> close   --timestring '%Y-%M-%D' --prefix "logstash-"  --older-than  10 --time-unit days

在这里,我们将关闭所有超过 10 天的索引,这样它们就不会占用太多的硬件资源。

Optimization


可以将前缀为 logstash- 的所有索引优化或强制合并为单个 shard使用以下命令:

curator --host 10<IP> optimize --max_num_segments 1 -prefix "logstash-"  --older-than  1 --time-unit days

在这里,我们为比今天更早的所有索引设置了一些段。

Summary


在本章中,我们看到了在索引时间数据时正常创建索引所带来的问题。您还学习了如何通过创建模板来创建索引并覆盖索引的默认映射和设置来克服这些困难。

我们还看到了如何使选择性索引包含在查询中,并了解了这种功能的好处。我们也熟悉了一些处理旧时数据的方法,如下:

  • 索引迁移

  • 分片分配

  • 优化索引

  • 指数收盘

  • 创建索引快照

在本章中,我们看到了为 Elasticsearch 中的索引管理开发的 curator 工具,它可以用来完成前面的大部分操作。