WEBKT

告别慢查询:Elasticsearch 中禁用 _source 字段与 stored_fields 的高效实践

2 0 0 0

1. 为什么 _source 会成为性能瓶颈?

2. 禁用 _source 的好处

3. stored_fields 的作用

4. 如何禁用 _source 并使用 stored_fields?

4.1 创建索引并禁用 _source

4.2 索引文档

4.3 查询数据

5. 深入分析与优化策略

5.1 数据建模

5.2 性能测试

5.3 索引优化

5.4 硬件配置

6. 总结

7. 进阶技巧:使用 includes 和 excludes 精细控制 _source (可选)

7.1 includes 参数

7.2 excludes 参数

7.3 总结

8. 案例分析:日志分析场景

9. 总结与建议

你好,我是老码农。在 Elasticsearch 的世界里,性能优化是一个永恒的话题。今天,我将和你分享一个能显著提升查询效率的技巧:禁用 _source 字段,并结合使用 stored_fields。这个方法尤其适用于那些对性能有极致要求的场景,例如日志分析、监控系统,或者任何需要快速检索大量数据的应用。

1. 为什么 _source 会成为性能瓶颈?

在 Elasticsearch 中,_source 字段存储了文档的原始 JSON 数据。这意味着每次查询时,Elasticsearch 都需要从磁盘中读取这个完整的 JSON 数据,即使你只需要其中的几个字段。这就像你每次取东西都要把整个箱子搬出来,即使你只需要其中的一个小物件。

想象一下,你有一个包含上百万文档的索引,每个文档的 _source 都很大。频繁的查询操作会导致大量的磁盘 I/O,进而导致查询速度变慢。尤其是在高并发的场景下,这种性能瓶颈会被进一步放大。

2. 禁用 _source 的好处

禁用 _source 字段,意味着 Elasticsearch 在索引文档时不会存储原始 JSON 数据。这有以下几个好处:

  • 减少磁盘空间占用:不存储 _source 字段可以显著减少索引的体积,从而节省存储空间。
  • 提高索引速度:索引过程无需存储完整的 JSON 数据,可以加快索引速度。
  • 提升查询性能:由于不需要读取 _source 字段,查询速度会得到提升。 Elasticsearch 可以直接从索引中获取所需的字段,减少了磁盘 I/O。

当然,禁用 _source 也有一些缺点

  • 无法直接获取完整的文档数据:如果你的应用程序需要获取完整的文档数据,那么禁用 _source 字段是不合适的。
  • 调试困难:在某些情况下,需要查看原始的 JSON 数据进行调试,禁用 _source 会带来不便。

3. stored_fields 的作用

stored_fields 允许你选择性地存储文档中的某些字段。你可以将需要查询和展示的字段存储在 stored_fields 中。这样,在查询时,Elasticsearch 只需要从 stored_fields 中读取数据,而不需要读取完整的 _source 字段。这是一种在性能和功能之间取得平衡的策略。

4. 如何禁用 _source 并使用 stored_fields

下面,我将通过一个实际的例子,演示如何在 Elasticsearch 中禁用 _source 字段并使用 stored_fields

4.1 创建索引并禁用 _source

首先,我们需要创建一个索引,并在索引的设置中禁用 _source 字段。可以通过以下方式实现:

PUT /my_index
{
"settings": {
"index": {
"_source": {
"enabled": false
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"store": true // 将 title 字段存储,相当于设置了 stored_fields
},
"content": {
"type": "text"
},
"author": {
"type": "keyword",
"store": true // 将 author 字段存储,相当于设置了 stored_fields
},
"timestamp": {
"type": "date"
}
}
}
}

在这个例子中,我们创建了一个名为 my_index 的索引。在 settings 中,我们设置 _source.enabledfalse,禁用了 _source 字段。在 mappings 中,我们定义了几个字段:titlecontentauthortimestamp。注意,titleauthor 字段的 store 属性设置为 true,这意味着它们将被存储在 stored_fields 中。对于 Elasticsearch 7.x 及以上版本,store 属性已经被 stored_fields 替代,虽然使用 store 仍然可以达到同样的效果,但是官方建议使用 stored_fields

4.2 索引文档

接下来,我们需要向索引中添加一些文档:

POST /my_index/_doc/
{
"title": "Elasticsearch 性能优化",
"content": "本文介绍了如何在 Elasticsearch 中优化查询性能...",
"author": "老码农",
"timestamp": "2024-01-01T00:00:00Z"
}
POST /my_index/_doc/
{
"title": "Elasticsearch 进阶教程",
"content": "深入理解 Elasticsearch 的内部机制...",
"author": "老码农",
"timestamp": "2024-01-02T00:00:00Z"
}

