ElasticSearch【有与无】【搜索引擎】【ES29】扩容设计【选读】

目录

1.简介

1.1.扩容的单元

1.2.分片预分配

1.3.海量分片

1.4.容量规划

1.5.副本分片

1.6.通过副本进行负载均衡

1.7.多索引

1.8.基于时间的数据

1.9.按时间范围索引

1.10.索引模板

1.11.数据过期

1.12.迁移旧索引

1.12.1.索引优化(Optimize)

1.12.2.关闭旧索引

1.12.3.归档旧索引

1.13.基于用户的数据

1.14.共享索引

1.15.利用别名实现一个用户一个索引

1.16.一个大的用户

1.17.扩容并不是无限的


1.简介

Elasticsearch 为了可扩展性而生。它可以良好地运行于你的笔记本电脑又或者一个拥有数百节点的集群,同时用户体验基本相同。由小规模集群增长为大规模集群的过程几乎完全自动化并且无痛。由大规模集群增长为超大规模集群需要一些规划和设计,但还是相对地无痛。

常见的数据流:

  • 时序数据(时间驱动相关性,例如日志或社交网络数据流)
  • 基于用户的数据(拥有很大的文档集但可以按用户或客户细分)

1.1.扩容的单元

一个分片即一个 Lucene 索引 ,一个 Elasticsearch 索引即一系列分片的集合。 应用程序与索引进行交互,Elasticsearch 将请求路由至相应的分片。

一个分片即为 扩容的单元 。一个最小的索引拥有一个分片。 这可能已经完全满足你的需求了 — 单个分片即可存储大量的数据 — 但这限制了你的可扩展性。

【举例】集群由一个节点组成,在集群内拥有一个索引,这个索引只含一个分片

PUT /my_index
{
  "settings": {
    "number_of_shards":   1,  // 创建一个拥有 1 主分片 0 个副本分片的索引
    "number_of_replicas": 0
  }
}

【说明】

这个设置值也许很小,但它满足当前的需求而且运行代价低。

决定根据 一个只有一个分片的索引无扩容因子 添加一个节点。这将会发生什么呢?

答案是:什么都不会发生。

因为只有一个分片,已经没有什么可以放在第二个节点上的了。 不能增加索引的分片数因为它是 route documents to shards 算法中的重要元素: shard = hash(routing) % number_of_primary_shards

当前的选择只有一个就是将数据重新索引至一个拥有更多分片的一个更大的索引,但这样做将消耗的时间是我们无法提供的。 通过事先规划,使用 预分配 的方式来完全避免这个问题。

 

1.2.分片预分配

一个分片存在于单个节点,但一个节点可以持有多个分片。

PUT /my_index
{
  "settings": {
    "number_of_shards":   2,  // 创建拥有两个主分片无副本分片的索引
    "number_of_replicas": 0
  }
}

当只有一个节点时,两个分片都将被分配至相同的节点。 从我们应用程序的角度来看,一切都和之前一样运作着。应用程序和索引进行通讯,而不是分片,现在还是只有一个索引。

这时,加入第二个节点,Elasticsearch 会自动将其中一个分片移动至第二个节点,如 一个拥有两个分片的索引可以利用第二个节点 描绘的那样, 当重新分配完成后,每个分片都将接近至两倍于之前的计算能力。

现在已经可以通过简单地将一个分片通过网络复制到一个新的节点来加倍我们的处理能力,并且零停机地做到了这一点。在分片移动过程中,所有的索引搜索请求均在正常运行。

在 Elasticsearch 中新添加的索引默认被指定了五个主分片。 这意味着我们最多可以将那个索引分散到五个节点上,每个节点一个分片。 它具有很高的处理能力,还未等你去思考这一切就已经做到了!

分片分裂

为什么 Elasticsearch 不支持 分片分裂(shard-splitting)— 将每个分片分裂为两个或更多部分的能力。 
原因就是分片分裂是一个糟糕的想法:

分裂一个分片几乎等于重新索引你的数据。它是一个比仅仅将分片从一个节点复制到另一个节点更重量级的操作。

分裂是指数的。起初你你有一个分片,然后分裂为两个,然后四个,八个,十六个,等等。分裂并不会刚好地把你的处理能力提升 50%。

分片分裂需要你拥有足够的能力支撑另一份索引的拷贝。通常来说,当你意识到你需要横向扩展时,你已经没有足够的剩余空间来做分裂了。

