前言
大家好,我是路由器没有路。
今天跟大家聊下自己在工作中总结的关于使用 ElasticSearch 的一些规范,如有不当的地方,欢迎指正。
Elasticsearch
是一个基于 Lucene
的搜索引擎,支持分布式搜索、多租户、实时搜索和分析等能力,具有高效、稳定、可扩展的优势,被广泛应用于企业级搜索和数据分析场景。
关于 ElasticSearch
的介绍,这里不再赘述了,有兴趣的同学可以看下我前面写的一篇文章:Elasticsearch 的简单介绍和如何使用。
基础配置规划
分片(shard)容量
- 非日志型(搜索型、线上业务型)的 shard 容量在 20~40GB(建议在 20G)
- 日志型的
shard
容量在 35~100GB(建议 35G) - 单个
shard
的文档个数不能超过 20 亿左右(Integer.MAX_VALUE - 128)
注:一个
shard
就是一个lucene
分片,ES 底层基于lucene
实现。
索引(index)数量
大索引需要拆分:提高性能,降低风险,将风险分散化。
反例:例如一个 10 多 T 的索引,按 date 查询、name 查询
正例:indexname 拆成多个 index_name\${date}
正例:indexname 按 hash 拆分 index_name{1,2,3,...100..}
节点、分片、索引
一个节点管理的 shard
数不要超过 200 个
索引 mapping 的设计原则
大原则:不使用默认配置和动态 mapping
、数据用途(类型、分词、存储、排序)弄清,下面是一个标准 mapping
:
{
"aliases": {
"my_index_name": {
}
},
"settings": {
"index": {
"refresh_interval": "1s",
"number_of_shards": "12",
"number_of_replicas": "1",
"search.slowlog.threshold.query.warn": "5s",
"search.slowlog.threshold.query.info": "1s",
"search.slowlog.threshold.fetch.warn": "1s",
"search.slowlog.threshold.fetch.info": "800ms",
"indexing.slowlog.threshold.index.warn": "12s",
"indexing.slowlog.threshold.index.info": "2s"
}
},
"mappings": {
"_default_": {
"_all": {
"enables": false
}
},
"my_type_name": {
"properties": {
"xxx_id": {
"type": "keyword"
},
"timestamp": {
"type": "long"
},
"xxx_status": {
"type": "integer"
},
"xxx_content": {
"type": "ztext"
}
}
}
}
}
refresh 频率(refresh_interval)
ES 的定位是准实时搜索引擎,该值默认是 1s,表示写入后 1 秒后可被搜索到,所以这里的值取决于业务对实时性的要求。
注意这里并不是越小越好,刷新频率高也意味着对 ES 的开销也大,通常业务类型在 1-5s,日志型在 30s-120s,如果集中导入数据可将其设置为-1,ES 会自动完成数据刷新(注意完成后更改回来,否则后续会出现搜索不到数据)。
别名(aliases)
记住:在某些场景下可使用别名,但不要过度依赖别名功能。
正例:
索引名:index_name_v1
别名:index_name
未来重建 index_name_v2 索引,对于业务来说只需要换别名。
type 个数
1 个就够了,从 ES6
开始只支持一个 type
,这个 type
比较鸡肋,后面的版本可能会去掉。
如果一定用:针对已经使用多个 type
的场景,一定要保证不同 type
下字段尽量保持一致,否则会加大数据稀疏性,存储与查询性能受影响
慢日志(slowlog)
一定要配置,默认不记录慢查询,kcc 提供了 grafana、kibana 查询功能。
副本(number_of_replicas)
1 个就够用,副本多写入压力不可忽视。极端情况下:譬如批量导入数据,可以将其调整为 0。
字段设计
text
和keyword
的用途必须分清:分词和关键词(确定字段是否需要分词)确定字段是否需要独立存储
字段类型不支持修改,必须谨慎
对不需要进行聚合/排序的字段禁用
doc_values
不要在
text
做模糊搜索
text
类型:适用于分词用于搜索,适用于 email 、内容、描述等需要分词的全文检索,不适用聚合。
keyword
类型:无需分词,整段完整精确匹配,适用于:email 、地址、状态码、分类 tags。
设置合理的 routing key(默认是 id)
id 不均衡:集群容量和访问不均衡,对于分布式存储是致命的。
关闭 _all
ES6.0
已经去掉,对容量(索引过大)和性能(性能下降)都有影响。
避免大宽表:
ES 默认最大 1000,但建议不要超过 100
text 类型的字段不要使用聚合查询
text
类型 fileddata
会加大对内存的占用,如果有需求使用,建议使用 keyword
聚合查询避免使用过多嵌套,
聚合查询的中间结果和最终结果都会在内存中进行,嵌套过多,会导致内存耗尽。
以下是聚合就嵌套了 3 层,结果都会保存在内存中,
如果唯一值较多,就会导致内存耗尽:
{
"aggs": {
"country": {
"terms": {
"filed": "country",
"size": 10
},
"aggs": {
"city": {
"terms": {
"filed": "city",
"size": 20
},
"aggs": {
"salary": {
"terms": {
"filed": "salary",
"size": 20
}
}
}
}
}
}
}
}
谨慎操作
- 原则:不要忽略设计,快就是慢,坏的索引设计后患无穷.
- 拒绝大聚合 :ES 计算都在 JVM 内存中完成。
拒绝模糊查询:es 一大杀手
即
wildcard
搜索{ "query": { "wildcard": { "title.keyword": "*张三*" } } }
拒绝深度分页
ES 获取数据时,每次默认最多获取 10000 条,获取更多需要分页,但存在深度分页问题,一定不要使用 from/Size 方式,建议使用 scroll 或者 searchAfter 方式。scroll 会把上一次查询结果缓存一定时间(通过配置 scroll=1m 实现),所以在使用 scroll 时一定要保证 search 结果集不要太大。
基数查询
尽量不要用基数查询去查询去重后的数据量大小(kibana
中界面上显示是 Unique Count,Distinct Count 等)即少用以下查询:
"aggregations": { "cardinality": { "field": "userId" } }
禁止查询 indexName-*
- 避免使用
script
、update_by_query
、delete_by_query
,对线上性能影响较大。
需注意的问题
- 一个索引的
shard
数一旦确定不能改变 - ES 不支持事务
ACID
特性。 - reindex:
reindex 可以实现索引的 shard 变更,但代价非常大:速度慢、对性能有影响,所以好的设计和规划更重要
总结
以上是自己在工作中总结的关于 ElasticSearch
的使用规范,如对你有帮助,可以给个赞。
另外,Elasticsearch
的使用需要结合实际业务场景,通过优化和管理来提高其性能和稳定性,我们需要根据特定的业务场景和使用需求来选择合适的方案。