在这个例子中,我们索引了两个文档。注意,由于我们禁用了 _source 字段,Elasticsearch 不会存储完整的 JSON 数据。但是,由于我们设置了 titleauthor 字段的 store 属性为 true,所以它们的值会被存储在 stored_fields 中。

4.3 查询数据

现在,我们可以执行查询操作。由于我们禁用了 _source 字段,我们需要使用 stored_fields 来获取数据。例如,我们可以查询标题为“Elasticsearch 性能优化”的文档,并获取其 titleauthor 字段:

GET /my_index/_search
{
"query": {
"match": {
"title": "Elasticsearch 性能优化"
}
},
"stored_fields": ["title", "author"]
}

在这个例子中,我们使用 match 查询来查找标题为“Elasticsearch 性能优化”的文档。在 stored_fields 中,我们指定了需要获取的字段:titleauthor。 Elasticsearch 将会返回这两个字段的值,而不会返回完整的 _source 字段。

查询结果如下:

{
"took": 2,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.2876821,
"hits": [
{
"_index": "my_index",
"_id": "_vjC64sBhX5nJ_zT_W1g",
"_score": 0.2876821,
"fields": {
"author": [
"老码农"
],
"title": [
"Elasticsearch 性能优化"
]
}
}
]
}
}

可以看到,查询结果只包含了 titleauthor 字段的值,而没有包含 _source 字段。这验证了我们禁用 _source 并使用 stored_fields 的配置是有效的。

5. 深入分析与优化策略

5.1 数据建模

在决定是否禁用 _source 并使用 stored_fields 之前,你需要仔细考虑你的数据模型。确定哪些字段是需要查询的,哪些字段是需要展示的。将这些字段存储在 stored_fields 中,可以最大限度地提高查询效率。

对于不需要查询或展示的字段,可以不进行存储。这样可以进一步减少索引的体积,提高索引速度。

5.2 性能测试

在实际应用中,你需要进行充分的性能测试,以评估禁用 _source 并使用 stored_fields 带来的性能提升。你可以使用 Elasticsearch 的性能测试工具,例如 Rally,来模拟真实的用户场景,并测量查询的响应时间、吞吐量等指标。

比较禁用 _source 前后,以及使用 stored_fields 前后的性能差异。根据测试结果,调整你的配置,找到最佳的性能优化方案。

5.3 索引优化

除了禁用 _source 和使用 stored_fields 之外,还有其他一些索引优化的技巧,可以进一步提升 Elasticsearch 的性能:

  • 选择合适的字段类型:根据数据的类型,选择合适的字段类型。例如,对于日期时间数据,使用 date 类型;对于精确的数值,使用 keyword 类型;对于需要全文搜索的文本,使用 text 类型。
  • 使用合适的分词器:根据你的应用场景,选择合适的分词器。例如,对于中文文本,可以使用 ik_max_wordik_smart 分词器。
  • 调整分片和副本的数量:合理地配置分片和副本的数量,可以提高索引的吞吐量和查询的可用性。分片的数量应该根据你的数据量和集群规模进行调整。副本的数量可以根据你的数据冗余需求进行调整。
  • 使用缓存: Elasticsearch 提供了多种缓存机制,例如节点缓存、查询缓存等。合理地配置缓存,可以减少磁盘 I/O,提高查询速度。

5.4 硬件配置

除了软件层面的优化,硬件配置也会对 Elasticsearch 的性能产生影响。以下是一些建议:

  • 使用 SSD 存储:SSD 具有比 HDD 更快的读写速度,可以显著提高索引和查询的性能。
  • 增加内存:为 Elasticsearch 节点分配足够的内存,可以提高缓存的命中率,减少磁盘 I/O。
  • 优化网络:使用高速网络连接,可以减少网络延迟,提高查询速度。
  • 集群规模:根据你的数据量和并发量,合理地配置集群规模。增加节点的数量可以提高集群的吞吐量和可用性。

6. 总结

通过禁用 _source 字段并结合使用 stored_fields,可以显著提高 Elasticsearch 的查询性能。这是一种在性能和功能之间取得平衡的策略。在实际应用中,你需要仔细考虑你的数据模型,进行充分的性能测试,并结合其他索引优化技巧,才能找到最佳的性能优化方案。