Elasticsearch 通过另一种方式来支持分片分裂。你总是可以把你的数据重新索引至一个拥有适当分片个数的新索引(参阅 [reindex])。 
和移动分片比起来这依然是一个更加密集的操作,依然需要足够的剩余空间来完成,但至少你可以控制新索引的分片个数了。

1.3.海量分片

【缺点】

  • 一个分片的底层即为一个 Lucene 索引,会消耗一定文件句柄、内存、以及 CPU 运转。
  • 每一个搜索请求都需要命中索引中的每一个分片,如果每一个分片都处于不同的节点还好, 但如果多个分片都需要在同一个节点上竞争使用相同的资源就有些糟糕了。
  • 用于计算相关度的词项统计信息是基于分片的。如果有许多分片,每一个都只有很少的数据会导致很低的相关度。
适当的预分配是好的。但上千个分片就有些糟糕。
我们很难去定义分片是否过多了,这取决于它们的大小以及如何去使用它们。
 一百个分片但很少使用还好,两个分片但非常频繁地使用有可能就有点多了。 
监控节点保证它们留有足够的空闲资源来处理一些特殊情况。

【建议】

适当的预分配是好的。但上千个分片就有些糟糕。我们很难去定义分片是否过多了,这取决于它们的大小以及如何去使用它们。 一百个分片但很少使用还好,两个分片但非常频繁地使用有可能就有点多了。 监控你的节点保证它们留有足够的空闲资源来处理一些特殊情况。

 

1.4.容量规划

影响因素:使用的硬件、文档的大小和复杂度、文档的索引分析方式、运行的查询类型、执行的聚合以及你的数据模型等等。

在特定场景下:

  1. 基于准备用于生产环境的硬件创建一个拥有单个节点的集群。
  2. 创建一个和准备用于生产环境相同配置和分析器的索引,但让它只有一个主分片无副本分片。
  3. 索引实际的文档(或者尽可能接近实际)。
  4. 运行实际的查询和聚合(或者尽可能接近实际)。

基本来说,你需要复制真实环境的使用方式并将它们全部压缩到单个分片上直到它``挂掉。'' 实际上 挂掉 的定义也取决于你:一些用户需要所有响应在 50 毫秒内返回;另一些则乐于等上 5 秒钟。

一旦定义好了单个分片的容量,很容易就可以推算出整个索引的分片数。 用需要索引的数据总数加上一部分预期的增长,除以单个分片的容量,结果就是需要的主分片个数。

容量规划不应当作为你的第一步。

先看看有没有办法优化你对 Elasticsearch 的使用方式。也许你有低效的查询,缺少足够的内存,又或者你开启了 swap?

我们见过一些新手对于初始性能感到沮丧,立即就着手调优垃圾回收又或者是线程数,而不是处理简单问题例如去掉通配符查询。

1.5.副本分片

副本分片的主要目的就是为了故障转移。如果持有主分片的节点挂掉了,一个副本分片就会晋升为主分片的角色。

在索引写入时,副本分片做着与主分片相同的工作。新文档首先被索引进主分片然后再同步到其它所有的副本分片。增加副本数并不会增加索引容量。

副本分片可以服务于读请求,如果索引也如常见的那样是偏向查询使用的,那可以通过增加副本的数目来提升查询性能,但也要为此 增加额外的硬件资源

通过增加第二个节点来提升索引容量。 增加额外的节点不会帮助我们提升索引写入能力,但也可以通过增加副本数在搜索时利用额外的硬件:

PUT /my_index/_settings
{
  "number_of_replicas": 1
}

拥有两个主分片,加上每个主分片的一个副本,总共给予我们四个分片:每个节点一个,一个拥有两个主分片一份副本的索引可以在四个节点中横向扩展。

 

1.6.通过副本进行负载均衡

搜索性能取决于最慢的节点的响应时间,所以尝试均衡所有节点的负载是一个好想法。 如果只是增加一个节点而不是两个,最终我们会有两个节点各持有一个分片,而另一个持有两个分片做着两倍的工作。

可以通过调整副本数量来平衡这些。通过分配两份副本而不是一个,最终会拥有六个分片,刚好可以平均分给三个节点

PUT /my_index/_settings
{
  "number_of_replicas": 2
}

作为奖励,我们同时提升了我们的可用性。我们可以容忍丢失两个节点而仍然保持一份完整数据的拷贝。

 

