ElasticSearch复习指南:从零搭建一个商品搜索案例

发布于:2025-09-10 ⋅ 阅读:(19) ⋅ 点赞:(0)

前言:为什么我们需要ElasticSearch?

想象一下,你正在开发一个电商网站,商品数量达到百万级别。用户需要能够快速搜索商品,并且希望支持模糊匹配、按价格筛选、按分类过滤等功能。

如果使用传统的数据库(如MySQL)来实现,你可能会写出这样的查询:

SELECT * FROM products 
WHERE name LIKE '%手机%' 
AND price BETWEEN 1000 AND 5000 
AND category_id = 1
ORDER BY create_time DESC
LIMIT 20 OFFSET 0;

随着数据量增加,这种查询会变得越来越慢,而且无法很好地支持模糊搜索和相关度排序。这就是ElasticSearch的用武之地。

一、ElasticSearch核心概念快速回顾

1.1 倒排索引:为什么ES这么快?

传统数据库使用"正排索引":文档→关键词

文档1:我爱学习ElasticSearch
文档2:ElasticSearch真强大

而ElasticSearch使用"倒排索引":关键词→文档

我: [文档1]
爱: [文档1]
学习: [文档1]
ElasticSearch: [文档1, 文档2]
真: [文档2]
强大: [文档2]

这样搜索"ElasticSearch"时,直接就能找到文档1和文档2,无需扫描所有文档。

1.2 基本概念对照表

ElasticSearch术语 传统数据库类比 说明
Index(索引) Database(数据库) 数据容器
Type(类型) Table(表) ES7+已废弃
Document(文档) Row(行) 基本数据单位
Field(字段) Column(列) 数据字段
Mapping(映射) Schema(模式) 数据结构定义

二、实战案例:搭建商品搜索系统

让我们通过一个真实案例来复习ElasticSearch的使用。

2.1 环境准备

首先使用Docker启动ElasticSearch和Kibana:

# 启动ElasticSearch
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.17.0

# 启动Kibana(Web管理界面)
docker run -d --name kibana --link elasticsearch:elasticsearch -p 5601:5601 kibana:7.17.0

2.2 创建商品索引

假设我们的商品有以下字段:

  • id:商品ID
  • name:商品名称
  • price:价格
  • category:分类
  • description:描述
  • tags:标签数组
PUT /products
{
  "mappings": {
    "properties": {
      "id": { "type": "integer" },
      "name": { 
        "type": "text",
        "analyzer": "ik_max_word",  // 使用中文分词器
        "search_analyzer": "ik_smart"
      },
      "price": { "type": "float" },
      "category": { "type": "keyword" },
      "description": { 
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "tags": { "type": "keyword" },
      "created_at": { "type": "date" }
    }
  }
}

2.3 插入测试数据

POST /products/_bulk
{"index":{"_id":1}}
{"id":1,"name":"Apple iPhone 13","price":5999,"category":"手机","description":"新一代iPhone,A15芯片,超视网膜XDR显示屏","tags":["苹果","智能手机","5G"],"created_at":"2022-01-01"}
{"index":{"_id":2}}
{"id":2,"name":"华为Mate 50","price":5499,"category":"手机","description":"华为旗舰手机,鸿蒙系统,超强拍照","tags":["华为","智能手机","5G"],"created_at":"2022-02-01"}
{"index":{"_id":3}}
{"id":3,"name":"小米手环7","price":249,"category":"智能穿戴","description":"1.62英寸AMOLED屏,120种运动模式","tags":["小米","智能手环","健康监测"],"created_at":"2022-03-01"}

2.4 实现复杂搜索功能

​案例1:基本搜索​​ - 查找商品名称或描述中包含"手机"的商品

GET /products/_search
{
  "query": {
    "multi_match": {
      "query": "手机",
      "fields": ["name", "description"]
    }
  }
}

​案例2:多条件过滤​​ - 搜索手机分类中,价格在5000-6000之间的商品

GET /products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "category": "手机" } }
      ],
      "filter": [
        { "range": { "price": { "gte": 5000, "lte": 6000 } } }
      ]
    }
  }
}

​案例3:模糊搜索与评分​​ - 搜索"华为"(用户输入错误)

GET /products/_search
{
  "query": {
    "fuzzy": {
      "name": {
        "value": "华为",
        "fuzziness": "AUTO"
      }
    }
  }
}

​案例4:聚合分析​​ - 统计每个分类的商品数量和平均价格

GET /products/_search
{
  "size": 0,
  "aggs": {
    "categories": {
      "terms": {
        "field": "category"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

三、性能优化技巧

3.1 索引设计优化

  1. ​合理使用字段类型​​:

    • 精确匹配用keyword
    • 文本搜索用text+适当的分词器
  2. ​索引分片策略​​:

    PUT /products
    {
      "settings": {
        "number_of_shards": 3,    // 主分片数,一旦设置不能修改
        "number_of_replicas": 1   // 副本分片数,可随时调整
      }
    }

3.2 查询优化

  1. ​避免深度分页​​:使用search_after代替from/size
  2. ​使用过滤器上下文​​:filter不计算评分,性能更好
  3. ​合理使用索引别名​​:实现零停机重建索引
// 1. 创建新索引
PUT /products_new

// 2. 将数据从旧索引迁移到新索引
POST _reindex
{
  "source": { "index": "products" },
  "dest": { "index": "products_new" }
}

// 3. 原子操作切换别名
POST _aliases
{
  "actions": [
    { "remove": { "index": "products", "alias": "products_alias" } },
    { "add": { "index": "products_new", "alias": "products_alias" } }
  ]
}

四、常见问题与解决方案

4.1 数据同步问题:如何保证数据库与ES数据一致?

推荐使用两种方案:

  1. ​双写模式​​:应用代码中同时写入数据库和ES

    • 优点:简单直接
    • 缺点:可能存在数据不一致
  2. ​日志订阅模式​​:通过CDC工具(如Canal、Debezium)捕获数据库变更

    • 优点:解耦,保证最终一致性
    • 缺点:架构复杂

4.2 中文分词问题

ES默认的中文分词效果不好,推荐使用IK分词器:

PUT /products
{
  "settings": {
    "analysis": {
      "analyzer": {
        "ik_smart_custom": {
          "type": "custom",
          "tokenizer": "ik_smart"
        }
      }
    }
  }
}

五、总结

通过这个商品搜索案例,我们复习了ElasticSearch的核心概念和实际应用:

  1. ​倒排索引​​是ES高性能搜索的基石
  2. ​合理的Mapping设计​​对搜索性能和准确性至关重要
  3. ​复合查询​​可以满足复杂的业务需求
  4. ​聚合分析​​提供了强大的数据分析能力
  5. ​性能优化​​需要从索引设计和查询两方面入手