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

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

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

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

sql 复制代码
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. ​性能优化​需要从索引设计和查询两方面入手
相关推荐
用户03284722207018 小时前
如何搭建本地yum源(上)
运维
大树884 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠4 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质4 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工4 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智4 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_4 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
施努卡机器视觉4 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造
AC赳赳老秦4 天前
用 OpenClaw 搭建服务器故障应急响应系统,自动处理 80% 常见运维故障
android·运维·服务器·python·rxjava·deepseek·openclaw
java_cj4 天前
深入kube-apiserver认证机制:从Bearer Token到mTLS的完整认证链解析
linux·运维·服务器·云原生·容器·kubernetes