1.7.多索引

记住没有任何规则限制你的应用程序只使用一个索引。 当发起一个搜索请求时,它被转发至索引中每个分片的一份拷贝(一个主分片或一个副本分片),如果向多个索引发出同样的请求,会发生完全相同的事情——只不过会涉及更多的分片

搜索 1 个有着 50 个分片的索引与搜索 50 个每个都有 1 个分片的索引完全等价:搜索请求均命中 50 个分片。

当需要在不停服务的情况下增加容量时,下面有一些有用的建议。相较于将数据迁移到更大的索引中,可以仅仅做下面这些操作:

  • 创建一个新的索引来存储新的数据。
  • 同时搜索两个索引来获取新数据和旧数据。

实际上,通过一点预先计划,添加一个新索引可以通过一种完全透明的方式完成,应用程序根本不会察觉到任何的改变。

 

使用索引别名来指向当前版本的索引:

【举例】给你的索引命名为 tweets_v1 而不是 tweets 。你的应用程序会与 tweets 进行交互,但事实上它是一个指向 tweets_v1 的别名。 这允许你将别名切换至一个更新版本的索引而保持服务运转。

 

可以使用一个类似的技术通过增加一个新索引来扩展容量。这需要一点点规划,因为你需要两个别名:一个用于搜索另一个用于索引数据:

PUT /tweets_1/_alias/tweets_search 
PUT /tweets_1/_alias/tweets_index 

当需要额外容量时,可以创建一个名为 tweets_2 的索引,并且像这样更新别名:

POST /_aliases
{
  "actions": [
    { "add":    { "index": "tweets_2", "alias": "tweets_search" }},  //  添加索引 tweets_2 到别名 tweets_search 。
    { "remove": { "index": "tweets_1", "alias": "tweets_index"  }}, 
    { "add":    { "index": "tweets_2", "alias": "tweets_index"  }}  // 将别名 tweets_index 由 tweets_1 切换至 tweets_2 
  ]
}

一个搜索请求可以以多个索引为目标,所以将搜索别名指向 tweets_1 以及 tweets_2 是完全有效的。 然而,索引写入请求只能以单个索引为目标。因此,必须将索引写入的别名只指向新的索引。

一个文档 GET 请求,像一个索引写入请求那样,只能以单个索引为目标。 这导致在通过ID获取文档这样的场景下有一点复杂。
作为代替,可以对 tweets_1 以及 tweets_2 运行一个 [ids 查询] 搜索请求, 或者[multi-get] 请求。

在服务运行中使用多索引来扩展索引容量对于一些使用场景有着特别的好处

 

1.8.基于时间的数据

日志记录

Elasticsearch 提供了一个集成的日志平台叫做 ELK stack— Elasticsearch,Logstash,以及 Kibana ——来让这项工作变得简单。

Logstash 采集、解析日志并在将它们写入Elasticsearch之前格式化。

Elasticsearch 扮演了一个集中式的日志服务角色,

Kibana 是一个 图形化前端可以很容易地实时查询以及可视化你的网络变化。

搜索引擎中大多数使用场景都是增长缓慢相对稳定的文档集合。搜索查找最相关的文档,而不关心它是何时创建的。

 

1.9.按时间范围索引

按时间范围索引需要的数据,删除无用的数据。删除旧数据十分简单:只需要删除旧的索引。

【优点】

允许在需要的时候进行扩容。

不需要预先做任何艰难的决定。

别名可以更加透明地在索引间切换。 

POST /_aliases
{
  "actions": [
    { "add":    { "alias": "logs_current",  "index": "logs_2014-10" }}, 
    { "remove": { "alias": "logs_current",  "index": "logs_2014-09" }}, // 将 logs_current 由九月切换至十月。
    { "add":    { "alias": "last_3_months", "index": "logs_2014-10" }}, 
    { "remove": { "alias": "last_3_months", "index": "logs_2014-07" }}  // 将十月添加到 last_3_months 并且删掉七月。
  ]
}

1.10.索引模板

Elasticsearch 不要求你在使用一个索引前创建它。 对于日志记录类应用,依赖于自动创建索引比手动创建要更加方便.

Logstash 使用事件中的时间戳来生成索引名。 默认每天被索引至不同的索引中,因此一个 @timestamp 为 2019-10-01 00:00:01 的事件将被发送至索引 logstash-2019.10.01 中。 如果那个索引不存在,它将被自动创建。