希望这篇文章对你有所帮助。记住,性能优化是一个持续的过程。不断地学习、实践和总结,你才能成为一个真正的 Elasticsearch 专家!

如果你在实践过程中遇到任何问题,欢迎随时和我交流。让我们一起探索 Elasticsearch 的世界!

7. 进阶技巧:使用 includesexcludes 精细控制 _source (可选)

虽然我们主要讨论了禁用 _source,但有时候,你可能只想排除 _source 中的某些字段,而不是完全禁用它。Elasticsearch 提供了 includesexcludes 参数,允许你精细地控制 _source 的内容。

7.1 includes 参数

includes 参数允许你指定 _source 中需要包含的字段。只有在 includes 中指定的字段才会被存储在 _source 中。例如,你只想在 _source 中存储 titleauthor 字段,可以使用以下配置:

PUT /my_index
{
"settings": {
"index": {
"_source": {
"includes": ["title", "author"]
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"author": {
"type": "keyword"
},
"timestamp": {
"type": "date"
}
}
}
}

在这个例子中,只有 titleauthor 字段会被存储在 _source 中。其他字段(例如 contenttimestamp)将不会被存储。

7.2 excludes 参数

excludes 参数允许你指定 _source 中需要排除的字段。在除了 excludes 中指定的字段之外,其他所有字段都会被存储在 _source 中。例如,你想在 _source 中排除 content 字段,可以使用以下配置:

PUT /my_index
{
"settings": {
"index": {
"_source": {
"excludes": ["content"]
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"author": {
"type": "keyword"
},
"timestamp": {
"type": "date"
}
}
}
}

在这个例子中,content 字段将不会被存储在 _source 中。其他字段(例如 titleauthortimestamp)将会被存储。

7.3 总结

includesexcludes 参数提供了一种更灵活的方式来控制 _source 的内容。你可以根据你的实际需求,选择合适的参数来优化你的数据存储。请注意,includesexcludes 只能在索引创建时设置,无法在索引创建后进行修改。如果你需要修改 _source 的配置,你需要重建索引。

8. 案例分析:日志分析场景

让我们来看一个实际的案例:日志分析。假设你正在使用 Elasticsearch 来存储和分析应用程序的日志。你的日志数据通常包含以下字段:

  • timestamp:日志时间戳
  • level:日志级别(例如,INFO、WARN、ERROR)
  • message:日志消息
  • service:服务名称
  • ip:客户端 IP 地址

在这个场景中,你可能经常需要根据 timestamplevelservicemessage 字段进行查询。而对于 message 字段,你可能只需要进行全文搜索,不需要存储完整的 _source

在这种情况下,你可以考虑以下配置:

  1. 禁用 _source 并使用 stored_fields

    • 禁用 _source:因为你不需要存储完整的日志数据。只保留关键字段用于快速查询和分析。
    • timestamplevelservice 字段存储在 stored_fields 中。这些字段通常用于过滤和聚合,需要快速访问。
    • message 字段不需要存储在 stored_fields 中。对于 message 字段,你只需要进行全文搜索,不需要获取其原始值。
  2. 使用 includesexcludes (可选)

    • 如果你想保留 _source 中的部分字段,例如 timestamp,你可以使用 includes 参数。例如,_source: { includes: ["timestamp"] }
    • 如果你想排除 _source 中的 message 字段,可以使用 excludes 参数。例如,_source: { excludes: ["message"] }

通过这种配置,你可以显著提高日志分析的性能。查询速度更快,存储空间更节省。

9. 总结与建议

在 Elasticsearch 中优化性能是一个持续的过程,需要根据实际场景进行调整。以下是一些建议:

  • 了解你的数据:深入了解你的数据模型,确定哪些字段是需要查询和展示的,哪些字段是不需要的。
  • 进行性能测试:在实际应用中进行充分的性能测试,以评估不同的配置带来的性能提升。
  • 选择合适的策略:根据你的需求,选择合适的策略。禁用 _source 和使用 stored_fields 是一种常用的方法,但并非适用于所有场景。includesexcludes 参数提供了更灵活的控制方式。
  • 持续优化:性能优化是一个持续的过程。随着数据量和查询复杂度的增加,你需要不断地调整你的配置,以保持最佳的性能。

记住,没有“银弹”解决方案。最适合你的解决方案取决于你的具体需求和环境。希望这篇文章能为你提供一些有用的参考。祝你在 Elasticsearch 的世界里玩得开心!

老码农 Elasticsearch性能优化_sourcestored_fields

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/8239