索引结构设计
- 字段类型选择
- 对于商品名称字段,使用
text
类型,以支持全文搜索和模糊匹配。同时,可以为该字段增加keyword
子字段,用于精确匹配,例如在根据商品ID查询时使用。
- 品牌字段使用
keyword
类型,因为品牌通常是固定的有限集合,适合精确过滤。
- 价格字段使用
float
或integer
类型(根据实际价格精度需求),以便进行范围查询。
- 多字段和分词器
- 商品名称使用合适的分词器,如
ik_max_word
(针对中文),将文本拆分成尽可能多的词语,提高模糊匹配的召回率。
- 对于一些需要特殊处理的字段,可设置多字段。比如商品描述,除了使用默认分词器进行全文搜索的
text
类型字段外,还可创建一个使用keyword
类型的字段,用于存储原始描述内容,方便在特定场景下的精确检索。
- 索引分片与副本
- 根据集群规模和数据量合理设置分片数量,以确保数据均匀分布,提高查询并行度。一般原则是每个分片大小不超过50GB - 100GB。例如,对于1TB的数据,可以设置10 - 20个分片。
- 配置适当的副本数量,以提高系统的可用性和读性能。但副本过多会占用额外的磁盘空间和网络资源,一般设置1 - 2个副本。
查询语句优化
- 布尔查询组合
- 使用
bool
查询来组合各种查询条件。例如,对于“商品名称模糊匹配且按品牌过滤且按价格区间筛选”的需求,可将商品名称的模糊查询放在should
子句(以提高召回率),品牌过滤和价格区间筛选放在filter
子句(提高查询效率,因为filter
子句不会计算相关性分数且可利用缓存)。
- 示例查询:
{
"query": {
"bool": {
"should": [
{
"match": {
"product_name": {
"query": "手机",
"fuzziness": "AUTO"
}
}
}
],
"filter": [
{
"term": {
"brand": "华为"
}
},
{
"range": {
"price": {
"gte": 1000,
"lte": 5000
}
}
}
]
}
}
}
- 排序优化
- 对于热门商品优先展示,可通过在文档中增加一个表示热度的字段(如
popularity
),并在查询时按该字段降序排序。如果热度计算涉及复杂逻辑,可定期更新热度字段的值。
- 示例排序:
{
"query": {
"match_all": {}
},
"sort": [
{
"popularity": {
"order": "desc"
}
}
]
}
- 使用
profile
API
- 在开发和调优阶段,利用
profile
API来分析查询性能,找出查询执行过程中的瓶颈,例如哪个分片、哪个阶段耗时较长,进而针对性地优化查询语句。
缓存策略
- 请求缓存
- 启用Elasticsearch的请求缓存,对于相同的查询请求,直接从缓存中返回结果,减少重复查询压力。请求缓存适用于那些不经常变化的数据查询,如商品基本信息查询。可以通过设置
request_cache.enable
为true
来启用,并根据业务场景调整缓存的过期时间。
- 分片缓存
- 分片缓存主要用于
filter
子句中的查询,Elasticsearch会自动缓存filter
子句的结果。合理利用这一点,将一些不经常变化且可复用的过滤条件放在filter
子句中,如品牌过滤、固定价格区间过滤等,以提高查询效率。
- 应用层缓存
- 在应用层(如Web服务器)实现缓存机制,例如使用Redis。对于一些热门搜索关键词的结果进行缓存,当用户再次发起相同搜索请求时,直接从应用层缓存中返回结果,减少对Elasticsearch集群的请求压力。可以设置合适的缓存过期时间,以保证数据的实时性。同时,采用缓存更新策略,当商品数据发生变化时,及时更新应用层缓存。