[举例]

通常我们想要控制一些新建索引的设置(settings)和映射(mappings)。也许我们想要限制分片数为 1 ,并且禁用 _all 域。 索引模板可以用于控制何种设置(settings)应当被应用于新创建的索引

PUT /_template/my_logs     // 创建一个名为 my_logs 的模板。
{
  "template": "logstash-*",   // 将这个模板应用于所有以 logstash- 为起始的索引
  "order":    1,    // 这个模板将会覆盖默认的 logstash 模板,因为默认模板的 order 更低
  "settings": {
    "number_of_shards": 1  // 限制主分片数量为 1
  },
  "mappings": {
    "_default_": {  // 为所有类型禁用 _all 域
      "_all": {
        "enabled": false
      }
    }
  },
  "aliases": {
    "last_3_months": {}    // 添加这个索引至 last_3_months 别名中
  }
}

这个模板指定了所有名字以 logstash- 为起始的索引的默认设置,不论它是手动还是自动创建的。 如果我们认为明天的索引需要比今天更大的容量,我们可以更新这个索引以使用更多的分片。

这个模板还将新建索引添加至了 last_3_months 别名中,然而从那个别名中删除旧的索引则需要手动执行。

 

1.11.数据过期

按时间范围索引带来的一个好处是可以方便地删除旧数据:只需要删除那些变得不重要的索引就可以了。

DELETE /logs_2013*

删除整个索引比删除单个文档要更加高效:Elasticsearch 只需要删除整个文件夹。

但是删除索引是 终极手段 。在我们决定完全删除它之前还有一些事情可以做来帮助数据更加优雅地过期。

 

1.12.迁移旧索引

随着数据被记录,很有可能存在一个 热点 索引——今日的索引。 所有新文档都会被加到那个索引,几乎所有查询都以它为目标。那个索引应当使用你最好的硬件。

Elasticsearch 是如何得知哪台是你最好的服务器呢?你可以通过给每台服务器指定任意的标签来告诉它。

例如,你可以像这样启动一个节点:    ./bin/elasticsearch --node.box_type strong

box_type 参数是完全随意的——你可以将它随意命名只要你喜欢——但你可以用这些任意的值来告诉 Elasticsearch 将一个索引分配至何处。

我们可以通过按以下配置创建今日的索引来确保它被分配到我们最好的服务器上:

PUT /logs_2014-10-01
{
  "settings": {
    "index.routing.allocation.include.box_type" : "strong"
  }
}

昨日的索引不再需要我们最好的服务器了,我们可以通过更新索引设置将它移动到标记为 medium 的节点上:

POST /logs_2014-09-30/_settings
{
  "index.routing.allocation.include.box_type" : "medium"
}

1.12.1.索引优化(Optimize)

昨日的索引不大可能会改变。 日志事件是静态的:已经发生的过往不会再改变了。如果我们将每个分片合并至一个段(Segment),它会占用更少的资源更快地响应查询。

对还分配在 strong 主机上的索引进行优化(Optimize)操作将会是一个糟糕的想法, 因为优化操作将消耗节点上大量 I/O 并对索引今日日志造成冲击。但是 medium 的节点没有做太多类似的工作,可以安全地在上面进行优化。

昨日的索引有可能拥有副本分片。如果下发一个优化(Optimize)请求, 它会优化主分片和副本分片,这有些浪费。然而,可以临时移除副本分片,进行优化,然后再恢复副本分片:

POST /logs_2014-09-30/_settings
{ "number_of_replicas": 0 }

POST /logs_2014-09-30/_optimize?max_num_segments=1

POST /logs_2014-09-30/_settings
{ "number_of_replicas": 1 }

当然,没有副本将面临磁盘故障而导致丢失数据的风险。可以先备份数据。

 

1.12.2.关闭旧索引

当索引变得更“老”,它们到达一个几乎不会再被访问的时间点。 我们可以在这个阶段删除它们,但也许你想将它们留在这里以防万一有人在半年后还想要访问它们。

这些索引可以被关闭。它们还会存在于集群中,但它们不会消耗磁盘空间以外的资源。重新打开一个索引要比从备份中恢复快得多。

在关闭之前,值得我们去刷写索引来确保没有事务残留在事务日志中。一个空白的事务日志会使得索引在重新打开时恢复得更快:

POST /logs_2014-01-*/_flush   // 刷写(Flush)所有一月的索引来清空事务日志。
POST /logs_2014-01-*/_close  // 关闭所有一月的索引
POST /logs_2014-01-*/_open  // 当需要再次访问它们时,使用 open API 来重新打开它们

 

1.12.3.归档旧索引

最后,非常旧的索引可以通过[snapshot-restore API]归档至长期存储例如共享磁盘或者 Amazon S3,以防日后你可能需要访问它们。 当存在备份时就可以将索引从集群中删除了。

 

1.13.基于用户的数据

Elasticsearch 支持[多租户]所以每个用户可以在相同的集群中拥有自己的索引。 

有人偶尔会想要搜索所有用户的文档,这种情况可以通过搜索所有索引实现,但大多数情况下用户只关心它们自己的文档。

不要为每个索引都使用默认的主分片数。想想看它需要存储多少数据。有可能你仅需要一个分片——再多的都只是浪费资源。

大多数 Elasticsearch 的用户读到这里就已经够了。简单的“一个用户一个索引”对大多数场景都可以满足了。

我们需要的是一种可以在用户间共享资源的方法,给每个用户他们拥有自己的索引这种印象,而不在小用户上浪费资源。

 

1.14.共享索引

PUT /forums
{
  "settings": {
    "number_of_shards": 10 // 创建一个足够大的索引来存储数千个小论坛的数据
  },
  "mappings": {
    "post": {
      "properties": {
        "forum_id": {  // 每个帖子都必须包含一个 forum_id 来标识它属于哪个论坛
          "type":  "string",
          "index": "not_analyzed"
        }
      }
    }
  }
}

PUT /forums/post/1
{
  "forum_id": "baking",  // 每个帖子都必须包含一个 forum_id 来标识它属于哪个论坛
  "title":    "Easy recipe for ginger nuts",
  ...
}

排除索引中绝大部分的数据(属于其它论坛的数据),缓存会保证快速的响应

GET /forums/post/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "title": "ginger nuts"
        }
      },
      "filter": {
        "term": {
          "forum_id": {
            "baking"
          }
        }
      }
    }
  }
}

这个办法行得通,但我们可以做得更好。 来自于同一个论坛的帖子可以简单地容纳于单个分片,但它们现在被打散到了这个索引的所有十个分片中。 这意味着每个搜索请求都必须被转发至所有十个分片的一个主分片或者副本分片。 如果能够保证所有来自于同一个论坛的所有帖子都被存储于同一个分片可能会是个好想法。

通过使用如下公式来分配到一个指定分片:shard = hash(routing) % number_of_primary_shards

routing 的值默认为文档的 _id ,但我们可以覆盖它并且提供我们自己自定义的路由值,例如 forum_id 。 所有有着相同 routing 值的文档都将被存储于相同的分片:

PUT /forums/post/1?routing=baking 
{
  "forum_id": "baking", 
  "title":    "Easy recipe for ginger nuts",
  ...
}

指定单分片

GET /forums/post/_search?routing=baking 
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "title": "ginger nuts"
        }
      },
      "filter": {
        "term": { 
          "forum_id": {
            "baking"
          }
        }
      }
    }
  }
}

指定多分片

GET /forums/post/_search?routing=baking,cooking,recipes
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "title": "ginger nuts"
        }
      },
      "filter": {
        "terms": {
          "forum_id": {
            [ "baking", "cooking", "recipes" ]
          }
        }
      }
    }
  }
}

这种方式从技术上来说比较高效,由于要为每一个查询或者索引请求指定 routing 和 terms 的值看起来有一点的笨拙。 索引别名可以帮你解决这些!

 

1.15.利用别名实现一个用户一个索引

当你将一个别名与一个索引关联起来,你可以指定一个过滤器和一个路由值

PUT /forums/_alias/baking
{
  "routing": "baking",
  "filter": {
    "term": {
      "forum_id": "baking"
    }
  }
}

PUT /baking/post/1 
{
  "forum_id": "baking",  // 需要为过滤器指定 forumn_id 字段,但自定义路由值已经是隐含的了。
  "title":    "Easy recipe for ginger nuts",
  ...
}

对 baking 别名上的查询只会在自定义路由值关联的分片上运行,并且结果也自动按照我们指定的过滤器进行了过滤

GET /baking/post/_search
{
  "query": {
    "match": {
      "title": "ginger nuts"
    }
  }
}

当对多个论坛进行搜索时可以指定多个别名:

GET /baking,recipes/post/_search  // 两个 routing 的值都会应用,返回对结果会匹配任意一个过滤器。
{
  "query": {
    "match": {
      "title": "ginger nuts"
    }
  }
}

1.16.一个大的用户

大规模流行论坛都是从小论坛起步的。 有一天我们会发现我们共享索引中的一个分片要比其它分片更加繁忙,因为这个分片中一个论坛的文档变得更加热门。 这时,那个论坛需要属于它自己的索引。

我们用来提供一个用户一个索引的索引别名给了我们一个简洁的迁移论坛方式。

第一步就是为那个论坛创建一个新的索引,并为其分配合理的分片数,可以满足一定预期的数据增长:

PUT /baking_v1
{
  "settings": {
    "number_of_shards": 3
  }
}

第二步就是将共享的索引中的数据迁移到专用的索引中,可以通过scroll查询和bulk API来实现。 当迁移完成时,可以更新索引别名指向那个新的索引:

POST /_aliases
{
  "actions": [
    { "remove": { "alias": "baking", "index": "forums"    }},
    { "add":    { "alias": "baking", "index": "baking_v1" }}
  ]
}

更新索引别名的操作是原子性的;

专用的索引不再需要过滤器或者自定义的路由值了。我们可以依赖于 Elasticsearch 默认使用的 _id 字段来做分区。

最后一步是从共享的索引中删除旧的文档,可以通过搜索之前的路由值以及论坛 ID 然后进行批量删除操作来实现。

一个用户一个索引模型的优雅之处在于它允许你减少资源消耗,保持快速的响应时间,同时拥有在需要时零宕机时间扩容的能力。

 

1.17.扩容并不是无限的

集群状态 是一种数据结构,贮存下列集群级别的信息:

  • 集群级别的设置
  • 集群中的节点
  • 索引以及它们的设置、映射、分析器、预热器(Warmers)和别名
  • 与每个索引关联的分片以及它们分配到的节点

查看当前的集群状态

GET /_cluster/state

集群状态存在于集群中的每个节点,包括客户端节点。 这就是为什么任何一个节点都可以将请求直接转发至被请求数据的节点——每个节点都知道每个文档应该在哪里。

只有主节点被允许更新集群状态。索引请求引入了一个之前未知的字段。持有那个文档的主分片所在的节点必须将新的映射转发到主节点上。 主节点把更改合并到集群状态中,然后向所有集群中的所有节点发布一个新的版本。

搜索请求 使用 集群状态,但它们不会产生修改。同样,文档级别的增删改查请求也不会对集群状态产生修改。当然,除非它们引入了一个需要更新映射的新的字段了。 总的来说,集群状态是静态的不会成为瓶颈。

然而,需要记住的是相同的数据结构需要在每个节点的内存中保存,并且当它发生更改时必须发布到每一个节点。 集群状态的数据量越大,这个操作就会越久。

 

一个用户可能会决定为每一个 IP 地址或者每个 referer URL 使用一个单独的字段。 下面这个例子通过为每一个唯一的 referer 使用一个不同的字段名来保持对页面浏览量的计数:

POST /counters/pageview/home_page/_update
{
  "script": "ctx._source[referer]++",
  "params": {
    "referer": "http://www.foo.com/links?bar=baz"
  }
}

这种方式十分的糟糕!它会生成数百万个字段,这些都需要被存储在集群状态中。 每当见到一个新的 referer ,都有一个新的字段需要加入那个已经膨胀的集群状态中,这都需要被发布到集群的每个节点中去。

更好的方式是使用nested objects, 它使用一个字段作为参数名—`referer`—另一个字段作为关联的值—`count` :

 "counters": [
      { "referer": "http://www.foo.com/links?bar=baz",  "count": 2 },
      { "referer": "http://www.linkbait.com/article_3", "count": 10 },
      ...
    ]

这种嵌套的方式有可能会增加文档数量,但 Elasticsearch 生来就是为了解决它的。重要的是保持集群状态小而敏捷。

最终,不管初衷有多好,可能都会发现集群节点数量、索引、映射对于一个集群来说还是太大了。 此时,可能有必要将这个问题拆分到多个集群中了。 甚至可以向多个集群发出搜索请求,就好像一个巨大的集群那样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴 韵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值